svgo removeScripts Plugin XSS Bypass via Namespace Prefix and Case-Insensitive URI
SVGO's script-removal plugin could be bypassed by using a namespace-prefixed script tag like <svg:script> or a mixed-case JavaScript URI like JavaScript:, leaving executable code in the optimized SVG.
The problem
The removeScripts plugin (called removeScriptElement in v1-v3) is disabled by default but exists for users who rely on SVGO to sanitize SVG before serving it.
Two gaps let scripts survive sanitization: the plugin matched only bare script element names, so <svg:script> was invisible to it. Separately, JavaScript URI matching was case-sensitive, so JavaScript:alert(1) in an href attribute was not stripped.
Proof of concept
A working proof-of-concept for this issue in svgo, with the exact payload below.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:uwu="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100" version="1.1">
<!-- Bypass 1: namespaced script tag, not matched by bare-name check -->
<svg:script>
alert(document.cookie);
</svg:script>
<!-- Bypass 2: mixed-case URI scheme, not matched by case-sensitive check -->
<a uwu:href="JavaScript:(() => { alert(document.cookie) })();">
<text y="30">click me</text>
</a>
</svg>The root cause is an incomplete blocklist (CWE-184) combined with improper input neutralization (CWE-79). The old plugin compared element names as plain strings, so svg:script never equaled script. The URI check used a case-sensitive regex or string match, so JavaScript: passed through untouched.
The patch rewired element matching to be namespace-aware, explicitly accepting only elements in the SVG or XHTML namespace, and switched the JavaScript URI check to a case-insensitive comparison. This means a custom <foo:script> in an unrelated namespace is now left alone correctly, while all browser-executable variants are caught.
The fix
Upgrade to svgo 2.8.3 (v2), 3.3.4 (v3), or 4.0.2 (v4). If you cannot upgrade, replace the removeScripts/removeScriptElement plugin with a dedicated SVG sanitizer such as DOMPurify before passing the SVG to SVGO.
Reported by SethFalco.
Related research
- high · 7.5CVE-2026-53950CVE-2026-53950: @tryghost/activitypub Stored XSS via Federated Post Content
- criticalCVE-2026-70470CVE-2026-70470: Flowise Pyodide Validator Unicode Homoglyph Bypass RCE
- highCVE-2026-69263CVE-2026-69263: Flowise MCP Environment Variable Blocklist Bypass (Unauthenticated RCE)
- highCVE-2026-69149CVE-2026-69149: @angular/platform-server SSR Fallback Raw-Content XSS