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.7better-auth Stored XSS via javascript: redirect_uri in oidc-provider and mcp Plugins
- highCVE-2026-49864CVE-2026-49864: wetty DOM XSS via File-Download Filename
- highfast-xml-parser: Repeated DOCTYPE Declarations Reset Entity Expansion Limits
- high · 7.5CVE-2026-59892CVE-2026-59892: @opentelemetry/propagator-jaeger Denial of Service via Malformed Header