DOMPurify CVE-2026-41238
MEDIUMAttack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
ReconExploit prototype pollution gadget
-
DeliveryPollute Object.prototype with permissive regex
-
ExploitCall DOMPurify.sanitize() with default config
-
InstallConfiguration fallback inherits polluted properties
-
C2Validation regex matches all custom elements and attributes
-
ExecuteXSS payload passes sanitization
-
ImpactDOM rendering triggers event handler
-
Step 8Attacker gains execution context
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A05:2025 - Injection
ASVS 5.0
V1 - Encoding and Sanitization Output encoding, context-aware escaping
MITRE ATT&CK
2 techniques across 2 tactics: Initial Access, Execution The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires three specific prerequisites: (1) The application must use DOMPurify 3.0.1 through 3.3.3 with the default configuration (no CUSTOM_ELEMENT_HANDLING option passed, or CUSTOM_ELEMENT_HANDLING set to null or undefined). Explicitly passing CUSTOM_ELEMENT_HANDLING: {} or CUSTOM_ELEMENT_HANDLING: {tagNameCheck: null, attributeNameCheck: null} prevents the vulnerability. (2) A prototype pollution gadget must be present and exploitable in the application's dependency tree or inline code-this could be lodash < 4.17.21, jQuery.extend, query-string parser, or any deep-merge utility vulnerable to prototype pollution. (3) The attacker must achieve execution context in the same JavaScript runtime-typically through ability to supply user input to a sanitizable content sink. The attack does not require authentication, but does require user interaction (UI:R in CVSS) meaning the polluted payload must be rendered in a victim's browser after triggering the prototype pollution. The vulnerability is NOT exploitable if the application explicitly defines CUSTOM_ELEMENT_HANDLING in the config, if no prototype pollution gadget exists in the application's dependency chain, or if the JavaScript execution context is isolated (e.g., Web Worker without access to Object.prototype of the main thread). |
| Impact | This vulnerability presents moderate real-world risk despite the 6.9 CVSS score. The CVSS vector (AV:N/AC:H/PR:N/UI:R/S:C) indicates network attack surface, high attack complexity (AC:H), no authentication, and requirement for user interaction (UI:R), which aligns with the actual attack scenario: user must trigger XSS when the polluted payload is rendered in their browser. The ACH factor likely reflects the prerequisite requirement that an attacker must first successfully exploit a separate prototype pollution gadget. However, this is not a theoretical concern-prototype pollution vulnerabilities are widespread in npm ecosystem dependencies (lodash before 4.17.21, jQuery.extend, query-string, deep-merge utilities, etc.). The XSS impact is severe (C:H/I:L/A:N suggests confidentiality breach and minor integrity compromise), and the default configuration vulnerability means any application using DOMPurify without explicitly setting CUSTOM_ELEMENT_HANDLING is automatically affected. Risk is further elevated because DOMPurify 3.0.1-3.3.3 represent recent versions that developers would be expected to update to. The vulnerability requires no configuration changes and affects the most common usage pattern. Detection of active exploitation has not yet been independently confirmed at the time of analysis, but the exploit is straightforward to construct given a prototype pollution gadget. Organizations should treat this as high-priority given the prevalence of prototype pollution in frontend libraries and the ubiquity of DOMPurify in security-sensitive applications. |
| Exploit ScenarioDerived | An attacker identifies that a target web application uses both DOMPurify 3.0.1-3.3.3 for HTML sanitization and a vulnerable dependency like lodash with a prototype pollution gadget. The attacker crafts a malicious input payload that first exploits the prototype pollution vulnerability (e.g., by manipulating query parameters or JSON data processed by the vulnerable lodash version) to set Object.prototype.tagNameCheck and Object.prototype.attributeNameCheck to permissive regex patterns. Subsequently, when the application calls DOMPurify.sanitize() on user-controlled content with a custom element and event handler (e.g., <x-pwn onfocus=alert(document.cookie) autofocus>), the sanitizer allows the payload through because its validation logic now resolves the polluted prototype properties. When the sanitized content is injected into the DOM, the event handler fires, stealing cookies or session tokens. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Apr 23, 2026
DOMPurify versions 3.0.1 through 3.3.3 fail to prevent prototype pollution-based XSS attacks when using default configurations. An attacker who can exploit a prototype pollution gadget elsewhere in the application can pollute Object.prototype with permissive regex values, causing DOMPurify to bypass sanitization and allow arbitrary custom elements with event handler attributes. The vulnerability affects the standard DOMPurify.sanitize(userInput) call without requiring special configuration.
RemediationAI
Vendor-released patch: DOMPurify 3.4.0 or later fixes the vulnerability by replacing the vulnerable || {} fallback with Object.create(null) at line 590, preventing prototype chain inheritance. Upgrade immediately by running npm update dompurify or yarn upgrade dompurify. For applications that cannot upgrade immediately, implement the application-level mitigation by always explicitly providing a CUSTOM_ELEMENT_HANDLING configuration: pass DOMPurify.sanitize(userInput, { CUSTOM_ELEMENT_HANDLING: { tagNameCheck: null, attributeNameCheck: null } }) instead of relying on default configurations. This explicit object triggers the configuration parsing path and prevents prototype pollution from affecting sanitization logic. Additionally, audit dependencies for known prototype pollution gadgets (lodash < 4.17.21, vulnerable versions of jquery, query-string, deep-merge, and other deep-merge utilities) and update those dependencies to patched versions. While not a remediation for this CVE specifically, implementing Content Security Policy (CSP) with no-inline-script restrictions provides defense-in-depth against any XSS that does bypass sanitization.
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
DOMPurify is a client-side DOM sanitization library that strips dangerous HTML and JavaScript from user-supplied content. The vulnerability stems from unsafe fallback object initialization in the configuration parsing logic (line 590 of purify.js). When CUSTOM_ELEMENT_HANDLING is not explicitly provided in the config, the code assigns a plain JavaScript object literal ({}) as the fallback. Unlike Object.create(null), this plain object inherits from Object.prototype through the normal prototype chain. If a prior prototype pollution gadget has polluted Object.prototype.tagNameCheck or Object.prototype.attributeNameCheck with permissive regex patterns (e.g., /.*/ which matches everything), these polluted values propagate through the prototype chain into DOMPurify's validation logic at lines 973-977 and attribute validation routines. The vulnerability is distinct from GHSA-cj63-jhhr-wcxv (Array.prototype pollution via USE_PROFILES, fixed in 3.3.2) and CVE-2024-45801 (GHSA-mmhx-hmjr-r674, __depth prototype pollution, fixed in 3.1.3). The attack chain requires two sequential vulnerabilities: first, a prototype pollution primitive in the application's dependency tree or code, and second, DOMPurify's failure to isolate its configuration objects from prototype chain pollution.
Know when the next one hits your stack
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:L/A:N
Primary rating from Vendor (https://github.com/cure53/DOMPurify).
CVSS VectorVendor: https://github.com/cure53/DOMPurify
Lifecycle Timeline
2DescriptionCVE.org
Summary
DOMPurify versions 3.0.1 through 3.3.3 (latest) are vulnerable to a prototype pollution-based XSS bypass. When an application uses DOMPurify.sanitize() with the default configuration (no CUSTOM_ELEMENT_HANDLING option), a prior prototype pollution gadget can inject permissive tagNameCheck and attributeNameCheck regex values into Object.prototype, causing DOMPurify to allow arbitrary custom elements with arbitrary attributes - including event handlers - through sanitization.
Affected Versions
- 3.0.1 through 3.3.3 (current latest) - all affected
- 3.0.0 and all 2.x versions - NOT affected (used
Object.create(null)for initialization, no|| {}reassignment) - The vulnerable
|| {}reassignment was introduced in the 3.0.0→3.0.1 refactor - This is distinct from GHSA-cj63-jhhr-wcxv (USE_PROFILES Array.prototype pollution, fixed in 3.3.2)
- This is distinct from CVE-2024-45801 / GHSA-mmhx-hmjr-r674 (__depth prototype pollution, fixed in 3.1.3)
Root Cause
In purify.js at line 590, during config parsing:
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};When no CUSTOM_ELEMENT_HANDLING is specified in the config (the default usage pattern), cfg.CUSTOM_ELEMENT_HANDLING is undefined, and the fallback {} is used. This plain object inherits from Object.prototype.
Lines 591-598 then check cfg.CUSTOM_ELEMENT_HANDLING (the original config property) - which is undefined - so the conditional blocks that would set tagNameCheck and attributeNameCheck from the config are never entered.
As a result, CUSTOM_ELEMENT_HANDLING.tagNameCheck and CUSTOM_ELEMENT_HANDLING.attributeNameCheck resolve via the prototype chain. If an attacker has polluted Object.prototype.tagNameCheck and Object.prototype.attributeNameCheck with permissive values (e.g., /.*/), these polluted values flow into DOMPurify's custom element validation at lines 973-977 and attribute validation, causing all custom elements and all attributes to be allowed.
Impact
- Attack type: XSS bypass via prototype pollution chain
- Prerequisites: Attacker must have a prototype pollution primitive in the same execution context (e.g., vulnerable version of lodash, jQuery.extend, query-string parser, deep merge utility, or any other PP gadget)
- Config required: Default. No special DOMPurify configuration needed. The standard
DOMPurify.sanitize(userInput)call is affected. - Payload: Any HTML custom element (name containing a hyphen) with event handler attributes survives sanitization
Proof of Concept
// Step 1: Attacker exploits a prototype pollution gadget elsewhere in the application
Object.prototype.tagNameCheck = /.*/;
Object.prototype.attributeNameCheck = /.*/;
// Step 2: Application sanitizes user input with DEFAULT config
const clean = DOMPurify.sanitize('<x-x onfocus=alert(document.cookie) tabindex=0 autofocus>');
// Step 3: "Sanitized" output still contains the event handler
console.log(clean);
// Output: <x-x onfocus="alert(document.cookie)" tabindex="0" autofocus="">
// Step 4: When injected into DOM, XSS executes
document.body.innerHTML = clean; // alert() firesTested configurations that are vulnerable:
| Call Pattern | Vulnerable? |
|---|---|
DOMPurify.sanitize(input) | YES |
DOMPurify.sanitize(input, {}) | YES |
DOMPurify.sanitize(input, { CUSTOM_ELEMENT_HANDLING: null }) | YES |
DOMPurify.sanitize(input, { CUSTOM_ELEMENT_HANDLING: {} }) | NO (explicit object triggers L591 path) |
Suggested Fix
Change line 590 from:
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};To:
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);The create(null) function (already used elsewhere in DOMPurify, e.g., in clone()) creates an object with no prototype, preventing prototype chain inheritance.
Alternative application-level mitigation:
Applications can protect themselves by always providing an explicit CUSTOM_ELEMENT_HANDLING in their config:
DOMPurify.sanitize(input, {
CUSTOM_ELEMENT_HANDLING: {
tagNameCheck: null,
attributeNameCheck: null
}
});Timeline
- 2026-04-04: Vulnerability discovered during automated DOMPurify fuzzing research (Fermat project)
- 2026-04-04: Confirmed in Chrome browser with DOMPurify 3.3.3
- 2026-04-04: Verified distinct from GHSA-cj63-jhhr-wcxv and CVE-2024-45801
- 2026-04-04: Advisory drafted, responsible disclosure initiated
Credit
https://github.com/trace37labs
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Cross-Site Scripting occurs when an application accepts untrusted data and sends it to a web browser without proper validation or encoding.
GHSA-v9jr-rg53-9pgp
NVD
CVE.org
EPSS
Vulnrichment
VulDB
Exploit-DB