Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@ import { keepAlive } from '../../../__internal__/keep-alive'

keepAlive(h, Fragment)

const SVG_NAMESPACE = 'http://www.w3.org/2000/svg'

/**
* Creates a sanitizer that allows foreignObject only inside SVG context.
*
* Mermaid v11+ uses foreignObject for flowchart node labels, but foreignObject
* is a known mXSS vector (CVE-2020-26870) outside SVG context. The strategy:
* 1. ADD_TAGS allows foreignObject to survive DOMPurify's initial filtering
* 2. The uponSanitizeElement hook removes it if NOT inside an SVG element
* 3. HTML_INTEGRATION_POINTS lets its HTML children parse correctly
*/
function createSvgAwareSanitizer() {
const purify = DOMPurify()

const config = {
ADD_TAGS: ['foreignObject'],
ADD_ATTR: ['xmlns'],
HTML_INTEGRATION_POINTS: { foreignobject: true },
}

purify.addHook('uponSanitizeElement', (node, data) => {
if (data.tagName === 'foreignobject') {
const parent = node.parentElement
if (!parent || parent.namespaceURI !== SVG_NAMESPACE) {
node.parentNode?.removeChild(node)
}
}
})

return (dirty: string | Node) => purify.sanitize(dirty, config)
}

const sanitizeSvg = createSvgAwareSanitizer()

type PreviewPanelProps = Pick<
CodeBlockProps,
'text' | 'language' | 'config'
Expand Down Expand Up @@ -56,7 +90,7 @@ export const PreviewPanel = defineComponent<PreviewPanelProps>({
typeof previewContent === 'string' ||
previewContent instanceof Element
) {
previewContainer.innerHTML = DOMPurify.sanitize(previewContent)
previewContainer.innerHTML = sanitizeSvg(previewContent)
}
})

Expand Down
Loading