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-P98J-92PF-MC4P

GHSA-P98J-92PF-MC4P: DOM-Based Cross-Site Scripting (DOM XSS) via Hook Detach Bypass in DOMPurify In-Place Sanitization

Amit Schendel
Amit Schendel
Senior Security Researcher

Sep 30, 2026·8 min read·20 visits

Executive Summary (TL;DR)

DOMPurify versions prior to 3.4.16 are vulnerable to DOM XSS when using in-place sanitization with custom hooks. If a hook detaches an element during the afterSanitizeElements or afterSanitizeAttributes phases, nested payloads avoid sanitization and execute in the browser.

A DOM-based Cross-Site Scripting (DOM XSS) vulnerability in DOMPurify allows attackers to bypass sanitization when using the in-place sanitization configuration (IN_PLACE: true) combined with custom hooks. If custom hooks detach elements during post-processing phases, nested malicious payloads can escape sterilization and execute in the browser's context.

Vulnerability Overview & Technical Context

DOMPurify is a client-side HTML sanitization library designed to mitigate Cross-Site Scripting (XSS) vulnerabilities. By default, the library parses dirty HTML markup inside an inert, detached DOM context, such as a DocumentFragment, to prevent premature resource resolution or execution of malicious handlers. Once sanitization is complete, the sanitized node is either converted back to a safe HTML string or imported into the target document safely.

To optimize performance in intensive client-side environments, DOMPurify offers an alternative execution configuration known as in-place sanitization (IN_PLACE: true). When this setting is enabled, DOMPurify performs sanitization directly on a live DOM tree passed by the application, modifying elements in memory. This option is popular in complex single-page applications because it completely bypasses the processing overhead associated with repeated DOM serialization and deserialization.

However, this optimization introduces a critical attack surface when combined with custom sanitization hooks. If an application registers custom hooks that programmatically detach elements from the active document structure, the sanitization engine's traversal mechanics are disrupted. This structural disruption allows nested malicious elements within the detached branch to escape validation entirely.

This vulnerability is cataloged as GHSA-P98J-92PF-MC4P and mapped to CWE-79 (Improper Neutralization of Input During Web Page Generation). Because it has no corresponding CVE record, it escapes detection by traditional vulnerability scanners and automated security audit tools, making manual technical analysis and targeted remediation essential for secure application engineering.

Root Cause Analysis of the Traversal Loophole

The root cause of GHSA-P98J-92PF-MC4P lies in the interaction between the browser's native tree traversal APIs, custom hook callbacks, and DOMPurify's post-sanitization lifecycle phases. DOMPurify relies on a native NodeIterator object, initialized via document.createNodeIterator, to walk the targeted DOM tree. The iterator flatly and sequentially visits every node in the DOM tree, executing security validations on each element.

To allow developers to implement custom policies, DOMPurify exposes multiple extensibility hooks that execute at distinct stages of element processing. These hooks include beforeSanitizeElements, uponSanitizeElement, and the post-sanitization phases afterSanitizeElements and afterSanitizeAttributes. If a custom hook callback detaches a node from the active tree (for example, by invoking node.remove() or parentNode.removeChild()), the parent-child relationship of that subtree is broken.

When a node is detached, the browser's NodeIterator automatically advances its internal pointer past the detached element. Consequently, the descendants of the detached node are never visited by the remaining iterations of the sanitization loop. This results in a complete bypass of DOMPurify's validation routine for all children nested within the detached parent node.

Because the sanitization operates on a live DOM tree, the browser's rendering engine may have already parsed the untrusted markup and scheduled asynchronous events before the sanitizer is invoked. An element such as <img src="invalid" onerror="payload"> schedules its onerror callback in the browser's event queue immediately upon parsing. If the container element is detached during a hook phase and its nested descendants escape neutralization, the browser processes the queued event handler once synchronous execution completes, triggering the XSS payload.

Detailed Code and Patch Analysis

Prior to version 3.4.16, DOMPurify maintained a defensive mechanism called _handleHookDetachedNode. This function recursively invokes _neutralizeSubtree to strip all attributes and event handlers from any detached node, neutralizing potential threats. However, this safety function was only integrated during the early phases of node processing: beforeSanitizeElements and uponSanitizeElement. Detachments occurring during the post-processing hook phases bypassed this defense entirely.

The security patch introduced in commit b9b9d80f7e401771c2ccaef5f45def7eec8f27d7 resolves this gap by integrating _handleHookDetachedNode into the exit paths of the afterSanitizeElements and afterSanitizeAttributes hooks. This ensures that any detachment occurring at the end of element or attribute processing triggers immediate neutralization of the detached subtree before the browser returns control to the event loop.

// Patch in src/purify.ts fixing the afterSanitizeElements block
if (removed === false) {
  _executeHooks(hooks.afterSanitizeElements, currentNode, null);
 
  /* The hook may have detached the kept custom element. Same
     IN_PLACE hazard as the before/upon sites: neutralize the
     detached subtree before the walker skips past it (see
     _handleHookDetachedNode). */
  if (_handleHookDetachedNode(currentNode, root)) {
    return true;
  }
}

Additionally, the patch addresses potential bypasses within the attribute sanitization routines. In _sanitizeAttributes, a custom hook could detach the target node, which would previously go undetected. The patched implementation checks for node detachment both before and after executing attribute-focused hooks, establishing complete lifecycle coverage.

const _sanitizeAttributes = function (
  currentNode: Element,
  root: Node
): void {
  /* Execute a hook if present */
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
 
  /* A hook may have detached the node - the attribute hooks can detach
     exactly like the element hooks can, and the walker will not revisit
     the detached subtree. Treat it as removed and, on the IN_PLACE path,
     neutralize it (see _handleHookDetachedNode for the full rationale). */
  if (_handleHookDetachedNode(currentNode, root)) {
    return;
  }
  // ...

Exploitation Mechanics and Workflow

To successfully exploit this vulnerability, three distinct conditions must be met. First, the application must invoke DOMPurify with the { IN_PLACE: true } configuration option. Second, a custom hook callback must be registered that conditionally removes or detaches elements during the afterSanitizeElements or afterSanitizeAttributes lifecycle phases. Third, the application must allow user-controlled markup to be appended to a live DOM element before invoking the sanitizer.

The exploit workflow begins when an attacker-crafted payload is injected into the application. The payload is carefully nested, ensuring that a benign outer element matches the deletion logic of the custom hook, while a malicious inner element contains the active payload.

<div id="remove-me">
  <img src="does-not-exist" onerror="alert('DOM XSS')">
</div>

When the application sets the innerHTML of a live container with this payload, the browser parsing engine instantly queues the image's onerror event handler. Next, the application calls DOMPurify.sanitize(element, { IN_PLACE: true }). During the walk, the custom hook identifies <div id="remove-me"> and detaches it. Since the detachment happens in the post-processing hook, the inner <img> is skipped by the walker and escapes sanitization. Once the synchronous execution context of the sanitizer returns, the browser executes the queued event handler in the application context.

Below is a conceptual visual representing the traversal bypass:

Security Impact and Risk Assessment

The security impact of GHSA-P98J-92PF-MC4P is severe, resulting in unauthenticated arbitrary JavaScript execution in the browser of the victim. This execution occurs within the context of the vulnerable application's origin. Consequently, the attacker can access sensitive resources, session tokens, and localStorage variables, and perform actions on behalf of the victim.

In modern single-page applications, DOM XSS allows attackers to perform session hijacking, exfiltrate JSON Web Tokens (JWTs), and bypass anti-CSRF protections. Attackers can also inject credential harvesting fields or redirect users to malicious third-party destinations. Because the exploit runs under the trusted domain name, users are highly susceptible to social engineering or phishing tactics integrated into the hijacked session.

The vulnerability is assessed with a CVSS 3.1 score of 8.1 (High) using the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N. Although the impact is critical, the exploitability is constrained by the requirement for specific application-defined hooks and configuration options, which lowers the probability of widespread automated exploitation but remains highly dangerous for targeted installations.

Remediation and Architectural Hardening

The primary remediation path is upgrading the dompurify dependency to version 3.4.16 or higher. This version ensures that any elements detached during post-processing hooks are systematically neutralized, preventing event handlers inside the detached subtree from executing.

npm install dompurify@3.4.16

If upgrading is not immediately possible, developers can implement architectural workarounds. Custom hooks should avoid calling raw detachment methods like node.remove() or node.parentNode.removeChild(node). Instead, applications should leverage DOMPurify's native, declarative configuration blocklists and whitelists, such as FORBID_TAGS and FORBID_ATTR to direct the parser without breaking the traversal mechanics.

// Safe configuration bypasses custom node-removal hooks
DOMPurify.sanitize(dirtyElement, {
  FORBID_TAGS: ['div'],
  FORBID_ATTR: ['id'],
  IN_PLACE: true
});

Additionally, applications should restrict the use of { IN_PLACE: true } to scenarios where performance testing demonstrates a strict necessity. Operating on inert document contexts or handling inputs as raw HTML strings prior to injection provides strong defense-in-depth, neutralizing the event-loop race condition that enables this class of DOM XSS.

Official Patches

Cure53DOMPurify Official Repository and Release Log

Fix Analysis (1)

Technical Appendix

CVSS Score
8.1/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N

Affected Systems

Applications utilizing DOMPurify (npm package: dompurify) with both custom lifecycle hooks and the in-place sanitization configuration (IN_PLACE: true).

Affected Versions Detail

Product
Affected Versions
Fixed Version
dompurify
Cure53
< 3.4.163.4.16
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork (Client-Side DOM XSS)
CVSS Score8.1 (High)
Exploit StatusProof-of-Concept (PoC)
KEV StatusNot Listed
ImpactArbitrary Client-Side Code Execution

MITRE ATT&CK Mapping

T1059.007JavaScript
Execution
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

GitHubA fully reproducible proof-of-concept exists within the library's official test suite.

Vulnerability Timeline

Pull Request #1636 merged, addressing the issue
2026-09-23
Official release of DOMPurify 3.4.16 featuring the security patch
2026-09-23
GitHub Security Advisory published as GHSA-P98J-92PF-MC4P
2026-09-23

References & Sources

  • [1]DOMPurify GitHub Repository

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.

More Reports

•about 13 hours ago•CVE-2026-128087
3.7

CVE-2026-128087: Premature Key Exchange Callback Invocation Prior to Host Key Verification in russh

CVE-2026-128087 (GHSA-9264-jvgf-6w2f) is an incorrect behavior order (CWE-696) and improper authorization (CWE-863) vulnerability in the russh Rust SSH library, affecting versions 0.55.0 through 0.65.0. During the initial SSH key exchange (KEX) handshake, the client state machine invokes the client::Handler::kex_done() callback—providing the negotiated raw shared secret bytes—prior to validating the remote server's host key or certificate via check_server_key(). Consequently, an untrusted or rogue SSH server can complete cryptographic key agreement and force a connecting client to execute side-effecting application logic inside kex_done() before the server's identity is checked and rejected.

Amit Schendel
Amit Schendel
4 views•10 min read
•about 14 hours ago•CVE-2026-128082
7.5

CVE-2026-128082: Uncontrolled Recursion and Denial of Service in AutoMapper via Constructor Parameter Mapping

CVE-2026-128082 (GHSA-6vhm-j9h7-f9gh) is an uncontrolled recursion vulnerability (CWE-674) in AutoMapper, a convention-based object-object mapper for .NET. When mapping nested or self-referential types into destination models that rely on parameterized constructors, C# positional records, or recursive ConstructUsing expressions, AutoMapper evaluates child constructor arguments prior to incrementing the parent's recursion depth counter. This ordering flaw bypasses both explicit MaxDepth limits and the default depth limit of 64. An unauthenticated remote attacker can supply deeply nested input payloads to affected endpoints, exhausting the thread call stack and triggering an uncatchable System.StackOverflowException that terminates the hosting .NET process.

Amit Schendel
Amit Schendel
7 views•11 min read
•about 15 hours ago•CVE-2026-128080
5.8

CVE-2026-128080: Sensitive Query Parameter Disclosure on Incoming Server Spans in OpenTelemetry JavaScript (@opentelemetry/instrumentation-http)

An information exposure vulnerability in the @opentelemetry/instrumentation-http package for OpenTelemetry JavaScript versions 0.204.0 prior to 0.222.0 causes incoming HTTP server spans to record raw, unredacted query string parameters under the url.query attribute. While outgoing client spans applied built-in and user-configured query parameter redaction, incoming server spans bypassed this sanitization completely, resulting in the transmission of sensitive credentials such as pre-signed cloud URLs and API tokens to external observability backends.

Alon Barad
Alon Barad
6 views•10 min read
•about 16 hours ago•CVE-2026-128089
7.5

CVE-2026-128089: Uncontrolled Resource Consumption in dnspython WKS Record Parser

CVE-2026-128089 is an uncontrolled resource consumption vulnerability in the dnspython toolkit for Python, affecting all versions prior to 2.9.2. When parsing Well-Known Service (WKS) records from text via dns.rdtypes.IN.WKS.WKS.from_text(), the parser fails to validate that numeric service port identifiers fall within the RFC 1035 valid range of 0 through 65535. An attacker capable of supplying untrusted zone files or RDATA text strings containing an arbitrarily large service port integer can force the parser to allocate an unbounded bitmap array using an inefficient linear append loop, resulting in severe CPU exhaustion, application hangs, or Out-Of-Memory (OOM) process termination.

Amit Schendel
Amit Schendel
6 views•10 min read
•about 17 hours ago•CVE-2026-126347
3.1

CVE-2026-126347: Open Redirect via Triple-Slash Redirect Paths in Tornado RedirectHandler

CVE-2026-126347 (GHSA-5cjw-f85j-cv6r) is an open redirect vulnerability (CWE-601) in Tornado, an asynchronous Python networking library and web framework. The flaw resides in tornado.web.RedirectHandler when dynamic URL redirection templates incorporate unvalidated user input. Due to a parser differential between RFC 3986 (used by Python's urllib.parse.urlsplit) and the WHATWG URL Standard (used by modern web browsers), inputs containing three or more leading slashes bypass Tornado 6.6.0's validation logic, allowing unauthenticated attackers to redirect users to arbitrary external domains.

Alon Barad
Alon Barad
7 views•11 min read
•about 18 hours ago•CVE-2026-128123
5.9

CVE-2026-128123: Uncaught Exception and Denial of Service in SvelteKit @sveltejs/adapter-node

A Denial of Service (DoS) vulnerability exists in @sveltejs/adapter-node versions 5.5.7 and 6.0.0-next.0 through 6.0.0. When a SvelteKit server route returns a direct response from a fetch() call with a Content-Type of text/event-stream, the Node.js adapter attempts to mutate the response headers to disable proxy buffering. Because headers returned directly by the WHATWG Fetch API carry an immutable guard, this mutation throws an unhandled TypeError, terminating the underlying Node.js process and denying service to all active and subsequent clients.

Alon Barad
Alon Barad
5 views•9 min read