mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-03 11:05:35 +08:00
fix: sanitize plugin svg icons
This commit is contained in:
17
package-lock.json
generated
17
package-lock.json
generated
@@ -39,6 +39,7 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dompurify": "^3.4.7",
|
||||
"express": "^4.18.2",
|
||||
"fuse.js": "^7.0.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
@@ -4580,6 +4581,13 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
@@ -7485,6 +7493,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz",
|
||||
"integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-prop": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"cmdk": "^1.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dompurify": "^3.4.7",
|
||||
"express": "^4.18.2",
|
||||
"fuse.js": "^7.0.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
import { authenticatedFetch } from '../../../utils/api';
|
||||
|
||||
type Props = {
|
||||
@@ -10,38 +12,43 @@ type Props = {
|
||||
// Module-level cache so repeated renders don't re-fetch
|
||||
const svgCache = new Map<string, string>();
|
||||
|
||||
const FORBIDDEN_SVG_TAGS = [
|
||||
'script',
|
||||
'foreignObject',
|
||||
'iframe',
|
||||
'object',
|
||||
'embed',
|
||||
'link',
|
||||
'meta',
|
||||
'style',
|
||||
'animate',
|
||||
'set',
|
||||
'animateTransform',
|
||||
'animateMotion',
|
||||
];
|
||||
|
||||
const FORBIDDEN_SVG_ATTRS = [
|
||||
'href',
|
||||
'xlink:href',
|
||||
'src',
|
||||
'style',
|
||||
];
|
||||
|
||||
function sanitizeSvg(svgText: string): string | null {
|
||||
const sanitized = DOMPurify.sanitize(svgText, {
|
||||
USE_PROFILES: { svg: true, svgFilters: true },
|
||||
FORBID_TAGS: FORBIDDEN_SVG_TAGS,
|
||||
FORBID_ATTR: FORBIDDEN_SVG_ATTRS,
|
||||
});
|
||||
|
||||
if (!sanitized) return null;
|
||||
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(svgText, 'image/svg+xml');
|
||||
const doc = new DOMParser().parseFromString(sanitized, 'image/svg+xml');
|
||||
const root = doc.documentElement;
|
||||
if (!root || root.nodeName.toLowerCase() !== 'svg') return null;
|
||||
|
||||
doc
|
||||
.querySelectorAll('script,foreignObject,iframe,object,embed,link,meta,style')
|
||||
.forEach((el) => el.remove());
|
||||
|
||||
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
||||
const elements: Element[] = [root];
|
||||
while (walker.nextNode()) {
|
||||
elements.push(walker.currentNode as Element);
|
||||
}
|
||||
|
||||
elements.forEach((el) => {
|
||||
Array.from(el.attributes).forEach((attr) => {
|
||||
const name = attr.name.toLowerCase();
|
||||
const value = attr.value.trim().toLowerCase();
|
||||
if (
|
||||
name.startsWith('on') ||
|
||||
name === 'href' ||
|
||||
name === 'xlink:href' ||
|
||||
value.startsWith('javascript:')
|
||||
) {
|
||||
el.removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return new XMLSerializer().serializeToString(root);
|
||||
if (doc.querySelector('parsererror')) return null;
|
||||
return sanitized;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user