# xmldom: HTML raw-text closing-tag case mismatch causes output amplification

xmldom's HTML raw-text parser matched closing tags case-sensitively, so mixed-case `</ScRiPt>` caused quadratic output amplification and DoS.

- Payout: Research
- Severity: high
- Program: xmldom
- Found by: KarimTantawey
- Disclosed: 2026-09-08
- CVE: CVE-2026-83612
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-6mj3-qw4j-hgrw
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-6mj3-qw4j-hgrw
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:nodejs technique:parser-differential technique:source-review vuln:dos

**Target:** HTML parser (parseHtmlSpecialContent in lib/sax.js) of npm @xmldom/xmldom
**Root cause:** The raw-text closing tag is located with a case-sensitive `source.indexOf('</' + tagName + '>')`, so a mixed-case close tag returns -1; the unguarded `substring(elStartEnd + 1, -1)` then extracts text backwards from position 0 and the function returns -1, forcing character-by-character fallback that re-captures all preceding source per element.
**Steps:**
1. Parse untrusted HTML in HTML mode: `new DOMParser().parseFromString(payload, 'text/html')`
2. Craft payload with repeated raw-text elements whose close tag case differs from the open tag: `'<script>x</ScRiPt>'.repeat(n)` inside `<html><body>...</body></html>`
3. Serialize the result with `new XMLSerializer().serializeToString(doc)`
4. Observe quadratic growth: 18 KB input yields ~9 MB output (501x); 36 KB yields ~36 MB (1001x)
5. Repeat with `style`, `textarea`, or `title` for the same amplification
**How found:** Reporter noticed the raw-text close-tag search used a byte-for-byte case-sensitive `indexOf` contrary to the WHATWG RAWTEXT end-tag-name rule, then traced the -1 return through the unguarded substring and the parse loop's fallback to derive the O(n²) amplification; also noted 0.8.x shares the case-sensitive indexOf but lacks the amplification introduced in the 0.9.0-beta.1 refactor.
**Exploitation:** Attacker submits a tens-of-KB HTML document containing many case-mismatched raw-text elements to any service that parses untrusted text/html with xmldom and serializes the result; the parser re-captures preceding source per element, producing tens to hundreds of MB of output and exhausting heap or stalling the event loop. No auth, config, or error handler needed.
**Insight:** When a parser locates a terminator with a case-sensitive search but the spec mandates case-insensitive matching, check what happens on a -1 result: an unguarded substring with a negative end index silently extracts backwards from offset 0, and returning that sentinel to the parse loop can turn a single mismatch into per-element re-scanning and quadratic blowup.

