fix: block unsafe editor links
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, type RefObject } from 'react'
|
||||
import { isUrlValue, normalizeUrl, openExternalUrl } from '../utils/url'
|
||||
import { normalizeExternalUrl, openExternalUrl } from '../utils/url'
|
||||
|
||||
const CODE_CONTEXT_SELECTOR = '[data-content-type="codeBlock"], pre, code'
|
||||
|
||||
@@ -15,10 +15,8 @@ function resolveWikilinkTarget(target: HTMLElement) {
|
||||
return target.closest<HTMLElement>('.wikilink[data-target]')?.dataset.target ?? null
|
||||
}
|
||||
|
||||
function resolveUrlTarget(target: HTMLElement) {
|
||||
const href = target.closest<HTMLAnchorElement>('a[href]')?.getAttribute('href')?.trim()
|
||||
if (!href || !isUrlValue(href)) return null
|
||||
return normalizeUrl(href)
|
||||
function resolveAnchorHref(target: HTMLElement) {
|
||||
return target.closest<HTMLAnchorElement>('a[href]')?.getAttribute('href')?.trim() ?? null
|
||||
}
|
||||
|
||||
function blurActiveEditable(container: HTMLElement) {
|
||||
@@ -33,6 +31,52 @@ function setFollowLinksActive(container: HTMLElement, active: boolean) {
|
||||
else container.removeAttribute('data-follow-links')
|
||||
}
|
||||
|
||||
function consumeEditorLinkClick(event: MouseEvent) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
function activateWikilink(
|
||||
event: MouseEvent,
|
||||
container: HTMLElement,
|
||||
target: string,
|
||||
onNavigateWikilink: (target: string) => void,
|
||||
) {
|
||||
if (!hasFollowModifier(event)) return
|
||||
|
||||
consumeEditorLinkClick(event)
|
||||
blurActiveEditable(container)
|
||||
onNavigateWikilink(target)
|
||||
}
|
||||
|
||||
function activateExternalUrl(event: MouseEvent, href: string) {
|
||||
consumeEditorLinkClick(event)
|
||||
|
||||
if (!hasFollowModifier(event)) return
|
||||
|
||||
const urlTarget = normalizeExternalUrl(href)
|
||||
if (!urlTarget) return
|
||||
|
||||
openExternalUrl(urlTarget).catch((err) => console.warn('[link] Failed to open URL:', err))
|
||||
}
|
||||
|
||||
function handleEditorLinkClick(
|
||||
event: MouseEvent,
|
||||
container: HTMLElement,
|
||||
onNavigateWikilink: (target: string) => void,
|
||||
) {
|
||||
if (!(event.target instanceof HTMLElement) || isInsideCodeContext(event.target)) return
|
||||
|
||||
const wikilinkTarget = resolveWikilinkTarget(event.target)
|
||||
if (wikilinkTarget) {
|
||||
activateWikilink(event, container, wikilinkTarget, onNavigateWikilink)
|
||||
return
|
||||
}
|
||||
|
||||
const href = resolveAnchorHref(event.target)
|
||||
if (href) activateExternalUrl(event, href)
|
||||
}
|
||||
|
||||
export function useEditorLinkActivation(
|
||||
containerRef: RefObject<HTMLDivElement | null>,
|
||||
onNavigateWikilink: (target: string) => void,
|
||||
@@ -49,24 +93,7 @@ export function useEditorLinkActivation(
|
||||
if (document.visibilityState !== 'visible') resetModifierState()
|
||||
}
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (!hasFollowModifier(event)) return
|
||||
if (!(event.target instanceof HTMLElement) || isInsideCodeContext(event.target)) return
|
||||
|
||||
const wikilinkTarget = resolveWikilinkTarget(event.target)
|
||||
if (wikilinkTarget) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
blurActiveEditable(container)
|
||||
onNavigateWikilink(wikilinkTarget)
|
||||
return
|
||||
}
|
||||
|
||||
const urlTarget = resolveUrlTarget(event.target)
|
||||
if (!urlTarget) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
openExternalUrl(urlTarget).catch((err) => console.warn('[link] Failed to open URL:', err))
|
||||
handleEditorLinkClick(event, container, onNavigateWikilink)
|
||||
}
|
||||
|
||||
container.addEventListener('click', handleClick, true)
|
||||
|
||||
Reference in New Issue
Block a user