Sep 8, 2026·6 min read·51 visits
Unauthenticated Denial of Service via quadratic output amplification triggered by parsing HTML with case-mismatched closing raw-text tags in xmldom.
A Denial of Service (DoS) vulnerability exists in the xmldom library when parsing HTML-mode documents with mixed-case closing tags for raw-text or escapable raw-text elements like script, style, textarea, or title. This leads to algorithmic complexity issues and quadratic output amplification during DOM serialization.
The @xmldom/xmldom package is a pure JavaScript implementation of the W3C standard DOMParser and XMLSerializer specifications. It is widely used in server-side Node.js environments to parse, manipulate, and serialize XML and HTML documents. Because it runs in server-side applications, any vulnerabilities in its parsing logic can expose the host application to remote attacks.
A denial of service vulnerability, tracked as CVE-2026-83612, arises when the parser processes HTML documents containing specific raw-text or escapable raw-text elements. These elements, which include <script>, <style>, <textarea>, and <title>, are parsed in a dedicated mode designed to read content without evaluating sub-elements. The attack surface is exposed directly through the DOMParser.parseFromString() method when called with the text/html mime type.
An attacker can exploit this vulnerability by submitting a crafted HTML document that contains mismatched casing in the closing tags of these elements. This leads to a severe degradation of performance, causing uncontrolled resource consumption. The flaw affects all versions of the library starting from 0.9.0-beta.1 up to, but not including, 0.9.12.
The root cause of the vulnerability lies in a mismatch between the implementation of xmldom and the WHATWG HTML Specification regarding raw-text elements. The specification states that the closing tag of raw-text and escapable raw-text elements must be matched in a case-insensitive manner. Therefore, tags like </SCRIPT> or </ScRiPt> are valid closing markers for an opening <script> tag.
In vulnerable versions of xmldom, the internal helper function parseHtmlSpecialContent (located in lib/sax.js) attempted to find the end of raw-text elements using a case-sensitive search. It implemented this search via the source.indexOf('</' + tagName + '>', elStartEnd) statement. Because the variable tagName preserved the casing of the opening tag (typically lowercase), the parser failed to find closing tags that utilized mixed-case or uppercase characters, resulting in an index value of -1.
The parsing logic then used this return value directly within the String.prototype.substring() method to slice out the content of the element. In JavaScript, when substring(start, end) receives a negative value for the end parameter, it normalizes that parameter to 0. Furthermore, if the start parameter is greater than the end parameter, the method silently swaps the two arguments.
Consequently, the execution of source.substring(elStartEnd + 1, -1) resolved to source.substring(0, elStartEnd + 1). This behavior caused the parser to extract the entire document from its starting offset up to the opening of the current element, rather than returning an empty string or gracefully handling the missing tag. When serialized, this leading document portion was re-emitted, leading to a quadratic growth of the output size over repeated elements.
The vulnerability is localized within the file lib/sax.js inside the parseHtmlSpecialContent function. Below is an analysis of the vulnerable implementation compared directly against the patched code.
// In vulnerable versions of lib/sax.js
function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
var isEscapableRaw = isHTMLEscapableRawTextElement(tagName);
if (isEscapableRaw || isHTMLRawTextElement(tagName)) {
// Vulnerable case-sensitive search
var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
// If elEndStart is -1, substring(elStartEnd + 1, -1) swaps arguments
// and returns source.substring(0, elStartEnd + 1)
var text = source.substring(elStartEnd + 1, elEndStart);
// Text contains the entire document from index 0
}
}// In patched version 0.9.12
function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
var isEscapableRaw = isHTMLEscapableRawTextElement(tagName);
if (isEscapableRaw || isHTMLRawTextElement(tagName)) {
// Escape special regex characters in the tag name
var escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Perform case-insensitive search using a safe regular expression
var closeTag = new RegExp('</' + escapedTagName + '>', 'ig');
closeTag.lastIndex = elStartEnd;
var match = closeTag.exec(source);
var elEndStart = match ? match.index : -1;
// Introduce an explicit boundary guard to prevent backward slicing
if (elEndStart < 0) {
// Safely exit and delegate recovery to the main parser loop
return elStartEnd + 1;
}
var text = source.substring(elStartEnd + 1, elEndStart);
}
}By replacing the case-sensitive string search with a case-insensitive regular expression, the patch aligns xmldom with the WHATWG specification. The introduction of the boundary guard if (elEndStart < 0) ensures that if no closing tag is found, the function exits immediately and prevents any dangerous parameter swapping in the substring call.
An attacker can exploit this vulnerability if they have the ability to supply arbitrary HTML input to an application that processes it using xmldom. The exploit payload utilizes multiple raw-text elements that contain mismatched closing tags to trigger the quadratic amplification mechanism.
Consider the following raw-text bomb payload:
<html>
<body>
<script>x</ScRiPt>
<script>x</ScRiPt>
<script>x</ScRiPt>
</body>
</html>When the parser processes this markup, the first <script> tag fails to find its closing tag </ScRiPt> due to the case mismatch. The parser back-captures everything from the beginning of the document up to the end of the opening tag. The second <script> tag does the same, capturing the preceding markup which now includes the amplified contents of the first tag.
As the number of elements increases, the size of the parsed text buffer grows quadratically ($O(n^2)$). When the application calls XMLSerializer.serializeToString(), this massive internal buffer is written back to the output string. A payload of 1,000 repeating script blocks can result in an output string that is several hundred times larger than the input, locking the single-threaded Node.js event loop and exhausting system memory.
The concrete impact of CVE-2026-83612 is a severe Denial of Service (DoS) condition on the hosting application. Because Node.js operates on a single-threaded event loop, any operation that consumes excessive CPU cycles or causes heavy garbage collection will block all concurrent requests.
This vulnerability has been assigned a CVSS v4.0 score of 8.7, indicating high severity. The attack vector is Network (AV:N), the complexity is Low (AC:L), and no privileges are required (PR:N). There is no impact on confidentiality or integrity, but the impact on availability is High (VA:H).
If an application parses user-generated HTML in real-time (such as in a rich-text comment parser, an email processing system, or an HTML sanitizer), a remote unauthenticated attacker can submit a relatively small payload (under 50 KB) that exhausts the server's memory, eventually crashing the Node.js process due to Out-of-Memory (OOM) errors.
Remediation requires upgrading the @xmldom/xmldom package to version 0.9.12 or above. This release addresses the root parsing issue and prevents the quadratic amplification loop.
If the library is a transitive dependency (introduced by another package), you can force its resolution using your package manager's override capabilities. For npm, add the following to your package.json:
{
"overrides": {
"@xmldom/xmldom": "^0.9.12"
}
}If an immediate upgrade is not possible, applications should implement input size limits on incoming HTML payloads or sanitize tag names using a pre-parser that standardizes all closing tag casing to lowercase before handing the document to xmldom.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@xmldom/xmldom xmldom | >= 0.9.0-beta.1 < 0.9.12 | 0.9.12 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-178 / CWE-400 |
| Attack Vector | Network (Unauthenticated) |
| CVSS Score | 8.7 |
| EPSS Score | 0.00301 (22.45th percentile) |
| Impact | Denial of Service (CPU & Memory exhaustion) |
| Exploit Status | Proof-of-Concept Available |
| KEV Status | Not Listed |
The software does not properly handle case sensitivity when parsing raw-text HTML tags, causing a fallback logic error that results in resource exhaustion.
A local race condition and mutual exclusion bypass vulnerability exists in filelock versions 4.1.0 through 4.2.0. On Linux filesystems with rapid inode reuse, a local attacker can manipulate modification timestamps and lock files to cause waiting processes to falsely evict active soft lock markers.
vLLM prior to version 0.31.0 contains an uncontrolled resource consumption vulnerability (CWE-400) in its HTTP API request handling logic. Unauthenticated remote attackers can transmit per-request keyword arguments (`mm_processor_kwargs` and `media_io_kwargs`) that are passed directly into internal multimodal preprocessing pipelines, causing CPU/GPU memory exhaustion and crashing the engine process.
An insufficient verification of data authenticity vulnerability in SiYuan prior to version 3.7.4 allows an attacker to overwrite arbitrary installed marketplace packages (plugins, widgets, themes, and templates). By providing a mismatched package name and package archive repository URL during a Bazaar installation request, arbitrary file write into trusted plugin directories can be achieved, enabling persistence and execution across application restarts.
In obsidian-tc versions 1.0.1 through 1.31.3, memory lifecycle tools fail to validate note ownership prior to rematerializing Markdown files and execute non-atomic SQLite database mutations. An authorized caller can overwrite arbitrary note bodies on disk and cause persistent desynchronization between the database memory graph and physical vault files.
A high-severity authorization bypass vulnerability in obsidian-tc allows clients to bypass Human-In-The-Loop (HITL) confirmation prompts over Streamable HTTP deployments. Due to incomplete validation of returned prompt responses, an attacker echoing a signed requestState token can force execution of destructive vault operations even if a human user declines or cancels the prompt.
A Denial of Service (DoS) vulnerability in pypdf versions prior to 6.21.0 allows remote attackers to cause high CPU and memory resource exhaustion by supplying PDF files containing oversized or deeply nested arrays, dictionaries, or content streams.