From 55712743635cc8727249f6a47ee8776323aa5d44 Mon Sep 17 00:00:00 2001 From: 16th-admin Date: Tue, 28 Jul 2026 11:49:20 +0800 Subject: [PATCH 1/3] fix(security): insert Mermaid SVG as sanitized DOM --- src/services/pltxt2htm/advancedParser.ts | 31 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/services/pltxt2htm/advancedParser.ts b/src/services/pltxt2htm/advancedParser.ts index 06f1ad61..291d3d5c 100644 --- a/src/services/pltxt2htm/advancedParser.ts +++ b/src/services/pltxt2htm/advancedParser.ts @@ -27,6 +27,24 @@ function ensureMermaidInitialized() { mermaidInitialized = true } +function createMermaidDiagram(svg: string): HTMLElement | null { + const svgDocument = new DOMParser().parseFromString(svg, 'image/svg+xml') + const svgRoot = svgDocument.documentElement + if (svgRoot.localName !== 'svg' || svgDocument.querySelector('parsererror')) return null + + svgRoot.querySelectorAll('script, foreignObject').forEach((element) => element.remove()) + svgRoot.querySelectorAll('*').forEach((element) => { + for (const attribute of [...element.attributes]) { + const name = attribute.name.toLowerCase() + const value = attribute.value.trim().replace(/\s/g, '') + if (name.startsWith('on') || (name.endsWith('href') && /^(?:javascript|data):/i.test(value))) { + element.removeAttribute(attribute.name) + } + } + }) + + return svgDocument.importNode(svgRoot, true) +} async function renderMermaidDiagrams(container: HTMLElement) { ensureMermaidInitialized() @@ -42,11 +60,14 @@ async function renderMermaidDiagrams(container: HTMLElement) { try { const renderId = `mermaid-${Date.now()}-${index}`; - const { svg } = await mermaid.render(renderId, source); - const wrapper = document.createElement("div"); - wrapper.className = "mermaid-diagram"; - wrapper.innerHTML = svg; - pre.replaceWith(wrapper); + const { svg } = await mermaid.render(renderId, source) + const diagram = createMermaidDiagram(svg) + if (!diagram) return + + const wrapper = document.createElement('div') + wrapper.className = 'mermaid-diagram' + wrapper.replaceChildren(diagram) + pre.replaceWith(wrapper) } catch (e) { console.warn("mermaid render failed:", e); } From 5a056dde9ce8bc05036365659a7d8cd7bdda9031 Mon Sep 17 00:00:00 2001 From: 16th-admin Date: Tue, 28 Jul 2026 11:59:33 +0800 Subject: [PATCH 2/3] fix(security): sanitize Mermaid SVG with DOMPurify --- package-lock.json | 1 + package.json | 1 + src/services/pltxt2htm/advancedParser.ts | 22 ++++++---------------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 47d4ca25..a4a6570a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@fingerprintjs/fingerprintjs": "^5.0.1", "date-fns": "^4.1.0", + "dompurify": "^3.4.11", "highlight.js": "^11.11.1", "katex": "^0.16.45", "md-editor-v3": "^6.5.0", diff --git a/package.json b/package.json index afa16128..2de6fa5f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@fingerprintjs/fingerprintjs": "^5.0.1", "date-fns": "^4.1.0", + "dompurify": "^3.4.11", "highlight.js": "^11.11.1", "katex": "^0.16.45", "md-editor-v3": "^6.5.0", diff --git a/src/services/pltxt2htm/advancedParser.ts b/src/services/pltxt2htm/advancedParser.ts index 291d3d5c..40d5815b 100644 --- a/src/services/pltxt2htm/advancedParser.ts +++ b/src/services/pltxt2htm/advancedParser.ts @@ -2,6 +2,7 @@ import { getWasmInstance } from './wasmLoader' import { getDeallocator } from './deallocator' import hljs from 'highlight.js' import mermaid from 'mermaid' +import DOMPurify from 'dompurify' import renderMathInElement from 'katex/contrib/auto-render/auto-render.js' import 'katex/dist/katex.min.css' import storageManager from '@storage/index' @@ -27,23 +28,12 @@ function ensureMermaidInitialized() { mermaidInitialized = true } -function createMermaidDiagram(svg: string): HTMLElement | null { - const svgDocument = new DOMParser().parseFromString(svg, 'image/svg+xml') - const svgRoot = svgDocument.documentElement - if (svgRoot.localName !== 'svg' || svgDocument.querySelector('parsererror')) return null - - svgRoot.querySelectorAll('script, foreignObject').forEach((element) => element.remove()) - svgRoot.querySelectorAll('*').forEach((element) => { - for (const attribute of [...element.attributes]) { - const name = attribute.name.toLowerCase() - const value = attribute.value.trim().replace(/\s/g, '') - if (name.startsWith('on') || (name.endsWith('href') && /^(?:javascript|data):/i.test(value))) { - element.removeAttribute(attribute.name) - } - } +function createMermaidDiagram(svg: string): Node | null { + const diagram = DOMPurify.sanitize(svg, { + USE_PROFILES: { svg: true, svgFilters: true }, + RETURN_DOM: true, }) - - return svgDocument.importNode(svgRoot, true) + return diagram instanceof SVGSVGElement ? diagram : null } async function renderMermaidDiagrams(container: HTMLElement) { From fca6bedec9b4ce681a2cf32bb98581cd81cdf35f Mon Sep 17 00:00:00 2001 From: 16th-admin Date: Tue, 28 Jul 2026 12:01:47 +0800 Subject: [PATCH 3/3] fix(security): sanitize parser HTML output --- src/services/pltxt2htm/advancedParser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/pltxt2htm/advancedParser.ts b/src/services/pltxt2htm/advancedParser.ts index 40d5815b..e18ecf8b 100644 --- a/src/services/pltxt2htm/advancedParser.ts +++ b/src/services/pltxt2htm/advancedParser.ts @@ -112,7 +112,9 @@ async function parse(source: string, context: ParseContext = {}) { if (!rawHtml) return '' const tempDiv = document.createElement('div') - tempDiv.innerHTML = rawHtml + tempDiv.innerHTML = DOMPurify.sanitize(rawHtml, { + USE_PROFILES: { html: true, svg: true, svgFilters: true, mathMl: true }, + }) if (typeof renderMathInElement === 'function') { renderMathInElement(tempDiv, {