Mar 28, 2026·6 min read·5 visits
DOMPurify < 3.3.2 fails to properly neutralize specific raw-text elements like `<noscript>`. Attackers can inject payloads that bypass initial sanitization but mutate into executable JavaScript when re-inserted into the DOM.
DOMPurify versions prior to 3.3.2 are susceptible to a Mutation Cross-Site Scripting (mXSS) vulnerability. The flaw occurs due to discrepancies in browser parsing contexts when handling specific raw-text or RCDATA elements, allowing attackers to bypass sanitization.
DOMPurify is a widely deployed HTML sanitization library designed to strip malicious executable code from untrusted input before it is rendered in a web application. The library operates by parsing the input string into a Document Object Model (DOM) tree, traversing the nodes, and removing elements or attributes that do not comply with a strict allowlist. This mechanism relies entirely on the browser's built-in HTML parser to construct the initial state of the input.
GHSA-H8R8-WCCR-V5F2 identifies a Mutation Cross-Site Scripting (mXSS) vulnerability rooted in how the browser handles re-contextualization of specific HTML elements. The flaw affects DOMPurify versions prior to 3.3.2. When DOMPurify processes certain "wrapper" tags—specifically <script>, <xmp>, <iframe>, <noembed>, <noframes>, and <noscript>—it analyzes their contents based on the current execution environment.
If the sanitized output is subsequently inserted into a different parsing context, the browser may interpret the previously neutralized payload as active HTML. This state discrepancy allows malicious JavaScript to execute despite passing through the DOMPurify sanitization pipeline. The vulnerability requires no user interaction and triggers automatically upon re-insertion of the sanitized payload.
The root cause of this vulnerability lies in the HTML specification's definitions for Raw Text and Replaceable Character Data (RCDATA) elements, combined with parser state variations. Browsers parse tags like <noscript> differently depending on the active environment, particularly whether JavaScript is enabled or disabled in the context where the parsing occurs.
When DOMPurify sanitizes input, it typically does so in a context where JavaScript is enabled. In this state, the browser parser treats the contents of a <noscript> tag as plain text rather than parsable HTML. Consequently, any nested HTML tags within the <noscript> element are not constructed as DOM nodes. DOMPurify inspects the resulting text node, identifies no malicious elements or attributes, and passes the string without modification.
However, the vulnerability manifests during re-contextualization. If the application takes the sanitized string and assigns it to the innerHTML of an element in a context where the parsing rules differ, the browser re-evaluates the string. The previously benign text inside the <noscript> tag is now treated as live HTML. The browser attempts to build DOM nodes from the text, successfully parsing and executing the hidden attacker payload.
The flaw exposes a fundamental challenge in client-side HTML sanitization: the sanitizer's DOM tree must exactly match the execution context's DOM tree. Prior to version 3.3.2, DOMPurify's tag handling logic did not aggressively neutralize payloads nested inside state-dependent RCDATA or raw-text tags.
In the vulnerable implementation, when the library encountered a <noscript> tag containing nested markup, the DOM traversal only saw a text node. The sanitization logic effectively bypassed the inner contents because the browser did not expose them as HTML elements.
Commit 5e56114 addresses this by introducing stricter handling for raw-text and RCDATA tags. The patch hardens the configuration and parsing logic to ensure that these specific tags (<script>, <xmp>, <iframe>, <noembed>, <noframes>, <noscript>) cannot be used to smuggle malicious attributes or prematurely close parent tags. The mitigation logic proactively strips or encodes elements that could alter the parsing state upon re-contextualization.
Exploiting this vulnerability requires the target application to accept user input, pass it through a vulnerable version of DOMPurify, and immediately assign the output to a new DOM execution context (such as via innerHTML or outerHTML).
The attack methodology relies on premature tag closure and state mutation. An attacker submits the following payload: <div><noscript><p title="</noscript><img src=x onerror=alert(1)>"></div>. During the sanitization phase, DOMPurify reads the <noscript> tag as raw text. The parser does not evaluate the </noscript> string as a closing tag because it is inside the plain text block, nor does it see the <img> tag.
Upon re-insertion into the target application, the browser parser context mutates. The browser encounters the </noscript> sequence inside the title attribute and interprets it as the definitive closing tag for the initial <noscript> element. This forces the browser to break out of the plain text parsing state. The subsequent string <img src=x onerror=alert(1)> is immediately parsed as a new, executable HTML element. The invalid src attribute triggers the onerror event handler, executing the attacker's arbitrary JavaScript.
The vulnerability holds a CVSS v3.1 score of 6.5 (Medium), reflecting a network-based attack vector with low complexity and no required user interaction. The execution of arbitrary JavaScript within the victim's browser session results in low confidentiality and integrity impacts according to the CVSS metric, though practical client-side exploitation often leads to full account compromise.
Successful exploitation allows the attacker to execute cross-site scripting attacks within the origin of the vulnerable application. This provides access to sensitive session tokens, localStorage data, and the ability to perform unauthorized actions on behalf of the user. The attack payload can silently exfiltrate data or modify the structural integrity of the page.
Because the payload bypasses the primary sanitization layer, applications relying exclusively on DOMPurify for untrusted data handling are fully exposed. The lack of user interaction means that automated exploitation can occur whenever the vulnerable application renders the malicious input. The exploit operates entirely on the client side, leaving server-side availability unaffected.
The primary remediation for this vulnerability is to upgrade the DOMPurify library to version 3.3.2 or later. The patch provided by Cure53 comprehensively addresses the re-contextualization vectors associated with the identified raw-text and RCDATA tags.
Development teams should update their dependencies using the standard package manager workflows. For Node.js and NPM environments, executing npm install dompurify@latest will install the patched version. It is critical to audit package-lock.json or yarn.lock files to ensure no transitive dependencies are pulling in vulnerable legacy versions of the library.
If immediate patching is technically unfeasible, administrators can apply defensive programming measures. Ensure that untrusted content is never rendered in execution contexts that differ from the sanitization context. Additionally, implementing a robust Content Security Policy (CSP) that restricts inline script execution (unsafe-inline) will provide a secondary defense layer, mitigating the impact of successful XSS execution even if the DOMPurify bypass occurs.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
dompurify Cure53 | < 3.3.2 | 3.3.2 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Mutation Cross-Site Scripting (mXSS) |
| CWE ID | CWE-79 |
| CVSS v3.1 Score | 6.5 Medium |
| Attack Vector | Network |
| User Interaction | None |
| Exploit Status | Proof of Concept Available |
| Affected Component | Raw Text/RCDATA Parser Constraints |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')