Skip to main content

DOMPurify CVE-2026-41238

MEDIUM
Cross-site Scripting (XSS) (CWE-79)
2026-04-22 https://github.com/cure53/DOMPurify GHSA-v9jr-rg53-9pgp
6.9
CVSS 3.1 · Vendor: https://github.com/cure53/DOMPurify
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Recon
    Exploit prototype pollution gadget
  2. Delivery
    Pollute Object.prototype with permissive regex
  3. Exploit
    Call DOMPurify.sanitize() with default config
  4. Install
    Configuration fallback inherits polluted properties
  5. C2
    Validation regex matches all custom elements and attributes
  6. Execute
    XSS payload passes sanitization
  7. Impact
    DOM rendering triggers event handler
  8. Step 8
    Attacker 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.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.0% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – 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

npm dompurify fix: 3.4.0

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

Vendor (https://github.com/cure53/DOMPurify) PRIMARY 6.9 MEDIUM
AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:L/A:N
Red Hat 6.8 MEDIUM

Primary rating from Vendor (https://github.com/cure53/DOMPurify).

CVSS VectorVendor: https://github.com/cure53/DOMPurify

Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
Required
Scope
Changed
Confidentiality
High
Integrity
Low
Availability
None

Lifecycle Timeline

2
Patch released
Apr 23, 2026 - 02:30 nvd
Patch available
CVE Published
Apr 22, 2026 - 17:31 nvd
MEDIUM 6.9

DescriptionCVE.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:

javascript
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

javascript
// 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() fires

Tested configurations that are vulnerable:

Call PatternVulnerable?
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:

javascript
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};

To:

javascript
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:

javascript
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

EPSS Score
0.0%
Top 90% exploitable
Threat Intel
1
1 source
Data Quality
7 sources
Confidence: High
NVD CVE.org EPSS Vulnrichment GHSA Vendor GitHub VulDB

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.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.0%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact· derived
Medium
Technical Impact
Partial

Priority Score

34
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +34
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Cross-Site Scripting occurs when an application accepts untrusted data and sends it to a web browser without proper validation or encoding.

Affected Products (CPE)CVE.org

pkg:npm/dompurify

Vendor StatusVendor

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy