Archived
39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
import "server-only";
|
|
|
|
/**
|
|
* Lightweight defense-in-depth pass over uploaded SVG markup before it is
|
|
* stored. Strips executable content (scripts, event handlers, external
|
|
* references) so a malicious SVG can't run script if it's ever rendered
|
|
* inline (dangerouslySetInnerHTML) rather than via <img src>. Not a full
|
|
* parser — good enough for a self-uploaded logo, not a substitute for
|
|
* treating SVG as active content from an untrusted source.
|
|
*/
|
|
export function sanitizeSvg(input: string): string {
|
|
let svg = input;
|
|
|
|
// Strip <script>...</script> blocks and self-closing <script/> tags.
|
|
svg = svg.replace(/<script[\s\S]*?<\/script\s*>/gi, "");
|
|
svg = svg.replace(/<script\b[^>]*\/>/gi, "");
|
|
|
|
// Strip on* event handler attributes (onload, onclick, onerror, ...).
|
|
svg = svg.replace(/\son\w+\s*=\s*"[^"]*"/gi, "");
|
|
svg = svg.replace(/\son\w+\s*=\s*'[^']*'/gi, "");
|
|
svg = svg.replace(/\son\w+\s*=\s*[^\s>]+/gi, "");
|
|
|
|
// Strip javascript: URIs in href/xlink:href/src attributes.
|
|
svg = svg.replace(
|
|
/((?:xlink:href|href|src)\s*=\s*)"javascript:[^"]*"/gi,
|
|
'$1""',
|
|
);
|
|
svg = svg.replace(
|
|
/((?:xlink:href|href|src)\s*=\s*)'javascript:[^']*'/gi,
|
|
"$1''",
|
|
);
|
|
|
|
// Strip <foreignObject> (can embed arbitrary HTML) and <iframe>.
|
|
svg = svg.replace(/<foreignObject[\s\S]*?<\/foreignObject\s*>/gi, "");
|
|
svg = svg.replace(/<iframe[\s\S]*?<\/iframe\s*>/gi, "");
|
|
|
|
return svg;
|
|
}
|