CVE-2026-41238: DOMPurify: Prototype Pollution to XSS Bypass via CUSTOM_ELEMENT_HANDLING Fallback

Published Apr 22, 2026
·
Updated

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 CUSTOMELEMENTHANDLING 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 (USEPROFILES 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 CUSTOMELEMENTHANDLING = cfg.CUSTOMELEMENTHANDLING || {};

When no CUSTOMELEMENTHANDLING is specified in the config (the default usage pattern), cfg.CUSTOMELEMENTHANDLING is undefined, and the fallback {} is used. This plain object inherits from Object.prototype.

Lines 591-598 then check cfg.CUSTOMELEMENTHANDLING (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, CUSTOMELEMENTHANDLING.tagNameCheck and CUSTOMELEMENTHANDLING.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 Pattern | Vulnerable? | |---|---| | DOMPurify.sanitize(input) | YES | | DOMPurify.sanitize(input, {}) | YES | | DOMPurify.sanitize(input, { CUSTOMELEMENTHANDLING: null }) | YES | | DOMPurify.sanitize(input, { CUSTOMELEMENTHANDLING: {} }) | NO (explicit object triggers L591 path) |

Suggested Fix

Change line 590 from: javascript CUSTOMELEMENTHANDLING = cfg.CUSTOMELEMENTHANDLING || {};

To: javascript CUSTOMELEMENTHANDLING = cfg.CUSTOMELEMENTHANDLING || 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 CUSTOMELEMENTHANDLING in their config:

javascript DOMPurify.sanitize(input, { CUSTOMELEMENTHANDLING: { 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

Other sources

DOMPurify is a DOM-only cross-site scripting sanitizer for HTML, MathML, and SVG. Versions 3.0.1 through 3.3.3 are vulnerable to a prototype pollution-based XSS bypass. When an application uses DOMPurify.sanitize() with the default configuration (no CUSTOMELEMENTHANDLING 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. Version 3.4.0 fixes the issue.

NVD

Affected Software

3 affected componentsFixes available
npm/dompurify>=3.0.1<3.4.0
3.4.0
IBM Db2 Genius Hub<=1.1, 1.1.1, 1.1.2
IBM Agentics<=1.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/dompurify to a version that resolves this vulnerability.

    Fixed in 3.4.0
  2. Upgrade

    Upgrade DOMPurify to a version that resolves this vulnerability.

    Fixed in 3.4.0
  3. Configuration

    In the application’s DOMPurify.sanitize() call, always pass an explicit CUSTOM_ELEMENT_HANDLING in the config so tagNameCheck and attributeNameCheck do not resolve via the prototype chain (avoid relying on the fallback when CUSTOM_ELEMENT_HANDLING is omitted or left undefined).

    DOMPurify CUSTOM_ELEMENT_HANDLING = { tagNameCheck: <explicit function/regex>, attributeNameCheck: <explicit function/regex> }

Event History

Apr 22, 2026
Advisory Published
via GitHub·05:31 PM
Data Sourced
via GitHub·05:31 PM
DescriptionSeverityWeaknessAffected Software
Apr 23, 2026
CVE Published
via MITRE·02:43 PM
Data Sourced
via MITRE·02:43 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·04:16 PM
DescriptionSeverityWeakness
Jul 13, 2026
Data Sourced
via IBM·12:00 AM
DescriptionAffected Software

Parent advisories

This vulnerability appears in the following advisories.

Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-41238?

CVE-2026-41238 is classified as a high severity vulnerability due to its potential exploitation via prototype pollution-based XSS bypass.

2

How do I fix CVE-2026-41238?

To fix CVE-2026-41238, upgrade DOMPurify to version 3.4.0 or later.

3

Which versions of DOMPurify are affected by CVE-2026-41238?

DOMPurify versions 3.0.1 through 3.3.3 are affected by CVE-2026-41238.

4

What is the impact of CVE-2026-41238?

The impact of CVE-2026-41238 includes the potential for cross-site scripting (XSS) attacks due to an XSS bypass.

5

What configurations are vulnerable in relation to CVE-2026-41238?

The default configuration of DOMPurify without the `CUSTOM_ELEMENT_HANDLING` option is vulnerable in relation to CVE-2026-41238.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203