Outdated HTML Sanitizer Allows Script Injection in Specific Contexts

Your application uses a library called DOMPurify to clean up untrusted content (like user-submitted text) before displaying it on your website. A flaw in the version you're running means that cleaning process can be bypassed under specific conditions, potentially allowing malicious scripts to run in a visitor's browser. Upgrading to the latest version closes the gap.

Business Impact And Actions

high urgency

Business Impact

If exploited, an attacker who can submit content to your site could run scripts in other users' browsers — potentially stealing session cookies, redirecting users to phishing pages, or performing actions on their behalf. The risk is real but conditional: it only applies if your app renders DOMPurify-cleaned content inside certain HTML elements (like form text areas or noscript blocks). A compliance audit would flag this as an unpatched known vulnerability in a security-critical library.

What To Do

  1. Ask your developer to upgrade the DOMPurify npm package to version 3.2.7 or later — this is typically a 15-minute change.
  2. If you're on the 2.x branch of DOMPurify, ask your developer to migrate to the 3.x branch, as the 2.x branch will not receive a patch for this issue.
  3. Ask your developer to add or strengthen a Content Security Policy (CSP) header on your site — this acts as a second line of defence that limits what scripts can run even if sanitization is bypassed.
  4. After the upgrade, ask your developer to confirm the new version is deployed by checking the package version in your production build.

DOMPurify 3.1.3–3.2.6: XSS via Textarea Rawtext Bypass in SAFE_FOR_XML (CVE-2025-15599)

medium severity CVSS 6.1

Vulnerability Explanation

DOMPurify's SAFE_FOR_XML mode uses a regex to detect and strip dangerous closing tags (e.g., </style>, </title>) from attribute values, preventing context-escape attacks when sanitized output is embedded in XML or rawtext HTML elements. The vulnerable versions failed to include `</textarea>` in this regex pattern. An attacker can craft an attribute value containing `</textarea>` followed by a JavaScript payload. When DOMPurify sanitizes this input, the closing tag passes through unchallenged. If the application then renders the sanitized output inside a `<textarea>` element (or similar rawtext context), the browser's parser interprets the injected closing tag as ending the textarea, breaking out of the rawtext context and executing the subsequent JavaScript.

Root Cause

The SAFE_FOR_XML regex in DOMPurify's attribute sanitization logic (`_sanitizeAttributes`) only checked for a subset of rawtext element closing tags (`</style>`, `</title>`), omitting `</textarea>` and other rawtext elements. This incomplete allowlist left a gap that could be exploited when sanitized output was placed inside a textarea or similar rawtext context.

Technical Impact

An attacker who can supply input that is sanitized by the vulnerable DOMPurify version and then rendered inside a rawtext element (e.g., `<textarea>`, `<noscript>`, `<xmp>`) can execute arbitrary JavaScript in the victim's browser. This enables session hijacking, credential theft, DOM manipulation, or redirection to attacker-controlled pages. Exploitation requires: (1) attacker-controlled input reaches DOMPurify sanitization, and (2) the sanitized output is placed inside a rawtext HTML element.

Severity Justification

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N — Network-accessible, no privileges required, but requires user interaction and a specific rendering context (output placed inside a rawtext element). VulnCheck scores this 5.1 under CVSS v4. Consistent with a medium rating.

Affected Components

  • DOMPurify >= 3.1.3 and < 3.2.7
  • DOMPurify >= 2.5.3 and <= 2.5.8 (2.x branch — no patch available)

Remediation Steps

  1. Upgrade DOMPurify to version 3.2.7 or later (latest stable is 3.3.2+): `npm install dompurify@latest` or `yarn upgrade dompurify`.
  2. If you are on the 2.x branch, migrate to 3.x. The 2.x branch received no patch for this CVE. If migration is not immediately possible, audit all call sites where sanitized output is inserted into rawtext elements and eliminate those patterns as a temporary mitigation.
  3. Audit your codebase for patterns where `DOMPurify.sanitize()` output is assigned to `element.innerHTML` or `element.value` inside a `<textarea>`, `<noscript>`, `<xmp>`, `<noembed>`, `<noframes>`, or `<iframe>` — these are the vulnerable rendering contexts.
  4. Implement or strengthen a Content Security Policy (CSP) header with `script-src 'self'` (or a nonce-based policy) to limit inline script execution as a defence-in-depth measure.
  5. Lock your dependency version in `package.json` to `>=3.2.7` to prevent accidental downgrades.

Verification Steps

  1. Run `npm list dompurify` or `yarn list dompurify` in your project root and confirm the installed version is 3.2.7 or higher.
  2. Check your production bundle: search for the DOMPurify version string with `grep -r 'DOMPurify' dist/ | grep version` or inspect the minified bundle for the version comment.
  3. Use the GitHub Advisory GHSA-v8jm-5vwx-cfxm as a reference to confirm the fix commit (c861f5a83fb8d90800f1680f855fee551161ac2b) is included in your installed version.

Code Examples (javascript)

Vulnerable
// Vulnerable: DOMPurify 3.1.3–3.2.6 with SAFE_FOR_XML (default: true)
// Attacker-supplied input:
const dirty = '<p title="</textarea><img src=x onerror=alert(1)>">hello</p>';
const clean = DOMPurify.sanitize(dirty); // </textarea> passes through

// Dangerous rendering context:
document.querySelector('textarea').innerHTML = clean;
// Browser parses </textarea>, breaks out of rawtext, executes onerror handler
Fixed
// Step 1: Upgrade to DOMPurify >= 3.2.7
// package.json
{
  "dependencies": {
    "dompurify": "^3.2.7"
  }
}

// Step 2: The same sanitize call is now safe — the regex
// correctly strips </textarea> from attribute values.
const clean = DOMPurify.sanitize(dirty); // </textarea> is stripped

// Step 3 (defence-in-depth): Add CSP header
// Nginx example:
// add_header Content-Security-Policy "script-src 'self';" always;

Best Practices

  • Never render DOMPurify-sanitized output directly inside rawtext elements (`<textarea>`, `<noscript>`, `<xmp>`, `<iframe>`) — prefer setting `textContent` or using safe DOM APIs instead.
  • Pin security-critical dependencies like DOMPurify to a minimum safe version in `package.json` and enforce this in CI with `npm audit --audit-level=moderate`.
  • Deploy a Content Security Policy that restricts inline script execution; this limits the blast radius of any future sanitizer bypass.
  • Subscribe to DOMPurify's security mailing list (https://lists.ruhr-uni-bochum.de/mailman/listinfo/dompurify-security) to receive prompt notification of future security releases.

Found this in your infrastructure?

VulWall scans for this and dozens of other issues automatically.

Scan Your Domain Free