GHSA-h8r8-wccr-v5f2: DOMPurify is vulnerable to mutation-XSS via Re-Contextualization
DescriptionA mutation-XSS (mXSS) condition was confirmed when sanitized HTML is reinserted into a new parsing context using `innerHTML` and special wrappers. The vulnerable wrappers confirmed in browser behavior are `script`, `xmp`, `iframe`, `noembed`, `noframes`, and `noscript`. The payload remains seemingly benign after `DOMPurify.sanitize()`, but mutates during the second parse into executable markup with an event handler, enabling JavaScript execution in the client (`alert(1)` in the PoC).VulnerabilityThe root cause is context switching after sanitization: sanitized output is treated as trusted and concatenated into a wrapper string (for example, `<xmp> ... </xmp>` or other special wrappers) before being reparsed by the browser. In this flow, attacker-controlled text inside an attribute (for example `</xmp>` or equivalent closing sequences for each wrapper) closes the special parsing context early and reintroduces attacker markup (`<img ... onerror=...>`) outside the original attribute context. DOMPurify sanitizes the original parse tree, but the application performs a second parse in a different context, reactivating dangerous tokens (classic mXSS pattern).PoCStart the PoC app:Open `http://localhost:3001`.Set `Wrapper en sink` to `xmp`.Use payload:Click `Sanitize + Render`.Observe:`Sanitized response` still contains the `</xmp>` sequence inside `alt`.The sink reparses to include `<img src="x" onerror="alert('expoc')">`.`alert('expoc')` is triggered.Files:index.htmlserver.jspackage.jsonEvidencePoC[daft-video.webm](https://github.com/user-attachments/assets/499a593d-0241-4ab8-95a9-cf49a00bda90)XSS triggered
<img width="2746" height="1588" alt="daft-img" src="https://github.com/user-attachments/assets/1f463c14-d5a3-4c93-94e4-12d2d02c7d15" />Why This HappensThis is a mutation-XSS pattern caused by a parse-context mismatch:Parse 1 (sanitization phase): input is interpreted under normal HTML parsing rules.Parse 2 (sink phase): sanitized output is embedded into a wrapper that changes parser state (`xmp` raw-text behavior).Attacker-controlled sequence (`</xmp>`) gains structural meaning in parse 2 and alters DOM structure.Sanitization is not a universal guarantee across all future parsing contexts. The sink design reintroduces risk.Remediation GuidanceDo not concatenate sanitized strings into new HTML wrappers followed by `innerHTML`.Keep the rendering context stable from sanitize to sink.Prefer DOM-safe APIs (`textContent`, `createElement`, `setAttribute`) over string-based HTML composition.If HTML insertion is required, sanitize as close as possible to final insertion context and avoid wrapper constructs with raw-text semantics (`xmp`, `script`, etc.).Add regression tests for context-switch/mXSS payloads (including `</xmp>`, `</noscript>`, similar parser-breakout markers).Reported by Oscar Uribe, Security Researcher at Fluid Attacks. Camilo Vera and Cristian Vargas from the Fluid Attacks Research Team have identified a mXSS via Re-Contextualization in DomPurify 3.3.1.Following Fluid Attacks [Disclosure Policy](https://fluidattacks.com/advisories/policy), if this report corresponds to a vulnerability and the conditions outlined in the policy are met, this advisory will be published on the website over the next few days (the timeline may vary depending on maintainers' willingness to attend to and respond to this report) at the following URL: https://fluidattacks.com/advisories/daftAcknowledgements: [Camilo Vera](https://github.com/caverav/) and [Cristian Vargas](https://github.com/tachote).
CVSS v4.0: 6.9