CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-CJ63-JHHR-WCXV
5.3

GHSA-cj63-jhhr-wcxv: Prototype Pollution to XSS Bypass in DOMPurify USE_PROFILES

Alon Barad
Alon Barad
Software Engineer

Apr 3, 2026·7 min read·1 visit

PoC Available

Executive Summary (TL;DR)

DOMPurify versions prior to 3.3.2 improperly initialize the ALLOWED_ATTR array when USE_PROFILES is configured. By polluting Array.prototype, attackers can force the sanitizer to accept malicious HTML attributes like 'onclick', leading to cross-site scripting.

A prototype pollution vulnerability in DOMPurify allows attackers to bypass the HTML sanitizer's attribute allowlist when the USE_PROFILES configuration is enabled. This results in DOM-based Cross-Site Scripting (XSS) via injected event handlers.

Vulnerability Overview

DOMPurify operates as an HTML sanitizer designed to strip malicious content from untrusted HTML strings before insertion into the Document Object Model (DOM). The library relies on an internal validation engine that compares incoming tags and attributes against strict allowlists. The vulnerability identified as GHSA-cj63-jhhr-wcxv occurs specifically when developers enable the USE_PROFILES configuration directive.

This flaw represents a severe intersection between an environmental weakness (Prototype Pollution, CWE-1321) and an application-level security bypass (Cross-Site Scripting, CWE-79). The root cause lies in how the library instantiates the ALLOWED_ATTR collection during profile generation. In vulnerable versions, this collection inherits properties from standard JavaScript prototypes.

An attacker capable of polluting Array.prototype can successfully bypass the sanitizer's validation logic. This manipulation allows the injection of arbitrary HTML attributes, including highly dangerous event handlers like onclick or onerror. The sanitizer erroneously identifies these malicious attributes as explicitly permitted entries.

Versions of the dompurify npm package prior to 3.3.2 contain this vulnerable code path. The maintainers resolved the issue in version 3.3.2 by utilizing prototype-less objects for attribute tracking, structurally eliminating the pollution vector.

Root Cause Analysis

The underlying flaw stems from the initialization methodology applied to the ALLOWED_ATTR variable. When the USE_PROFILES option is active, DOMPurify resets this structure to dynamically build an allowlist based on the selected profile (e.g., HTML, SVG, or MathML). In vulnerable iterations, the library defines this variable using a standard JavaScript array literal ([]).

Because standard arrays inherit properties from Array.prototype, any properties maliciously added to the prototype chain become implicitly accessible on the newly instantiated ALLOWED_ATTR array. JavaScript engine mechanics dictate that an object property lookup checks the object itself first, and upon failure, traverses the prototype chain. This architectural behavior facilitates the vulnerability.

DOMPurify validates incoming HTML attributes by checking for their explicit presence in the ALLOWED_ATTR structure. The engine executes a bracket notation lookup (ALLOWED_ATTR[lcName]), where lcName represents the lowercase name of the attribute under inspection. If the lookup returns a truthy value, the library classifies the attribute as safe and permits its inclusion in the finalized HTML string.

If an attacker previously pollutes the environment via Array.prototype.onclick = true, the lookup ALLOWED_ATTR['onclick'] seamlessly resolves to true via prototype inheritance. The sanitizer engine misinterprets this inherited value as a deliberate configuration choice, allowing the payload to bypass security filters.

Code Analysis and Patch Verification

The remediation implemented in version 3.3.2 directly targets the prototype chain initialization flaw. The structural modification occurs within the profile processing logic located in src/purify.ts, substituting the vulnerable array literal assignment with a secure alternative.

     /* Parse profile info */
     if (USE_PROFILES) {
       ALLOWED_TAGS = addToSet({}, TAGS.text);
-      ALLOWED_ATTR = [];
+      ALLOWED_ATTR = create(null); // create is Object.create
       if (USE_PROFILES.html === true) {
         addToSet(ALLOWED_TAGS, TAGS.html);
         addToSet(ALLOWED_ATTR, ATTRS.html);

The function create(null) serves as an internal alias for Object.create(null). This invocation generates a null-prototype object, sometimes referred to as a dictionary object. This distinct object type intentionally lacks an inherent link to Object.prototype or Array.prototype.

By severing the prototype chain entirely, the validation lookup ALLOWED_ATTR[lcName] exclusively evaluates properties explicitly mapped onto the object instance. Extraneous prototype pollution in the broader JavaScript environment no longer corrupts the sanitizer's internal validation structures.

Secondary hardening measures accompanied this fix in the 3.3.2 release. The maintainers restructured the _isValidAttribute function to process FORBID_ATTR rules immediately prior to allowlist validation. This ensures explicit deny lists act as an absolute override against any configuration anomalies, and the developers expanded the SAFE_FOR_XML regular expression to intercept inherently risky tags like script and iframe.

Exploitation Methodology

Exploiting this DOMPurify bypass requires a multi-stage attack sequence. The threat actor must first secure a mechanism to pollute the JavaScript prototype chain within the victim's execution environment. This initial vector often involves manipulating deeply nested JSON structures or exploiting distinct parameter parsing vulnerabilities present in the target application.

The initial payload involves injecting a property onto Array.prototype that precisely matches the name of a dangerous HTML attribute. For example, executing Array.prototype.onclick = true primes the environment for the bypass. Once the prototype is polluted, the attacker supplies the malicious HTML string containing the targeted attribute to the vulnerable DOMPurify implementation.

// 1. Induce Prototype Pollution via an external vulnerability
Array.prototype.onclick = true;
 
// 2. Supply the malicious HTML string
const dirty = '<img src="x" onerror="alert(1)" onclick="alert(\'XSS\')">';
 
// 3. Application invokes DOMPurify with USE_PROFILES enabled
const clean = DOMPurify.sanitize(dirty, { 
    USE_PROFILES: { html: true } 
});

During the sanitization pass, DOMPurify successfully strips the onerror attribute, as Array.prototype.onerror remains undefined. However, the onclick attribute survives the validation sweep entirely due to the earlier prototype manipulation. The sanitizer outputs <img src="x" onclick="alert('XSS')">.

The final execution phase relies on the application inserting the sanitized string into the live Document Object Model. User interaction with the maliciously augmented element, or automated execution mechanisms depending on the chosen attribute, triggers the arbitrary JavaScript payload in the context of the user's browser session.

Impact Assessment

The successful exploitation of GHSA-cj63-jhhr-wcxv neutralizes the core security guarantees provided by the DOMPurify library. Applications relying on the sanitizer to safely render user-controlled HTML become highly susceptible to DOM-based Cross-Site Scripting (XSS). The injected malicious scripts execute fully within the context of the victim's browser session.

This execution context grants the threat actor extensive capabilities. Attackers can exfiltrate sensitive session tokens, manipulate the user interface to facilitate phishing, or silently perform unauthorized actions on the platform under the guise of the authenticated user. The integrity and confidentiality of the user session are entirely compromised.

The CVSS v4.0 metrics assign a base score of 5.3 (Medium) to this vulnerability. The score correctly reflects the stringent prerequisite requirement: the attacker must establish independent prototype pollution capabilities before leveraging this bypass. The attack complexity itself remains low, as no specialized timing or race conditions are required.

The attack surface is strictly constrained by configuration dependencies. Applications that do not explicitly invoke the USE_PROFILES directive during DOMPurify instantiation remain structurally isolated from this specific bypass variant. The vulnerability exclusively impacts deployments heavily reliant on custom profile generation.

Remediation and Hardening

The primary and most effective remediation strategy necessitates updating the dompurify dependency to version 3.3.2 or later. This release integrates the structural changes replacing array literals with null-prototype objects, permanently neutralizing the prototype pollution attack vector within the attribute validation logic.

Engineering teams managing environments unable to execute an immediate dependency update can deploy a configuration-based mitigation. Disabling the USE_PROFILES configuration option bypasses the vulnerable code execution path completely. Developers should revert to manual ALLOWED_TAGS and ALLOWED_ATTR declarations until patching becomes feasible.

Applications should adopt global prototype pollution defenses as a standard architectural practice. Invoking structural freezing techniques, such as Object.freeze(Object.prototype) and Object.freeze(Array.prototype) early in the application lifecycle, blocks the prerequisite condition required to initiate the DOMPurify bypass.

Implementing a strict Content Security Policy (CSP) provides a critical secondary layer of defense. A robust CSP that aggressively restricts the execution of inline event handlers (e.g., omitting 'unsafe-inline') prevents the execution of the XSS payload, even if the application processes and renders the bypassed HTML output.

Official Patches

Cure53DOMPurify 3.3.2 Release Notes

Technical Appendix

CVSS Score
5.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:L/SI:L/SA:N

Affected Systems

DOMPurify (npm package)Applications utilizing DOMPurify with the USE_PROFILES configuration enabled

Affected Versions Detail

Product
Affected Versions
Fixed Version
dompurify
Cure53
< 3.3.23.3.2
AttributeDetail
GHSA IDGHSA-cj63-jhhr-wcxv
CVSS v4.0 Score5.3 (Medium)
CWE IDCWE-1321 (Prototype Pollution), CWE-79 (XSS)
Attack VectorNetwork
Exploit StatusProof of Concept (PoC) Available
Patched Version3.3.2

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-1321
Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

Prototype Pollution leading to Cross-Site Scripting (XSS)

Known Exploits & Detection

Research ContextProof of concept demonstrating Array.prototype pollution leading to 'onclick' validation bypass.

Vulnerability Timeline

Vulnerability published in GitHub Advisory Database
2026-04-03
Patch released in dompurify version 3.3.2
2026-04-03

References & Sources

  • [1]GitHub Advisory: GHSA-cj63-jhhr-wcxv
  • [2]Release Tag 3.3.2
  • [3]OSV Record: GHSA-cj63-jhhr-wcxv
  • [4]DOMPurify Patch Diff

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.