{"id":"GHSA-x4fp-j954-r2f4","summary":"xmldom: End-tag Whitespace-Trim Regex ReDoS — quadratic backtracking in the 0.8.x end-tag parser","details":"## Summary\n\nOn the `@xmldom/xmldom` **`0.8.x`** line, parsing an XML end tag whose name is followed by a long run\nof whitespace and then a non-whitespace character triggers quadratic-time regular-expression\nbacktracking (ReDoS), so a single small crafted end tag stalls the Node.js event loop. It is reachable\nfrom `DOMParser.parseFromString` under **default options**, unauthenticated, before any validity\ncheck — an availability-only denial of service. The `0.9.x` line is **not** affected.\n\n## Details\n\n`lib/sax.js` (release-0.8.x, commit `e5c1480`) trims trailing whitespace from a captured end-tag name\nwith an unanchored global regex:\n\n- `lib/sax.js` line 120: https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/sax.js#L120\n\n```js\n/[ \\t\\n\\r]+$/g\n```\n\nApplied to a string shaped `whitespace-run + one non-whitespace char` (e.g. the content of an end tag\n`\u003c/   …   x\u003e`), the engine must, for every starting position, extend `[ws]+` to the end and then fail\nthe `$` anchor when the trailing non-whitespace char is present — classic O(n²) backtracking in the\nlength of the whitespace run. The trimmed substring is delimited only by `indexOf('\u003e')`, so the\nattacker controls its length directly.\n\n## Proof of Concept\n\n```js\nconst { DOMParser } = require('@xmldom/xmldom'); // 0.8.x\nconst n = 64 * 1024;\nconst payload = '\u003cr\u003e\u003c/' + ' '.repeat(n) + 'x\u003e';\nconsole.time('parse');\nnew DOMParser().parseFromString(payload, 'text/xml');\nconsole.timeEnd('parse');\n```\n\nMeasured (Node 18) — time quadruples per doubling of the whitespace run (canonical O(n²)):\n\n| Whitespace run | Isolated regex | End-to-end `parseFromString` (0.8.13) |\n|---|---|---|\n|  4 KB | 5.6 ms   | 5.7 ms   |\n|  8 KB | 22.7 ms  | 22.5 ms  |\n| 16 KB | 88.6 ms  | 92 ms    |\n| 32 KB | 354 ms   | 361 ms   |\n| 64 KB | 1434 ms  | 1452 ms  |\n| 128 KB | 5761 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.4 s at 64 KB; multi-second with larger inputs). No memory\nblow-up, no data exposure, no integrity impact. Because XML is routinely accepted from untrusted\nsources and parsed with default options, one request can stall a server.\n\n## Affected Versions\n\nAffected on the `0.7.x` and `0.8.x` lines (the trailing-whitespace trim was added in `0.7.0`, present\nthrough `0.8.14`); the fix targets the `0.8.x` LTS patch. The `0.9.x` line rewrote end-tag parsing to\nan anchored linear matcher and never had this regex, so it is **not** affected. No published unscoped\n`xmldom` is affected — the vulnerable code exists only in a `0.7.0` git tag that was never released to\nnpm (`npm view xmldom` → `latest` = 0.6.0).\n\n## Fix Applied\n\nAnchors the end-tag trailing-whitespace trim so it runs in linear time instead of\nbacktracking quadratically on a long whitespace run. Byte-identical output. Non-breaking; 0.8.x-only.\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.","aliases":["CVE-2026-83619"],"modified":"2026-09-08T21:15:04.998562447Z","published":"2026-09-08T21:01:09Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-09-08T21:01:09Z","nvd_published_at":"2026-09-01T15:17:40Z","cwe_ids":["CWE-1333","CWE-400"],"severity":"HIGH"},"references":[{"type":"WEB","url":"https://github.com/xmldom/xmldom/security/advisories/GHSA-x4fp-j954-r2f4"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-83619"},{"type":"WEB","url":"https://github.com/xmldom/xmldom/pull/1072"},{"type":"WEB","url":"https://github.com/xmldom/xmldom/commit/3abb0934f5a8a84d83a1f9cde0f2bd04c08b2a09"},{"type":"PACKAGE","url":"https://github.com/xmldom/xmldom"},{"type":"WEB","url":"https://github.com/xmldom/xmldom/releases/tag/0.8.15"}],"affected":[{"package":{"name":"@xmldom/xmldom","ecosystem":"npm","purl":"pkg:npm/%40xmldom/xmldom"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0.7.0"},{"fixed":"0.8.15"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-x4fp-j954-r2f4/GHSA-x4fp-j954-r2f4.json","last_known_affected_version_range":"\u003c= 0.8.14"}}],"schema_version":"1.9.0","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"}]}