diff --git a/src/web-ui/src/component-library/components/Markdown/MermaidBlock.test.tsx b/src/web-ui/src/component-library/components/Markdown/MermaidBlock.test.tsx new file mode 100644 index 0000000000..b77bc0f383 --- /dev/null +++ b/src/web-ui/src/component-library/components/Markdown/MermaidBlock.test.tsx @@ -0,0 +1,95 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from 'vitest'; + +import { sanitizeMermaidSvg } from './sanitizeMermaidSvg'; + +describe('sanitizeMermaidSvg', () => { + describe('XSS vectors are removed', () => { + it('strips '; + const clean = sanitizeMermaidSvg(raw); + expect(clean).not.toContain(' { + const raw = ''; + const clean = sanitizeMermaidSvg(raw); + expect(clean).not.toContain('onload'); + expect(clean).not.toContain('onmouseover'); + expect(clean).not.toContain('alert(2)'); + expect(clean.toLowerCase()).toContain(' { + const raw = 'x'; + const clean = sanitizeMermaidSvg(raw); + expect(clean).not.toContain('javascript:'); + expect(clean.toLowerCase()).toContain(' { + const raw = + '' + + '
' + + '

label

'; + const clean = sanitizeMermaidSvg(raw); + expect(clean).not.toContain('onerror'); + expect(clean).not.toContain('onclick'); + expect(clean).not.toContain(' { + expect(sanitizeMermaidSvg('')).toBe(''); + expect(sanitizeMermaidSvg('
not an svg
')).toBe(''); + }); + }); + + describe('normal mermaid output is preserved', () => { + it('keeps structural svg elements and drops nothing that a rendered diagram needs', () => { + const raw = ` + + + + + + + + + + + + + Process + + + +

Hello

+
+
+
`; + const clean = sanitizeMermaidSvg(raw); + const lower = clean.toLowerCase(); + expect(lower).toContain(' = ({ className="mermaid-block__diagram" data-bf-component="mermaid-block" data-bf-part="diagram" - dangerouslySetInnerHTML={{ __html: svgContent }} + dangerouslySetInnerHTML={{ __html: sanitizeMermaidSvg(svgContent) }} />
diff --git a/src/web-ui/src/component-library/components/Markdown/sanitizeMermaidSvg.ts b/src/web-ui/src/component-library/components/Markdown/sanitizeMermaidSvg.ts new file mode 100644 index 0000000000..b6645570f5 --- /dev/null +++ b/src/web-ui/src/component-library/components/Markdown/sanitizeMermaidSvg.ts @@ -0,0 +1,134 @@ +/** + * Sanitizer for Mermaid-rendered SVG markup. + * + * Mermaid is initialized with `securityLevel: 'loose'`, which allows label + * markup (raw HTML in labels) to pass through rendering. A crafted diagram + * can therefore carry `