From 51a5ca76ae589dfcf78899663e2e79f2a4353607 Mon Sep 17 00:00:00 2001 From: one Date: Sat, 16 Aug 2025 18:54:16 +0800 Subject: [PATCH] feat: sanitize svg before rendering --- package.json | 1 + src/renderer/src/components/Preview/utils.ts | 14 +++++++++++--- yarn.lock | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a824984845..70ed89485c 100644 --- a/package.json +++ b/package.json @@ -179,6 +179,7 @@ "dexie-react-hooks": "^1.1.7", "diff": "^7.0.0", "docx": "^9.0.2", + "dompurify": "^3.2.6", "dotenv-cli": "^7.4.2", "electron": "37.2.3", "electron-builder": "26.0.15", diff --git a/src/renderer/src/components/Preview/utils.ts b/src/renderer/src/components/Preview/utils.ts index 423f57d41f..e400062911 100644 --- a/src/renderer/src/components/Preview/utils.ts +++ b/src/renderer/src/components/Preview/utils.ts @@ -1,4 +1,5 @@ import { makeSvgSizeAdaptive } from '@renderer/utils' +import DOMPurify from 'dompurify' /** * Renders an SVG string inside a host element's Shadow DOM to ensure style encapsulation. @@ -14,6 +15,13 @@ export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLEleme throw new Error('Host element for SVG rendering is not available.') } + // Sanitize the SVG content + const sanitizedContent = DOMPurify.sanitize(svgContent, { + USE_PROFILES: { svg: true, svgFilters: true }, + RETURN_DOM_FRAGMENT: false, + RETURN_DOM: false + }) + const shadowRoot = hostElement.shadowRoot || hostElement.attachShadow({ mode: 'open' }) // Base styles for the host element and the inner SVG @@ -36,19 +44,19 @@ export function renderSvgInShadowHost(svgContent: string, hostElement: HTMLEleme shadowRoot.innerHTML = '' shadowRoot.appendChild(style) - if (svgContent.trim() === '') { + if (sanitizedContent.trim() === '') { return } const parser = new DOMParser() - const doc = parser.parseFromString(svgContent, 'image/svg+xml') + const doc = parser.parseFromString(sanitizedContent, 'image/svg+xml') const parserError = doc.querySelector('parsererror') let svgElement: Element = doc.documentElement // If parsing fails or the namespace is incorrect, fall back to the more lenient HTML parser. if (parserError || svgElement.namespaceURI !== 'http://www.w3.org/2000/svg') { const tempDiv = document.createElement('div') - tempDiv.innerHTML = svgContent + tempDiv.innerHTML = sanitizedContent const svgFromHtml = tempDiv.querySelector('svg') if (svgFromHtml) { diff --git a/yarn.lock b/yarn.lock index 276a523034..3a27d98704 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8552,6 +8552,7 @@ __metadata: dexie-react-hooks: "npm:^1.1.7" diff: "npm:^7.0.0" docx: "npm:^9.0.2" + dompurify: "npm:^3.2.6" dotenv-cli: "npm:^7.4.2" electron: "npm:37.2.3" electron-builder: "npm:26.0.15" @@ -11445,7 +11446,7 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:^3.2.5": +"dompurify@npm:^3.2.5, dompurify@npm:^3.2.6": version: 3.2.6 resolution: "dompurify@npm:3.2.6" dependencies: