CVE-2026-83612 Overview
CVE-2026-83612 affects @xmldom/xmldom, a pure JavaScript W3C standard-based DOMParser and XMLSerializer module widely used across Node.js and browser applications. Versions from 0.9.0-beta.1 through 0.9.11 mishandle mixed-case closing tags for HTML raw-text elements including script, style, textarea, and title. When DOMParser.parseFromString() processes these elements in HTML mode, a case-sensitive search fails to locate the closing tag, triggering unstable parser progression and quadratic output amplification. A small untrusted text/html document can consume disproportionate CPU and memory during parsing and serialization, enabling a denial-of-service condition against any service that parses attacker-controlled HTML.
Critical Impact
A minimal untrusted HTML input can trigger quadratic CPU and memory amplification, exhausting resources on servers that parse attacker-supplied documents.
Affected Products
- @xmldom/xmldom version 0.9.0-beta.1 through 0.9.11
- Node.js and JavaScript applications embedding vulnerable xmldom releases
- Downstream libraries and frameworks depending on @xmldom/xmldom in HTML-mode parsing
Discovery Timeline
- 2026-09-01 - CVE-2026-83612 published to NVD
- 2026-09-01 - Last updated in NVD database
- Fix released - @xmldom/xmldom version 0.9.12 published with the patch
Technical Details for CVE-2026-83612
Vulnerability Analysis
The defect resides in parseHtmlSpecialContent inside lib/sax.js. When the parser encounters an HTML raw-text or escapable raw-text element, it locates the matching end tag using source.indexOf('</' + tagName + '>', elStartEnd). This lookup is case-sensitive, but the WHATWG HTML specification (§13.2.5.14 RAWTEXT end tag name state) requires case-insensitive matching for closing tags. When a mixed-case closer such as </Script> follows a lowercase <script> opener, indexOf() returns -1.
The returned -1 is then passed directly to source.substring(elStartEnd + 1, -1). JavaScript treats a negative end index as 0, so the call silently produces a backward slice interpreted as an empty or reversed range. The parser fails to advance correctly and re-emits accumulated source content on subsequent iterations. Repeated raw-text elements amplify the output quadratically relative to input size. This behavior maps to [CWE-178: Improper Handling of Case Sensitivity].
Root Cause
The root cause is a case-sensitivity mismatch between the parser implementation and the HTML specification, compounded by unchecked use of the indexOf() sentinel value. The code does not validate that a closing tag was actually found before slicing, so a -1 return propagates into substring() and drives runaway output growth during serialization.
Attack Vector
Exploitation requires only that an application pass attacker-controlled HTML into DOMParser.parseFromString() in HTML mode. No authentication, user interaction, or privileged access is required. A remote attacker submits a compact HTML payload containing raw-text elements terminated with mixed-case closers such as </SCRIPT>, </Style>, or </TeXtArEa>. Parsing and serialization then consume CPU and memory at a rate quadratic in the number of affected elements, producing a resource-exhaustion denial of service.
// Security patch in lib/sax.js from @xmldom/xmldom 0.9.12
// TODO: https://html.spec.whatwg.org/#cdata-rcdata-restrictions
var isEscapableRaw = isHTMLEscapableRawTextElement(tagName);
if (isEscapableRaw || isHTMLRawTextElement(tagName)) {
// The closing tag of a raw-text element matches case-insensitively
// (WHATWG HTML §13.2.5.14 RAWTEXT end tag name state). A case-sensitive
// search that missed the closing tag would return -1 and the `substring`
// below would treat -1 as a backward slice from position 0, re-emitting all
// prior source and amplifying output quadratically across repeated elements.
// The regex is anchored to `elStartEnd` via `lastIndex` so it scans forward
// only (O(distance), not the whole source).
var closeTag = new RegExp('</' + tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '>', 'ig');
closeTag.lastIndex = elStartEnd;
var match = closeTag.exec(source);
var elEndStart = match ? match.index : -1;
if (elEndStart < 0) {
// No closing tag: never slice with a -1 end index. Leave the element to
// the parse loop's normal recovery instead of back-capturing the source.
return elStartEnd + 1;
}
var text = source.substring(elStartEnd + 1, elEndStart);
if (isEscapableRaw) {
Source: GitHub Commit 7ced40c
Detection Methods for CVE-2026-83612
Indicators of Compromise
- Sustained CPU saturation in Node.js processes correlated with inbound HTTP requests carrying text/html payloads.
- Rapid heap growth or out-of-memory terminations in services invoking DOMParser.parseFromString().
- Request bodies containing raw-text elements (<script>, <style>, <textarea>, <title>) closed with mixed-case tags such as </Script> or </TITLE>.
Detection Strategies
- Perform software composition analysis to enumerate applications resolving @xmldom/xmldom at versions 0.9.0-beta.1 through 0.9.11.
- Instrument HTML parsing paths with per-request CPU and wall-clock budgets to surface anomalous parse durations.
- Alert on repeated occurrences of mixed-case raw-text closing tags in inbound HTML bodies at the WAF or reverse proxy.
Monitoring Recommendations
- Track event loop lag and worker thread stalls on services that accept user-submitted HTML.
- Correlate application memory pressure with request identifiers to isolate malicious payloads.
- Log and rate-limit endpoints that invoke DOM parsing on untrusted input.
How to Mitigate CVE-2026-83612
Immediate Actions Required
- Upgrade @xmldom/xmldom to version 0.9.12 or later across all applications and transitive dependencies.
- Audit dependency lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) to confirm no vulnerable versions remain resolved.
- Rebuild and redeploy container images and serverless bundles that embed the vulnerable package.
Patch Information
The issue is fixed in @xmldom/xmldom version 0.9.12. The patch replaces the case-sensitive indexOf() lookup with a case-insensitive regular expression anchored to the current parse position and returns early when no closing tag is found, preventing the negative-index substring() slice. See the GitHub Security Advisory GHSA-6mj3-qw4j-hgrw, Pull Request #1071, and Release v0.9.12 notes.
Workarounds
- Restrict or sanitize untrusted HTML before it reaches DOMParser.parseFromString(), stripping raw-text elements when feasible.
- Enforce request body size limits and parse timeouts on any endpoint that accepts HTML input.
- Isolate HTML parsing in a worker with strict CPU and memory quotas to contain resource exhaustion.
# Upgrade the vulnerable package to the patched release
npm install @xmldom/xmldom@0.9.12
# Verify no vulnerable versions remain in the dependency tree
npm ls @xmldom/xmldom
# For yarn or pnpm environments
yarn up @xmldom/xmldom@0.9.12
pnpm update @xmldom/xmldom@0.9.12
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

