CVE-2026-83606: xmldom PI grammar regex ReDoS Quadratic Backtracking On Unterminated Processing Vulnerability
HIGHCVE-2026-83606 is a HIGH quadratic backtracking on unterminated processing vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting xmldom PI grammar regex ReDoS libraries in the npm ecosystem. The issue involves quadratic backtracking on unterminated processing instructions. Remediation is available by upgrading affected packages to version 0.9.11.
CVE-2026-83606 at a Glance
How to Fix CVE-2026-83606
To remediate this vulnerability, upgrade all affected xmldom PI grammar regex ReDoS dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @xmldom/xmldom@0.9.11
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @xmldom/xmldom
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-83606, inspect your project dependencies using npm:
npm ls @xmldom/xmldom
Analyze the terminal output to locate any version of xmldom PI grammar regex ReDoS in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.9.11), your application is affected.
Note: The npm ls @xmldom/xmldom command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. In xmldom PI grammar regex ReDoS, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-83606 Affected xmldom PI grammar regex ReDoS Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@xmldom/xmldom
|
npm |
≥ 0.9.0-beta.9
< 0.9.11
|
0.9.11
|
References
Frequently Asked Questions
What is CVE-2026-83606?
Which xmldom PI grammar regex ReDoS versions are affected?
What version fixes CVE-2026-83606?
Is @xmldom/xmldom affected?
What CWE is associated with CVE-2026-83606?
How can I check npm dependencies for CVE-2026-83606?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-g53g-w8rj-fmg7",
"modified" : "2026-09-08T20:31:50Z",
"published" : "2026-09-08T20:31:50Z",
"aliases" : [ "CVE-2026-83606" ],
"summary" : "xmldom PI grammar regex ReDoS: quadratic backtracking on unterminated processing instructions",
"details" : "## Summary\n\n`@xmldom/xmldom`'s processing-instruction (PI) grammar regex exhibits quadratic-time backtracking\n(ReDoS) when parsing an **unterminated** processing instruction. A single small XML document\ncontaining `<?` + a target + a long run of whitespace and no closing `?>` forces the regular\nexpression engine into O(n²) work, stalling the Node.js event loop. The input is parsed with\n`DOMParser.parseFromString` under **default options**, so it is reachable from unauthenticated,\nnetwork-delivered XML (SOAP/SAML, webhooks, uploads, XML APIs).\n\n## Details\n\nThe PI production in `lib/grammar.js` compiles (flags `mu`) to:\n\n```\n^<\\?(NameChars)(?:[\\x20\\x09\\x0D\\x0A]+([Char]*?))?\\?>\n ^^^ S+ greedy ^^^ Char*? lazy\n```\n\n- `lib/grammar.js` line 261: https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/grammar.js#L261\n\nIn the optional tail `(?:S+(Char*?))?`, both the greedy separator `S+` and the lazy data `Char*?`\nmatch XML whitespace. When the required trailing `?>` is absent, the engine must ultimately fail —\nbut first it tries every partition of the whitespace run between `S+` and `Char*?`, which is O(n²)\nin the length of the trailing whitespace.\n\nThe regex is executed against the **entire remaining source string** in two places in `lib/sax.js`,\nso the whole whitespace tail is scanned:\n\n- `parsePI` — https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/sax.js#L680-L691\n- `parseProcessingInstruction` — https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/sax.js#L862-L879\n\n## Affected Versions\n\nOnly the `0.9.x` line is affected. `lib/grammar.js` (and this PI regex) was introduced in\ncommit `726b471` (\"fix!: preserve DOCTYPE internal subset (#498)\"), first released in\n**0.9.0-beta.9**, and is unchanged through **0.9.10**.\n\nThe `0.8.x` line (≤ 0.8.13) and the unscoped `xmldom` package (≤ 0.6.0) parse PIs via a different\ncode path bounded by `indexOf('?>')` — they do **not** contain this regex and are **not affected**\nby this issue. (They were not separately tested for a *different* PI ReDoS; the scope here is the\nspecific `grammar.js` regex.)\n\n| Line | PI code path | Affected? |\n|---|---|---|\n| `0.9.x` (0.9.0-beta.9 … 0.9.10) | `grammar.js` `PI` regex over full remaining source | **Yes** |\n| `0.8.x` (≤ 0.8.13) | `parseInstruction`, bounded by `indexOf('?>')` | No |\n| unscoped `xmldom` (≤ 0.6.0) | older `indexOf('?>')`-bounded parsing | No |\n\n## Proof of Concept\n\n```js\nconst { DOMParser } = require('@xmldom/xmldom');\nconst n = 32 * 1024;\nconst payload = '<a><?p' + ' '.repeat(n); // unterminated PI, no `?>`\nconsole.time('parse');\nnew DOMParser().parseFromString(payload, 'text/xml');\nconsole.timeEnd('parse');\n```\n\nMeasured (Node 18), trailing whitespace after `<?p`, no `?>` — time quadruples per doubling of\ninput length (canonical O(n²)):\n\n| Trailing whitespace | `g.PI.exec` | `parseFromString` |\n|---|---|---|\n| 2 KB | 4.4 ms | 5.1 ms |\n| 4 KB | 16.9 ms | 17.0 ms |\n| 8 KB | 111.4 ms | 66.3 ms |\n| 16 KB | 263.8 ms | 336.5 ms |\n| 32 KB | 1073.1 ms | — |\n\n## Impact\n\nAvailability only: a single parse of a small crafted document blocks the Node.js event loop for the\nduration of the quadratic scan (≈1 s at 32 KB; multi-second with larger inputs). No memory blow-up,\nno data exposure, no integrity impact. Because XML is routinely accepted from untrusted sources and\nparsed with default options, one request can stall a server.\n\n## Fix Applied\n\nFixed in `@xmldom/xmldom` **0.9.11** (`0.9.x`-only; the `0.8.x` LTS line and the\nunscoped `xmldom` package use a different, bounded PI code path and are not affected).\n\nPR [#1039](https://github.com/xmldom/xmldom/pull/1039) inserts a fixed-width negative lookahead\n`(?!\\s)` immediately after the greedy `S+`, so the separator can no longer hand whitespace back to\nthe lazy data group:\n\n```\n- var PI = reg(/^<\\?/, '(', Name, ')', regg(S, '(', Char, '*?)'), '?', /\\?>/);\n+ var PI = reg(/^<\\?/, '(', Name, ')', regg(S, '(?!', _SChar, ')(', Char, '*?)'), '?', /\\?>/);\n```\n\nThe change is correct, minimal, and behavior-preserving: it produces identical `[target, data]`\ncaptures on all valid PIs tested (incl. whitespace-heavy, tab/newline, empty-data, and xml-decl\ncases) and removes the backtracking blow-up (linear, ~0.4 ms at 128 KB after the fix). The lookahead\nis fixed-width and cannot itself backtrack — a strict improvement with no new parsing risk.\n\n## Severity note\n\nThe complexity is **quadratic**, not exponential, so a multi-second stall requires\ntens-to-hundreds of KB of input. `VA:H` reflects that xmldom applies **no input-size limit** and the\npath runs on default-options parsing, so a single unbounded parse can fully stall the event loop.",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "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"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@xmldom/xmldom"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.9.0-beta.9"
}, {
"fixed" : "0.9.11"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.9.10"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/xmldom/xmldom/security/advisories/GHSA-g53g-w8rj-fmg7"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-83606"
}, {
"type" : "WEB",
"url" : "https://github.com/xmldom/xmldom/pull/1039"
}, {
"type" : "WEB",
"url" : "https://github.com/xmldom/xmldom/commit/73df6b8bdbd86f904b9e8c3ab9c49aa54ef2802e"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/xmldom/xmldom"
}, {
"type" : "WEB",
"url" : "https://github.com/xmldom/xmldom/releases/tag/0.9.11"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-1333", "CWE-400" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-09-08T20:31:50Z",
"nvd_published_at" : "2026-09-01T15:17:38Z"
}
}