{"id":"CVE-2026-83618","aliases":["CLSA-2026-1790608669","DEBIAN-CVE-2026-83618","EUVD-2026-69244","GHSA-vr34-hp96-76pp","ROOT-APP-NPM-CVE-2026-83618","UBUNTU-CVE-2026-83618"],"status":"published","title":"xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","severity":{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.0057,"percentile":0.47669,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-625","CWE-91"],"published":"2026-09-01T14:49:46Z","modified":"2026-10-28T02:30:54Z","fixes":[{"ecosystem":"npm","package":"@xmldom/xmldom","fixed_in":["0.9.12"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"node-xmldom","fixed_in":["0.9.12-1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@xmldom/xmldom","fixed_in":["0.9.11-aikido.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/xmldom__xmldom","fixed_in":["0.9.11-root.io.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.10"},{"last_affected":"0.9.11"},{"fixed":"0.9.12"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.10-tuxcare.0"},{"fixed":"0.9.10-tuxcare.1"}]}],"versions":["0.9.10"],"bundled":9},{"source":"osv","ecosystem":"Debian:12","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.6-1","0.9.10-1","0.9.10-2","0.9.11-1","0.9.12-1","0.9.5-1","0.9.6-1","0.9.8-1","0.9.8-2","0.9.9-1"]},{"source":"osv","ecosystem":"Debian:13","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.10-1","0.9.10-2","0.9.11-1","0.9.12-1","0.9.6-1","0.9.8-1","0.9.8-2","0.9.9-1"]},{"source":"osv","ecosystem":"Debian:14","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.9.12-1"}]}],"versions":["0.9.10-1","0.9.10-2","0.9.11-1","0.9.6-1","0.9.8-1","0.9.8-2","0.9.9-1"]},{"source":"osv","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0.9.10"},{"fixed":"0.9.12"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.9.11-aikido.1"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/xmldom__xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.9.11-root.io.1"}]}]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.1.27+ds-1","0.1.27+ds-1+deb10u2build0.20.04.1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.5.0-1","0.7.3-1","0.7.5-1","0.7.5-1ubuntu0.22.04.1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.8.6-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.8-2","0.9.10-2","0.9.11-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.6-1","0.9.8-1","0.9.8-2"]}],"products":[{"vendor":"@xmldom","product":"xmldom"},{"vendor":"xmldom","product":"xmldom"}],"references":[{"url":"https://github.com/xmldom/xmldom/security/advisories/GHSA-vr34-hp96-76pp","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/xmldom/xmldom/pull/1071","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/xmldom/xmldom/commit/7b2ec67e1750daadd0bb06c92e875e726544a362","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/xmldom/xmldom/releases/tag/0.9.12","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-83618","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-vr34-hp96-76pp"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/83xxx/CVE-2026-83618.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-83618","tags":["advisory"]},{"url":"https://github.com/xmldom/xmldom","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-83618","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-83618","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-83618","title":"xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T14:49:46.201Z","modified":"2026-09-04T01:59:23.36Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-91","CWE-625"],"updated_at":"2026-11-02T13:44:51.734189Z"}],"euvd":[{"id":"EUVD-2026-69244","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T14:49:46Z","modified":"2026-09-04T01:59:23Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T18:01:13.160542Z"}],"ghsa":[{"id":"GHSA-vr34-hp96-76pp","title":"xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator","description":"## Summary\n\nAn embedded line terminator bypasses the `requireWellFormed` serializer check for a `DocumentType`'s\npublicId and systemId. The check was added to fix GHSA-f6ww-3ggp-fr8h; an id whose first line is a\nvalid literal slips past it and is emitted verbatim into the `\u003c!DOCTYPE …\u003e` declaration, so the markup\nafter the line terminator breaks out into the surrounding document. Callers who enabled\n`requireWellFormed` to neutralize DocumentType injection remain exposed.\n\n## Details\n\n`publicId` and `systemId` are stored as raw values **including their surrounding quotes**, and the\n`PubidLiteral`/`SystemLiteral` productions include those quotes. The serializer validates them with\n`g.PubidLiteral_match.test(publicId)` and `g.SystemLiteral_match.test(systemId)`, where both matchers\nare `reg('^', …, '$')` and inherit the `m` flag from xmldom's shared regexp builder. Under `m`, `$`\nmatches at an interior line terminator, so a value such as `\"valid pubid\"\\n\"\u003e\u003c!ENTITY …\u003e` satisfies\nthe matcher on its first line (`\"valid pubid\"` is a complete `PubidLiteral`) and the whole value —\nincluding the post-newline breakout — is emitted after `PUBLIC`/`SYSTEM`.\n\n### Root Cause\n\n1. A shared regexp builder compiles anchored productions with the `m` flag.\n2. `^…$` under `m` are line anchors, not string anchors.\n3. A full-string validator built on such a production (`.test()`) accepts any string with one\n   conforming line, so a complete, valid literal on the first line passes even though a line terminator\n   and breakout markup follow. `PubidChar` excluding `\u003c`/`\u003e` does not prevent it — the breakout is\n   appended *after* the literal, not embedded inside it.\n\nThe triggering line terminators are the ECMAScript `LineTerminator` set: U+000A, U+000D, U+2028, U+2029.\n\n## Affected Versions\n\nOnly `@xmldom/xmldom` 0.9.x is affected. The vulnerable matchers are built by `lib/grammar.js`'s\n`m`-flagged `reg()` builder, and the DocType `publicId`/`systemId` `requireWellFormed` check that\nconsumes them was introduced in 0.9.10 (the GHSA-f6ww-3ggp-fr8h fix); 0.9.10 and 0.9.11 carry it.\n`0.8.x` performs the same `requireWellFormed` check with inline, non-`m` regular expressions and is not\naffected. The unscoped `xmldom` package has no `grammar.js` and no `requireWellFormed` serializer, so\nthere is no check to bypass.\n\n## Proof of Concept\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\nconst impl = new DOMImplementation();\n\n// publicId: complete literal on line 1, then newline + breakout\nconst dt = impl.createDocumentType('html', '\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e', '');\nconst doc = impl.createDocument(null, 'root', dt);\nconsole.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));\n// Observed (no throw):\n//   \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n//   \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n// Expected: InvalidStateError (publicId is not a valid PubidLiteral).\n// Control: a single-line invalid publicId (\"no-surrounding-quotes\u003c\u003e\") DOES throw InvalidStateError,\n// confirming the check is active and specifically bypassed by the line terminator.\n```\n\n## Impact\n\n- **Bypass of the GHSA-f6ww-3ggp-fr8h mitigation.** Applications that adopted `requireWellFormed:\n  true` to neutralize DocumentType injection remain exposed.\n- **XML structure injection into the DOCTYPE**, including injected markup / entity declarations after\n  the public or system identifier.\n\n## Fix Applied\n\nThe anchored `PubidLiteral`/`SystemLiteral` validators used by the `requireWellFormed`\nserializer no longer treat an interior line terminator as satisfying the `$` anchor, so a `publicId`\nor `systemId` containing any ECMAScript `LineTerminator` (U+000A, U+000D, U+2028, U+2029) is rejected\nwith `InvalidStateError`. Valid single-line identifiers serialize unchanged, and the default\nserialization path is unaffected.\n\n\u003e **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain vulnerable\n\u003e unless `{ requireWellFormed: true }` is explicitly passed. Applications that serialize untrusted DOM\n\u003e content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\nconst impl = new DOMImplementation();\nconst dt = impl.createDocumentType('html', '\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e', '');\nconst doc = impl.createDocument(null, 'root', dt);\n\n// Default path (requireWellFormed off) — unchanged, still emits verbatim:\nconsole.log(new XMLSerializer().serializeToString(doc));\n//   \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n//   \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n\n// Opt-in path — now throws instead of emitting the breakout:\nnew XMLSerializer().serializeToString(doc, { requireWellFormed: true });\n//   InvalidStateError: DocumentType publicId is not a valid PubidLiteral\n```\n\n### Why the default stays verbatim\n\nThe W3C DOM Parsing \"require well-formed\" flag defaults to false, and a browser `XMLSerializer` emits\nthe DOCTYPE verbatim. Unconditionally throwing on a malformed `publicId`/`systemId` would be an\nunjustified breaking change to the default path, so the fix tightens only the opt-in\n`requireWellFormed` validator, matching browser and spec defaults.\n\n### Residual limitation\n\nThe guarantee holds only for callers that pass `{ requireWellFormed: true }`; the default\nserialization path still emits `publicId`/`systemId` verbatim. `publicId` and `systemId` are not\nvalidated at creation (`createDocumentType`) or on direct property assignment\n(`documentType.publicId = …`) — the WHATWG DOM specification places no well-formedness constraint on\nthese fields at creation time, so the serializer is the spec-aligned enforcement point.","status":"published","published":"2026-09-08T21:02:25Z","modified":"2026-09-08T21:02:26Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-91","CWE-625"],"updated_at":"2026-11-02T14:41:14.764963Z"}],"nvd":[{"id":"CVE-2026-83618","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T15:17:40.48Z","modified":"2026-09-08T21:11:31.703Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"}],"cwes":["CWE-91","CWE-625"],"updated_at":"2026-11-02T14:12:43.04945Z"}],"osv":[{"id":"CVE-2026-83618","title":"xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T14:49:46.201Z","modified":"2026-10-28T02:30:54.312842458Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-625","CWE-91"],"updated_at":"2026-11-02T15:45:18.482949Z"},{"id":"DEBIAN-CVE-2026-83618","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T15:17:40.48Z","modified":"2026-10-01T03:03:43.202127978Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high"}],"updated_at":"2026-11-02T15:46:04.577397Z"},{"id":"GHSA-vr34-hp96-76pp","title":"xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator","description":"## Summary\n\nAn embedded line terminator bypasses the `requireWellFormed` serializer check for a `DocumentType`'s\npublicId and systemId. The check was added to fix GHSA-f6ww-3ggp-fr8h; an id whose first line is a\nvalid literal slips past it and is emitted verbatim into the `\u003c!DOCTYPE …\u003e` declaration, so the markup\nafter the line terminator breaks out into the surrounding document. Callers who enabled\n`requireWellFormed` to neutralize DocumentType injection remain exposed.\n\n## Details\n\n`publicId` and `systemId` are stored as raw values **including their surrounding quotes**, and the\n`PubidLiteral`/`SystemLiteral` productions include those quotes. The serializer validates them with\n`g.PubidLiteral_match.test(publicId)` and `g.SystemLiteral_match.test(systemId)`, where both matchers\nare `reg('^', …, '$')` and inherit the `m` flag from xmldom's shared regexp builder. Under `m`, `$`\nmatches at an interior line terminator, so a value such as `\"valid pubid\"\\n\"\u003e\u003c!ENTITY …\u003e` satisfies\nthe matcher on its first line (`\"valid pubid\"` is a complete `PubidLiteral`) and the whole value —\nincluding the post-newline breakout — is emitted after `PUBLIC`/`SYSTEM`.\n\n### Root Cause\n\n1. A shared regexp builder compiles anchored productions with the `m` flag.\n2. `^…$` under `m` are line anchors, not string anchors.\n3. A full-string validator built on such a production (`.test()`) accepts any string with one\n   conforming line, so a complete, valid literal on the first line passes even though a line terminator\n   and breakout markup follow. `PubidChar` excluding `\u003c`/`\u003e` does not prevent it — the breakout is\n   appended *after* the literal, not embedded inside it.\n\nThe triggering line terminators are the ECMAScript `LineTerminator` set: U+000A, U+000D, U+2028, U+2029.\n\n## Affected Versions\n\nOnly `@xmldom/xmldom` 0.9.x is affected. The vulnerable matchers are built by `lib/grammar.js`'s\n`m`-flagged `reg()` builder, and the DocType `publicId`/`systemId` `requireWellFormed` check that\nconsumes them was introduced in 0.9.10 (the GHSA-f6ww-3ggp-fr8h fix); 0.9.10 and 0.9.11 carry it.\n`0.8.x` performs the same `requireWellFormed` check with inline, non-`m` regular expressions and is not\naffected. The unscoped `xmldom` package has no `grammar.js` and no `requireWellFormed` serializer, so\nthere is no check to bypass.\n\n## Proof of Concept\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\nconst impl = new DOMImplementation();\n\n// publicId: complete literal on line 1, then newline + breakout\nconst dt = impl.createDocumentType('html', '\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e', '');\nconst doc = impl.createDocument(null, 'root', dt);\nconsole.log(new XMLSerializer().serializeToString(doc, { requireWellFormed: true }));\n// Observed (no throw):\n//   \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n//   \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n// Expected: InvalidStateError (publicId is not a valid PubidLiteral).\n// Control: a single-line invalid publicId (\"no-surrounding-quotes\u003c\u003e\") DOES throw InvalidStateError,\n// confirming the check is active and specifically bypassed by the line terminator.\n```\n\n## Impact\n\n- **Bypass of the GHSA-f6ww-3ggp-fr8h mitigation.** Applications that adopted `requireWellFormed:\n  true` to neutralize DocumentType injection remain exposed.\n- **XML structure injection into the DOCTYPE**, including injected markup / entity declarations after\n  the public or system identifier.\n\n## Fix Applied\n\nThe anchored `PubidLiteral`/`SystemLiteral` validators used by the `requireWellFormed`\nserializer no longer treat an interior line terminator as satisfying the `$` anchor, so a `publicId`\nor `systemId` containing any ECMAScript `LineTerminator` (U+000A, U+000D, U+2028, U+2029) is rejected\nwith `InvalidStateError`. Valid single-line identifiers serialize unchanged, and the default\nserialization path is unaffected.\n\n\u003e **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain vulnerable\n\u003e unless `{ requireWellFormed: true }` is explicitly passed. Applications that serialize untrusted DOM\n\u003e content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\nconst impl = new DOMImplementation();\nconst dt = impl.createDocumentType('html', '\"valid pubid\"\\n\"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e', '');\nconst doc = impl.createDocument(null, 'root', dt);\n\n// Default path (requireWellFormed off) — unchanged, still emits verbatim:\nconsole.log(new XMLSerializer().serializeToString(doc));\n//   \u003c!DOCTYPE html PUBLIC \"valid pubid\"\n//   \"\u003e\u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\u003e\u003croot/\u003e\n\n// Opt-in path — now throws instead of emitting the breakout:\nnew XMLSerializer().serializeToString(doc, { requireWellFormed: true });\n//   InvalidStateError: DocumentType publicId is not a valid PubidLiteral\n```\n\n### Why the default stays verbatim\n\nThe W3C DOM Parsing \"require well-formed\" flag defaults to false, and a browser `XMLSerializer` emits\nthe DOCTYPE verbatim. Unconditionally throwing on a malformed `publicId`/`systemId` would be an\nunjustified breaking change to the default path, so the fix tightens only the opt-in\n`requireWellFormed` validator, matching browser and spec defaults.\n\n### Residual limitation\n\nThe guarantee holds only for callers that pass `{ requireWellFormed: true }`; the default\nserialization path still emits `publicId`/`systemId` verbatim. `publicId` and `systemId` are not\nvalidated at creation (`createDocumentType`) or on direct property assignment\n(`documentType.publicId = …`) — the WHATWG DOM specification places no well-formedness constraint on\nthese fields at creation time, so the serializer is the spec-aligned enforcement point.","status":"published","published":"2026-09-08T21:02:25Z","modified":"2026-09-08T21:15:04.901097321Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-625","CWE-91"],"updated_at":"2026-11-02T15:46:57.694388Z"},{"id":"UBUNTU-CVE-2026-83618","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. From 0.9.10 until 0.9.12, the requireWellFormed: true serializer validates DocumentType.publicId and DocumentType.systemId with PubidLiteral_match and SystemLiteral_match expressions produced by reg() in lib/grammar.js, which inherit the multiline flag. A complete valid literal on the first line can therefore satisfy the matcher while U+000A, U+000D, U+2028, or U+2029 and breakout markup remain in the emitted \u003c!DOCTYPE ...\u003e declaration. This bypasses the strict-serialization mitigation for the earlier DocumentType injection advisory; creation and direct property assignment remain unvalidated by design. This issue is fixed in @xmldom/xmldom version 0.9.12.","status":"published","published":"2026-09-01T15:17:00Z","modified":"2026-10-17T13:01:11.995834443Z","cvss":[{"version":"4.0","score":8.7,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T16:57:22.213888Z"},{"id":"CLSA-2026-1790608669","title":"TuxCare security update for @xmldom/xmldom (9 CVEs)","description":"TuxCare rebuilt @xmldom/xmldom to address 9 CVEs. Fixed in 0.9.10-tuxcare.1.","status":"published","published":"2026-09-28T15:17:49Z","modified":"2026-10-15T09:47:38.560737529Z","updated_at":"2026-11-02T15:43:48.65893Z"},{"id":"ROOT-APP-NPM-CVE-2026-83618","title":"CVE-2026-83618 in @xmldom/xmldom - Patched by Root","description":"Root has patched CVE-2026-83618 in the @xmldom/xmldom package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-09-16T07:51:17Z","modified":"2026-09-16T08:15:02.825446293Z","updated_at":"2026-11-02T15:53:27.649662Z"}]}}
