Outdated HTML Sanitizer Allows Script Injection in Specific Contexts

Your website uses a popular library called DOMPurify to clean up user-submitted content before displaying it — think of it like a filter that removes dangerous code. A flaw in certain versions of this library means the filter has a few gaps: attackers who know about these gaps can sneak malicious scripts through, but only when the cleaned content is placed inside specific, less-common page sections. A patch is available and the fix is straightforward.

Business Impact And Actions

medium urgency

Business Impact

If exploited, an attacker could run malicious code in a visitor's browser — potentially stealing session cookies, redirecting users, or performing actions on their behalf. The risk is real but conditional: it requires the attacker to be able to submit content to your site AND for that content to be rendered inside one of a handful of specific HTML structures. Compliance-wise, a successful script injection incident could trigger breach notification obligations and damage customer trust.

What To Do

  1. Ask your developer to check which version of DOMPurify your site uses — if it's between 3.1.3 and 3.3.1 (or 2.5.3 and 2.5.8), it needs updating.
  2. Have your developer upgrade DOMPurify to version 3.3.2 or higher (or 2.5.9 if you're on the 2.x line). This is a dependency update and typically takes under an hour.
  3. If your site allows users to submit HTML content (comments, rich text editors, etc.), treat this as higher priority and schedule the fix this week.
  4. Consider asking your developer to add a Content Security Policy header as an extra layer of protection against script injection in general.

DOMPurify 3.1.3–3.3.1 / 2.5.3–2.5.8: mXSS via Incomplete SAFE_FOR_XML Regex (CVE-2026-0540)

medium severity CVSS 5.1–6.1

Vulnerability Explanation

CVE-2026-0540 is a mutation cross-site scripting (mXSS) vulnerability in DOMPurify's SAFE_FOR_XML mode. The SAFE_FOR_XML regex was designed to detect and block closing tags for rawtext elements (e.g., </style>, </title>) inside attribute values, preventing context-escape attacks. However, the regex only covered `style` and `title`, leaving five additional rawtext elements — `noscript`, `xmp`, `noembed`, `noframes`, and `iframe` — completely unguarded. An attacker can embed a payload such as `</noscript><img src=x onerror=alert(1)>` inside an attribute value. DOMPurify's sanitize() passes this as clean output. When the application then places that sanitized string inside a `<noscript>` wrapper (or equivalent) and re-parses it via innerHTML, the browser's HTML parser re-contextualizes the content: the closing `</noscript>` tag terminates the rawtext context early, and the injected `<img onerror=...>` is parsed as live markup, executing the attacker's JavaScript. This is a server-side rendering (SSR) and re-contextualization pattern — the payload appears benign after sanitization but mutates during the second parse.

Root Cause

The SAFE_FOR_XML regex pattern only matched closing tags for `style` and `title` rawtext elements. Five additional rawtext elements (`noscript`, `xmp`, `noembed`, `noframes`, `iframe`) were omitted from the pattern, creating a gap that allows attribute values containing their closing tags to escape the rawtext parsing context when the sanitized output is re-inserted into the DOM.

Technical Impact

An attacker who can inject content processed by the vulnerable DOMPurify version can achieve arbitrary JavaScript execution in the victim's browser. This enables session hijacking, credential theft, DOM manipulation, and actions performed on behalf of the victim. Exploitation requires the attacker to control input sanitized by DOMPurify AND for the application to re-insert that sanitized output inside one of the affected rawtext element wrappers.

Severity Justification

CVSS 4.0 score of 5.3 / CVSS 3.1 score of 6.1 (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N). Exploitation requires user interaction and a specific re-contextualization pattern in the application. No known active exploitation in the wild as of disclosure.

Affected Components

  • DOMPurify >= 3.1.3 and < 3.3.2
  • DOMPurify >= 2.5.3 and < 2.5.9

Remediation Steps

  1. Upgrade DOMPurify to version 3.3.2 or later (for the 3.x line) or 2.5.9 or later (for the 2.x line): `npm install dompurify@latest` or pin to the specific fixed version in package.json.
  2. Verify the installed version after upgrade: `node -e "const d = require('dompurify'); console.log(require('./node_modules/dompurify/package.json').version)"`
  3. Audit your codebase for any pattern where DOMPurify-sanitized output is concatenated into a string containing rawtext element wrappers (`<noscript>`, `<xmp>`, `<noembed>`, `<noframes>`, `<iframe>`) before being assigned to innerHTML. Refactor these to avoid re-contextualization, or sanitize after wrapping.
  4. Deploy or tighten a Content Security Policy (CSP) with `script-src 'self'` and without `unsafe-inline` as a defence-in-depth measure to limit the impact of any XSS that does execute.
  5. Run `npm audit` or `yarn audit` after upgrading to confirm no remaining known vulnerabilities in the DOMPurify dependency.

Verification Steps

  1. Run `npm list dompurify` and confirm the installed version is 3.3.2+ or 2.5.9+.
  2. Check the GitHub release notes at https://github.com/cure53/DOMPurify/releases/tag/3.3.2 to confirm the fix commit (fca0a938b4261ddc9c0293a289935a9029c049f5) is included.
  3. If you have a staging environment, attempt to pass `</noscript><img src=x onerror=alert(1)>` as an attribute value through DOMPurify and verify the output does not contain the unescaped closing tag.
  4. Use an online CSP evaluator (e.g., https://csp-evaluator.withgoogle.com/) to validate your Content Security Policy blocks inline script execution.

Code Examples (javascript)

Vulnerable
// DOMPurify 3.1.3 – 3.3.1: SAFE_FOR_XML regex only covers style/title
// Attacker input in an attribute value:
const userInput = 'value="</noscript><img src=x onerror=alert(1)>"';
const clean = DOMPurify.sanitize(userInput, { SAFE_FOR_XML: true });
// clean appears safe, but when re-inserted:
document.querySelector('noscript').innerHTML = clean; // XSS executes
Fixed
// Step 1: Upgrade to DOMPurify 3.3.2+ or 2.5.9+
// package.json
{
  "dependencies": {
    "dompurify": "^3.3.2"
  }
}

// Step 2: Avoid re-contextualizing sanitized output inside rawtext wrappers.
// Instead of:
noscriptEl.innerHTML = DOMPurify.sanitize(userInput);
// Sanitize AFTER wrapping, or use textContent for plain text:
noscriptEl.textContent = userInput; // if only plain text is needed

Best Practices

  • Never re-insert DOMPurify-sanitized HTML into a rawtext element context (noscript, xmp, iframe, noembed, noframes) via innerHTML — sanitize after wrapping, not before.
  • Implement a Content Security Policy with `script-src 'self'` to reduce the blast radius of any XSS bypass in sanitization libraries.
  • Pin and regularly audit frontend dependencies with `npm audit` or a software composition analysis (SCA) tool to catch vulnerable library versions early.
  • When using DOMPurify in server-side rendering contexts, be aware of re-contextualization risks: output that is safe in one HTML context may be unsafe when embedded in another.

Found this in your infrastructure?

VulWall scans for this and dozens of other issues automatically.

Scan Your Domain Free