{"id":"CVE-2026-83608","aliases":["AZL-99345","CLSA-2026-1789030963","CLSA-2026-1790108624","CLSA-2026-1790608669","CLSA-2026-1790755546","CLSA-2026-1791192659","DEBIAN-CVE-2026-83608","EUVD-2026-69219","GHSA-27p8-2357-5qqv","GHSA-6gmq-8vp8-gcm6","GHSA-965w-775f-mr7g","UBUNTU-CVE-2026-83608"],"status":"published","title":"xmldom: DocType `name` Injection Bypasses requireWellFormed","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","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-10","score":0.00612,"percentile":0.49638,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-91"],"published":"2026-09-01T14:27:26Z","modified":"2026-11-05T13:28:23Z","fixes":[{"ecosystem":"npm","package":"@xmldom/xmldom","fixed_in":["0.8.15","0.9.12"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"xmldom","fixed_in":["0.6.0-tuxcare.5"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"node-xmldom","fixed_in":["0.9.12-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.7.0"},{"last_affected":"0.8.14"},{"fixed":"0.8.15"}]}]},{"source":"ghsa","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"last_affected":"0.9.11"},{"fixed":"0.9.12"}]}]},{"source":"ghsa","ecosystem":"npm","package":"xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.6.0"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"python-tensorboard","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.16.2-6"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.6.0-tuxcare.0"},{"fixed":"0.6.0-tuxcare.5"}]}],"versions":["0.6.0","0.6.0-tuxcare.1","0.6.0-tuxcare.2","0.6.0-tuxcare.3","0.6.0-tuxcare.4"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.1.31-tuxcare.0"},{"fixed":"0.1.31-tuxcare.5"}]}],"versions":["0.1.31","0.1.31-tuxcare.1","0.1.31-tuxcare.2","0.1.31-tuxcare.3","0.1.31-tuxcare.4"],"bundled":5},{"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":"TuxCare:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.7.13-tuxcare.0"},{"fixed":"0.7.13-tuxcare.1"}]}],"versions":["0.7.13"],"bundled":14},{"source":"osv","ecosystem":"TuxCare:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.8-tuxcare.0"},{"fixed":"0.9.8-tuxcare.2"}]}],"versions":["0.9.8","0.9.8-tuxcare.1"],"bundled":8},{"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.7.0"},{"fixed":"0.8.15"}]}]},{"source":"osv","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0.9.0"},{"fixed":"0.9.12"}]}]},{"source":"osv","ecosystem":"npm","package":"xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"0.6.0"}]}]},{"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-27p8-2357-5qqv","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/pull/1072","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/xmldom/xmldom/commit/57aec90ac57b4408ae7c5d1746bf2a693b5ed90e","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/xmldom/xmldom/commit/85f12eb4d14b44de33216cfb72b50af4d24e9fdd","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/xmldom/xmldom/releases/tag/0.8.15","tags":["x_refsource_MISC","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-83608","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-27p8-2357-5qqv"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/83xxx/CVE-2026-83608.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-83608","tags":["advisory"]},{"url":"https://github.com/xmldom/xmldom","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-83608","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-83608","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-83608","title":"xmldom: DocType `name` Injection Bypasses requireWellFormed","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T14:27:26.057Z","modified":"2026-09-04T01:56:15.954Z","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"],"updated_at":"2026-11-02T13:45:14.072357Z"}],"euvd":[{"id":"EUVD-2026-69219","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T14:27:26Z","modified":"2026-09-04T01:56:15Z","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-27p8-2357-5qqv","title":"xmldom: DocType `name` Injection Bypasses requireWellFormed","description":"## Summary\n\nThe `@xmldom/xmldom` serializer emits `DocumentType.name` verbatim into the\n`\u003c!DOCTYPE …\u003e` declaration with no well-formedness guard. GHSA-f6ww-3ggp-fr8h\n(CVE-2026-41674) hardened the serializer's `requireWellFormed` path for a\nDocumentType's sibling fields — `publicId`, `systemId`, and `internalSubset` —\nbut it did **not** add any check for `name`. A `\u003e` (or whitespace) in the name\nterminates the doctype declaration early, letting the remaining characters\nbecome sibling markup in the serialized output.\n\nBecause `requireWellFormed: true` — the recommended mitigation for the prior\nxmldom injection CVEs — performs no validation on the DocType `name`, this is a\nbypass of that control, in the same family as the open element-name\n(GHSA-w2rr-34g9-rvrj) and attribute-name (GHSA-4w3w-2rp5-g8jm) name-injection advisories.\n\n## Details\n\nThe serializer's `DOCUMENT_TYPE_NODE` case runs the `requireWellFormed` block\nonly against `publicId`, `systemId`, and `internalSubset`, then pushes\n`n.name` directly into the buffer between the `\u003c!DOCTYPE ` prefix and the\nclosing `\u003e`:\n\n- 0.9.x (v0.9.10, `bb7a085`):\n  [serializer DocType case, `lib/dom.js#L3256-L3283`](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3256-L3283)\n  — the `requireWellFormed` block ([#L3259-L3269](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3259-L3269))\n  validates `publicId`/`systemId`/`internalSubset` but not `name`, which is\n  emitted verbatim at [#L3270](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3270).\n- 0.8.x (v0.8.13, `e5c1480`):\n  [serializer DocType case, `lib/dom.js#L1914-L1946`](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L1914-L1946)\n  — same structure; `name` is emitted verbatim at [#L1928](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L1928).\n- unscoped `xmldom` (v0.6.0, `c80a161`):\n  [`lib/dom.js#L1105`](https://github.com/xmldom/xmldom/blob/c80a161172cc4d8733583bf0cf59abfa589f6d9e/lib/dom.js#L1105)\n  emits `node.name` verbatim; this line predates `requireWellFormed`, so there\n  is no well-formedness path at all.\n\n### Enabling write paths\n\n`DocumentType.name` is a plain, writable own-property, so the enabling vector\ndiffers by line:\n\n- **0.9.x** — `createDocumentType()` validates the name via\n  `validateQualifiedName` ([`lib/dom.js#L925-L936`](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L925-L936),\n  validation at [#L926](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L926)),\n  so the deliverable vector is a **direct property write**\n  (`dt.name = 'html\u003e\u003cscript\u003e…'`) to the unguarded own-property.\n- **0.8.x** — `createDocumentType()` does **not** validate the name\n  ([`lib/dom.js#L456-L464`](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L456-L464)),\n  so the malicious name is reachable directly through `createDocumentType()` as\n  well as via direct property write.\n- **unscoped `xmldom` (\u003c= 0.6.0)** — `createDocumentType()` does not validate\n  the name ([`lib/dom.js#L286`](https://github.com/xmldom/xmldom/blob/c80a161172cc4d8733583bf0cf59abfa589f6d9e/lib/dom.js#L286)),\n  same as 0.8.x.\n\nThis is the same structural root cause the sibling name-injection advisories\nshare: the serializer's `requireWellFormed` path validates content delimiters\nbut no name field, and every name-like field is a plain writable property, so\nmutation / direct property-write bypasses any creation-time check.\n\n### Root Cause\n\n1. The serializer's `requireWellFormed` DocType block checks `publicId`,\n   `systemId`, and `internalSubset` (the fields hardened by GHSA-f6ww-3ggp-fr8h)\n   but has no check for `name`.\n2. `DocumentType.name` is a plain writable own-property; on 0.8.x and the\n   unscoped package `createDocumentType()` does not validate it either.\n3. The serializer emits `name` directly between the doctype delimiters:\n   `\u003c!DOCTYPE ${name}…\u003e`.\n\n## Proof of Concept\n\nRun against `@xmldom/xmldom` v0.9.10 (commit `bb7a085`):\n\n```javascript\nconst { DOMImplementation, XMLSerializer, DOMParser } = require('@xmldom/xmldom');\n\nconst impl = new DOMImplementation();\nconst serializer = new XMLSerializer();\n\n// 0.9.x createDocumentType validates the name, so overwrite it via direct property write\nconst dt = impl.createDocumentType('html', '', '');\ndt.name = 'html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script';\nconst doc = impl.createDocument(null, 'r', dt);\n\nconst output = serializer.serializeToString(doc, { requireWellFormed: true });\nconsole.log(output);\n// Output: \u003c!DOCTYPE html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script\u003e\u003cr/\u003e\n//\n// requireWellFormed: true did NOT prevent the injection (no exception thrown).\n// The injected \u003cscript\u003e is well-formed XHTML that a browser would execute.\n```\n\nConfirmed runtime behavior:\n\n- **0.9.x** — `createDocumentType()` rejects the malicious name at creation\n  (`InvalidCharacterError`); a direct write to `dt.name` bypasses that, and\n  `serializeToString(…, { requireWellFormed: true })` emits the breakout with no\n  exception.\n- **Re-parse confirmation** — re-parsing the output shows the injected\n  `\u003cscript\u003e` is a real second top-level element originating entirely from the\n  DocType name: the parser rejects it with\n  `HierarchyRequestError: Only one element can be added and only after doctype`.\n  A comment-injection variant (`dt.name = 'html\u003e\u003c!--INJECTED--'`, output\n  `\u003c!DOCTYPE html\u003e\u003c!--INJECTED--\u003e\u003cr/\u003e`) re-parses cleanly and the injected\n  comment node is enumerable, confirming the injected node is structurally live.\n- **0.8.x** (v0.8.13, `e5c1480`) — `createDocumentType('html\u003e\u003cscript\u003e…', '', '')`\n  accepts the malicious name directly (no creation-time validation), and\n  `serializeToString(doc, null, null, { requireWellFormed: true })` produces\n  `\u003c!DOCTYPE html\u003e\u003cscript\u003ealert(1)\u003c/script\u003e\u003cr/\u003e` with no exception.\n\nA browser reproduction does not apply: browsers keep `DocumentType.name`\n`readonly`, so the direct-write vector cannot be reproduced in a browser DOM.\nThe injection is specific to xmldom exposing `name` as writable and serializing\nit without a guard.\n\n## Impact\n\nApplications that build a `DocumentType` node with an attacker-influenced\n`name` — via direct property write on any affected line, or via\n`createDocumentType()` on 0.8.x and the unscoped package — and serialize the\ndocument are vulnerable to XML/markup injection:\n\n- **XML structure injection** — breaking out of the `\u003c!DOCTYPE …\u003e` declaration\n  to inject arbitrary sibling elements, comments, or additional markup into the\n  output.\n- **XSS via XHTML** — if the serialized output is served as XHTML or processed\n  by a browser-based XML parser, an injected `\u003cscript\u003e` element (in the XHTML\n  namespace) executes.\n- **requireWellFormed bypass** — applications that adopted\n  `requireWellFormed: true` as a mitigation for the prior injection CVEs\n  (including the sibling DocType fields fixed by GHSA-f6ww-3ggp-fr8h) remain\n  vulnerable through the DocType `name`.\n\n## Fix Applied\n\nUnder `requireWellFormed`, the serializer validates the DocType `name` as a well-formed XML\n`Name` and throws `InvalidStateError` when it is not — matching the sibling\n`publicId`/`systemId`/`internalSubset` checks. Non-breaking and opt-in; ships on both maintained versions. No\ncreation-time change is made: 0.9.x already validates the name at `createDocumentType`, and the\n0.8.x/unscoped creation gap cannot be closed without a breaking change, so it is left\nunfixed. See the [XML `Name` production](https://www.w3.org/TR/xml/#NT-Name).\n\u003e **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain\n\u003e vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that\n\u003e serialize untrusted DOM content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```javascript\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\n\nconst impl = new DOMImplementation();\nconst serializer = new XMLSerializer();\n\nconst dt = impl.createDocumentType('html', '', '');\ndt.name = 'html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script';\nconst doc = impl.createDocument(null, 'r', dt);\n\n// Default path: the ill-formed name is still emitted verbatim (injection present).\nconsole.log(serializer.serializeToString(doc));\n// \u003c!DOCTYPE html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script\u003e\u003cr/\u003e\n\n// Opt-in path: serialization throws instead of emitting the breakout.\nserializer.serializeToString(doc, { requireWellFormed: true });\n// throws InvalidStateError\n```\n\n### Why the default stays verbatim\n\nW3C DOM Parsing's require-well-formed flag defaults to false, and the browser\n`XMLSerializer` emits the name verbatim in that default mode. Unconditionally\nthrowing would be an unjustified breaking change against that specified default,\nso the guard is opt-in behind `{ requireWellFormed: true }`.\n\n### Residual limitation\n\nThe default serialization path still emits the ill-formed DocType `name`\nverbatim; protection applies only when `requireWellFormed: true` is passed. No\ncreation-time validation is added for the DocType `name`: 0.9.x already validates\nat `createDocumentType`, and the 0.8.x/unscoped creation gap is left unfixed — it\ncannot be closed without a breaking change.","status":"published","published":"2026-09-08T21:02:51Z","modified":"2026-09-08T21:02:54Z","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"],"updated_at":"2026-11-02T14:41:14.764963Z"}],"nvd":[{"id":"CVE-2026-83608","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T15:17:38.8Z","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"],"updated_at":"2026-11-02T14:12:43.04945Z"}],"osv":[{"id":"CVE-2026-83608","title":"xmldom: DocType `name` Injection Bypasses requireWellFormed","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T14:27:26.057Z","modified":"2026-10-28T02:30:32.622684472Z","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"],"updated_at":"2026-11-02T15:45:18.482949Z"},{"id":"DEBIAN-CVE-2026-83608","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T15:17:38.8Z","modified":"2026-10-01T03:03:43.909901616Z","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-27p8-2357-5qqv","title":"xmldom: DocType `name` Injection Bypasses requireWellFormed","description":"## Summary\n\nThe `@xmldom/xmldom` serializer emits `DocumentType.name` verbatim into the\n`\u003c!DOCTYPE …\u003e` declaration with no well-formedness guard. GHSA-f6ww-3ggp-fr8h\n(CVE-2026-41674) hardened the serializer's `requireWellFormed` path for a\nDocumentType's sibling fields — `publicId`, `systemId`, and `internalSubset` —\nbut it did **not** add any check for `name`. A `\u003e` (or whitespace) in the name\nterminates the doctype declaration early, letting the remaining characters\nbecome sibling markup in the serialized output.\n\nBecause `requireWellFormed: true` — the recommended mitigation for the prior\nxmldom injection CVEs — performs no validation on the DocType `name`, this is a\nbypass of that control, in the same family as the open element-name\n(GHSA-w2rr-34g9-rvrj) and attribute-name (GHSA-4w3w-2rp5-g8jm) name-injection advisories.\n\n## Details\n\nThe serializer's `DOCUMENT_TYPE_NODE` case runs the `requireWellFormed` block\nonly against `publicId`, `systemId`, and `internalSubset`, then pushes\n`n.name` directly into the buffer between the `\u003c!DOCTYPE ` prefix and the\nclosing `\u003e`:\n\n- 0.9.x (v0.9.10, `bb7a085`):\n  [serializer DocType case, `lib/dom.js#L3256-L3283`](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3256-L3283)\n  — the `requireWellFormed` block ([#L3259-L3269](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3259-L3269))\n  validates `publicId`/`systemId`/`internalSubset` but not `name`, which is\n  emitted verbatim at [#L3270](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L3270).\n- 0.8.x (v0.8.13, `e5c1480`):\n  [serializer DocType case, `lib/dom.js#L1914-L1946`](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L1914-L1946)\n  — same structure; `name` is emitted verbatim at [#L1928](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L1928).\n- unscoped `xmldom` (v0.6.0, `c80a161`):\n  [`lib/dom.js#L1105`](https://github.com/xmldom/xmldom/blob/c80a161172cc4d8733583bf0cf59abfa589f6d9e/lib/dom.js#L1105)\n  emits `node.name` verbatim; this line predates `requireWellFormed`, so there\n  is no well-formedness path at all.\n\n### Enabling write paths\n\n`DocumentType.name` is a plain, writable own-property, so the enabling vector\ndiffers by line:\n\n- **0.9.x** — `createDocumentType()` validates the name via\n  `validateQualifiedName` ([`lib/dom.js#L925-L936`](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L925-L936),\n  validation at [#L926](https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L926)),\n  so the deliverable vector is a **direct property write**\n  (`dt.name = 'html\u003e\u003cscript\u003e…'`) to the unguarded own-property.\n- **0.8.x** — `createDocumentType()` does **not** validate the name\n  ([`lib/dom.js#L456-L464`](https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L456-L464)),\n  so the malicious name is reachable directly through `createDocumentType()` as\n  well as via direct property write.\n- **unscoped `xmldom` (\u003c= 0.6.0)** — `createDocumentType()` does not validate\n  the name ([`lib/dom.js#L286`](https://github.com/xmldom/xmldom/blob/c80a161172cc4d8733583bf0cf59abfa589f6d9e/lib/dom.js#L286)),\n  same as 0.8.x.\n\nThis is the same structural root cause the sibling name-injection advisories\nshare: the serializer's `requireWellFormed` path validates content delimiters\nbut no name field, and every name-like field is a plain writable property, so\nmutation / direct property-write bypasses any creation-time check.\n\n### Root Cause\n\n1. The serializer's `requireWellFormed` DocType block checks `publicId`,\n   `systemId`, and `internalSubset` (the fields hardened by GHSA-f6ww-3ggp-fr8h)\n   but has no check for `name`.\n2. `DocumentType.name` is a plain writable own-property; on 0.8.x and the\n   unscoped package `createDocumentType()` does not validate it either.\n3. The serializer emits `name` directly between the doctype delimiters:\n   `\u003c!DOCTYPE ${name}…\u003e`.\n\n## Proof of Concept\n\nRun against `@xmldom/xmldom` v0.9.10 (commit `bb7a085`):\n\n```javascript\nconst { DOMImplementation, XMLSerializer, DOMParser } = require('@xmldom/xmldom');\n\nconst impl = new DOMImplementation();\nconst serializer = new XMLSerializer();\n\n// 0.9.x createDocumentType validates the name, so overwrite it via direct property write\nconst dt = impl.createDocumentType('html', '', '');\ndt.name = 'html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script';\nconst doc = impl.createDocument(null, 'r', dt);\n\nconst output = serializer.serializeToString(doc, { requireWellFormed: true });\nconsole.log(output);\n// Output: \u003c!DOCTYPE html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script\u003e\u003cr/\u003e\n//\n// requireWellFormed: true did NOT prevent the injection (no exception thrown).\n// The injected \u003cscript\u003e is well-formed XHTML that a browser would execute.\n```\n\nConfirmed runtime behavior:\n\n- **0.9.x** — `createDocumentType()` rejects the malicious name at creation\n  (`InvalidCharacterError`); a direct write to `dt.name` bypasses that, and\n  `serializeToString(…, { requireWellFormed: true })` emits the breakout with no\n  exception.\n- **Re-parse confirmation** — re-parsing the output shows the injected\n  `\u003cscript\u003e` is a real second top-level element originating entirely from the\n  DocType name: the parser rejects it with\n  `HierarchyRequestError: Only one element can be added and only after doctype`.\n  A comment-injection variant (`dt.name = 'html\u003e\u003c!--INJECTED--'`, output\n  `\u003c!DOCTYPE html\u003e\u003c!--INJECTED--\u003e\u003cr/\u003e`) re-parses cleanly and the injected\n  comment node is enumerable, confirming the injected node is structurally live.\n- **0.8.x** (v0.8.13, `e5c1480`) — `createDocumentType('html\u003e\u003cscript\u003e…', '', '')`\n  accepts the malicious name directly (no creation-time validation), and\n  `serializeToString(doc, null, null, { requireWellFormed: true })` produces\n  `\u003c!DOCTYPE html\u003e\u003cscript\u003ealert(1)\u003c/script\u003e\u003cr/\u003e` with no exception.\n\nA browser reproduction does not apply: browsers keep `DocumentType.name`\n`readonly`, so the direct-write vector cannot be reproduced in a browser DOM.\nThe injection is specific to xmldom exposing `name` as writable and serializing\nit without a guard.\n\n## Impact\n\nApplications that build a `DocumentType` node with an attacker-influenced\n`name` — via direct property write on any affected line, or via\n`createDocumentType()` on 0.8.x and the unscoped package — and serialize the\ndocument are vulnerable to XML/markup injection:\n\n- **XML structure injection** — breaking out of the `\u003c!DOCTYPE …\u003e` declaration\n  to inject arbitrary sibling elements, comments, or additional markup into the\n  output.\n- **XSS via XHTML** — if the serialized output is served as XHTML or processed\n  by a browser-based XML parser, an injected `\u003cscript\u003e` element (in the XHTML\n  namespace) executes.\n- **requireWellFormed bypass** — applications that adopted\n  `requireWellFormed: true` as a mitigation for the prior injection CVEs\n  (including the sibling DocType fields fixed by GHSA-f6ww-3ggp-fr8h) remain\n  vulnerable through the DocType `name`.\n\n## Fix Applied\n\nUnder `requireWellFormed`, the serializer validates the DocType `name` as a well-formed XML\n`Name` and throws `InvalidStateError` when it is not — matching the sibling\n`publicId`/`systemId`/`internalSubset` checks. Non-breaking and opt-in; ships on both maintained versions. No\ncreation-time change is made: 0.9.x already validates the name at `createDocumentType`, and the\n0.8.x/unscoped creation gap cannot be closed without a breaking change, so it is left\nunfixed. See the [XML `Name` production](https://www.w3.org/TR/xml/#NT-Name).\n\u003e **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain\n\u003e vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that\n\u003e serialize untrusted DOM content should audit all `serializeToString()` call sites and add it.\n\n### Proof of Concept - fixed path\n\n```javascript\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\n\nconst impl = new DOMImplementation();\nconst serializer = new XMLSerializer();\n\nconst dt = impl.createDocumentType('html', '', '');\ndt.name = 'html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script';\nconst doc = impl.createDocument(null, 'r', dt);\n\n// Default path: the ill-formed name is still emitted verbatim (injection present).\nconsole.log(serializer.serializeToString(doc));\n// \u003c!DOCTYPE html\u003e\u003cscript xmlns=\"http://www.w3.org/1999/xhtml\"\u003ealert(1)\u003c/script\u003e\u003cr/\u003e\n\n// Opt-in path: serialization throws instead of emitting the breakout.\nserializer.serializeToString(doc, { requireWellFormed: true });\n// throws InvalidStateError\n```\n\n### Why the default stays verbatim\n\nW3C DOM Parsing's require-well-formed flag defaults to false, and the browser\n`XMLSerializer` emits the name verbatim in that default mode. Unconditionally\nthrowing would be an unjustified breaking change against that specified default,\nso the guard is opt-in behind `{ requireWellFormed: true }`.\n\n### Residual limitation\n\nThe default serialization path still emits the ill-formed DocType `name`\nverbatim; protection applies only when `requireWellFormed: true` is passed. No\ncreation-time validation is added for the DocType `name`: 0.9.x already validates\nat `createDocumentType`, and the 0.8.x/unscoped creation gap is left unfixed — it\ncannot be closed without a breaking change.","status":"published","published":"2026-09-08T21:02:51Z","modified":"2026-09-08T21:15:03.913503797Z","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"],"updated_at":"2026-11-02T15:46:33.956911Z"},{"id":"UBUNTU-CVE-2026-83608","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T15:17:00Z","modified":"2026-10-17T13:01:11.973538538Z","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":"AZL-99345","title":"CVE-2026-83608 affecting package python-tensorboard 2.16.2-6","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.15 and 0.9.12, and in xmldom version 0.6.0 and earlier, the DOCUMENT_TYPE_NODE branch in lib/dom.js validates publicId, systemId, and internalSubset under requireWellFormed: true but emits DocumentType.name verbatim. A name containing \u003e or whitespace can terminate the \u003c!DOCTYPE ...\u003e declaration and inject sibling markup; the value can be supplied through createDocumentType() on the 0.8.x and unscoped lines or through a direct DocumentType.name property write on every affected line. The default path and legacy creation-time behavior remain permissive, while the vulnerable strict path fails to enforce an XML Name. This issue is fixed in @xmldom/xmldom versions 0.8.15 and 0.9.12; no fixed version is available for xmldom.","status":"published","published":"2026-09-01T15:17:38Z","modified":"2026-09-08T05:27:28Z","updated_at":"2026-11-02T15:28:12.596039Z"},{"id":"CLSA-2026-1789030963","title":"TuxCare security update for xmldom (1 CVE)","description":"TuxCare rebuilt xmldom to address 1 CVE. Fixed in 0.6.0-tuxcare.5.","status":"published","published":"2026-09-10T09:02:43Z","modified":"2026-11-05T13:28:20.297768596Z","updated_at":"2026-11-05T16:37:45.64821Z"},{"id":"CLSA-2026-1790108624","title":"TuxCare security update for xmldom (5 CVEs)","description":"TuxCare rebuilt xmldom to address 5 CVEs. Fixed in 0.1.31-tuxcare.5.","status":"published","published":"2026-09-22T20:23:44Z","modified":"2026-11-05T13:28:23.742251042Z","updated_at":"2026-11-05T16:37:45.64821Z"},{"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":"CLSA-2026-1790755546","title":"TuxCare security update for @xmldom/xmldom (14 CVEs)","description":"TuxCare rebuilt @xmldom/xmldom to address 14 CVEs. Fixed in 0.7.13-tuxcare.1.","status":"published","published":"2026-09-30T08:05:46Z","modified":"2026-10-15T09:47:53.549832566Z","updated_at":"2026-11-02T15:43:48.65893Z"},{"id":"CLSA-2026-1791192659","title":"TuxCare security update for @xmldom/xmldom (10 CVEs)","description":"TuxCare rebuilt @xmldom/xmldom to address 10 CVEs. Fixed in 0.9.8-tuxcare.2.","status":"published","published":"2026-10-05T09:30:59Z","modified":"2026-10-15T09:47:39.24556286Z","updated_at":"2026-11-02T15:43:49.016976Z"}]}}
