{"id":"CVE-2026-34601","aliases":["CLSA-2026-1778114662","CLSA-2026-1791987635","CLSA-2026-1791987856","DEBIAN-CVE-2026-34601","EUVD-2026-18460","GHSA-wh4c-j3r5-mjhp","UBUNTU-CVE-2026-34601","openSUSE-SU-2026:10497-1","openSUSE-SU-2026:10506-1"],"status":"published","title":"xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.0049,"percentile":0.42434,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-91"],"published":"2026-04-01T00:19:06Z","modified":"2026-11-05T13:28:26Z","fixes":[{"ecosystem":"npm","package":"@xmldom/xmldom","fixed_in":["0.8.12","0.9.9"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"@xmldom/xmldom","fixed_in":["0.7.13-tuxcare.2"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"node-xmldom","fixed_in":["0.9.9-1"],"sources":["osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"heroic-games-launcher","fixed_in":["2.20.1-5.1"],"sources":["osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"python-jupyterlab-templates","fixed_in":["0.5.3-1.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.6.0"}]}]},{"source":"ghsa","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.8.12"}]}]},{"source":"ghsa","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.0"},{"fixed":"0.9.9"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.6.0-tuxcare.0"},{"fixed":"0.6.0-tuxcare.2"}]}],"versions":["0.6.0","0.6.0-tuxcare.1"],"bundled":3},{"source":"osv","ecosystem":"TuxCare:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.7.13-tuxcare.0"},{"fixed":"0.7.13-tuxcare.2"}]}],"versions":["0.7.13","0.7.13-tuxcare.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"@xmldom/xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.9.8-tuxcare.0"},{"fixed":"0.9.8-tuxcare.5"}]}],"versions":["0.9.8","0.9.8-tuxcare.1","0.9.8-tuxcare.2","0.9.8-tuxcare.3","0.9.8-tuxcare.4"],"bundled":4},{"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.9-1"}]}],"versions":["0.9.6-1","0.9.8-1","0.9.8-2"]},{"source":"osv","ecosystem":"npm","package":"xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"0.6.0"}]}]},{"source":"osv","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.8.12"}]}]},{"source":"osv","ecosystem":"npm","package":"@xmldom/xmldom","ranges":[{"type":"SEMVER","events":[{"introduced":"0.9.0"},{"fixed":"0.9.9"}]}]},{"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:25.10","package":"node-xmldom","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.9.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"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"heroic-games-launcher","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.20.1-5.1"}]}]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-jupyterlab-templates","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.5.3-1.1"}]}]}],"products":[{"vendor":"red hat","product":"migration toolkit for applications 8"},{"vendor":"red hat","product":"red hat build of podman desktop"},{"vendor":"red hat","product":"red hat developer hub"},{"vendor":"red hat","product":"red hat fuse 7"},{"vendor":"red hat","product":"red hat openshift ai (rhoai)"},{"vendor":"red hat","product":"red hat openshift container platform 4"},{"vendor":"red hat","product":"red hat openshift dev spaces"},{"vendor":"red hat","product":"self-service automation portal 2"},{"vendor":"xmldom","product":"xmldom"}],"references":[{"url":"https://github.com/xmldom/xmldom/security/advisories/GHSA-wh4c-j3r5-mjhp","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/xmldom/xmldom/commit/2b852e836ab86dbbd6cbaf0537f584dd0b5ac184","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/xmldom/xmldom/releases/tag/0.8.12","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/xmldom/xmldom/releases/tag/0.9.9","tags":["x_refsource_MISC","web"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-34601","tags":["vdb-entry","x_refsource_REDHAT","advisory"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2454595","tags":["issue-tracking","x_refsource_REDHAT","report"]},{"url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-34601.json","tags":["x_sadp-csaf-vex","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34601","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-wh4c-j3r5-mjhp"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34601.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-34601","tags":["advisory"]},{"url":"https://github.com/xmldom/xmldom","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-34601","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-34601","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-34601","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-34601","title":"xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T17:47:13.209Z","modified":"2026-07-15T01:04:09.128Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high","assessor":"cna"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high","assessor":"adp"}],"cwes":["CWE-91","CWE-91"],"updated_at":"2026-11-02T14:20:14.302133Z"}],"euvd":[{"id":"EUVD-2026-18460","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T17:47:13Z","modified":"2026-07-15T01:04:09Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"updated_at":"2026-11-02T18:19:56.47701Z"}],"ghsa":[{"id":"GHSA-wh4c-j3r5-mjhp","title":"xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion","description":"## Summary\n\n`@xmldom/xmldom` allows attacker-controlled strings containing the CDATA terminator `]]\u003e` to be inserted into a `CDATASection` node. During serialization, `XMLSerializer` emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became **active XML markup** in the serialized output, enabling XML structure\ninjection and downstream business-logic manipulation.\n\nThe sequence `]]\u003e` is not allowed inside CDATA content and must be rejected or safely handled during serialization. ([MDN Web Docs](https://developer.mozilla.org/))\n\n### Attack surface\n\n`Document.createCDATASection(data)` is the most direct entry point, but it is not the only one. The WHATWG DOM spec intentionally does not validate `]]\u003e` in mutation methods — only `createCDATASection` carries that guard. The following paths therefore also allow `]]\u003e` to enter a CDATASection node and reach the serializer:\n\n- `CharacterData.appendData()`\n- `CharacterData.replaceData()`\n- `CharacterData.insertData()`\n- Direct assignment to `.data`\n- Direct assignment to `.textContent`\n\n(Note: assigning to `.nodeValue` does **not** update `.data` in this implementation — the serializer reads `.data` directly — so `.nodeValue` is not an exploitable path.)\n\n### Parse path\n\nParsing XML that contains a CDATA section is **not** affected. The SAX parser's non-greedy `CDSect` regex stops at the first `]]\u003e`, so parsed CDATA data never contains the terminator.\n\n---\n\n## Impact\n\nIf an application uses `xmldom` to generate \"trusted\" XML documents that embed **untrusted user input** inside CDATA (a common pattern in exports, feeds, SOAP/XML integrations, etc.), an attacker can inject additional XML elements/attributes into the generated document.\n\nThis can lead to:\n\n- Integrity violation of generated XML documents.\n- Business-logic injection in downstream consumers (e.g., injecting `\u003capproved\u003etrue\u003c/approved\u003e`,  `\u003crole\u003eadmin\u003c/role\u003e`, workflow flags, or other security-relevant elements).\n- Unexpected privilege/workflow decisions if downstream logic assumes injected nodes cannot appear.\n\nThis issue does **not** require malformed parsers or browser behavior; it is caused by serialization producing attacker-influenced XML markup.\n\n---\n\n## Root Cause (with file + line numbers)\n\n**File:** `lib/dom.js`\n\n### 1. No validation in `createCDATASection`\n\n`createCDATASection: function (data)` accepts any string and appends it directly.\n\n- **Lines 2216–2221** (0.9.8)\n\n### 2. Unsafe CDATA serialization\n\nSerializer prints CDATA sections as:\n\n```\n\u003c![CDATA[ + node.data + ]]\u003e\n```\n\nwithout handling `]]\u003e` in the data.\n\n- **Lines 2919–2920** (0.9.8)\n\nBecause CDATA content is emitted verbatim, an embedded `]]\u003e` closes the CDATA section early and the remainder of the attacker-controlled payload is interpreted as markup in the serialized XML.\n\n---\n\n## Proof of Concept — Fix A: `createCDATASection` now throws\n\nOn patched versions, passing `]]\u003e` directly to `createCDATASection` throws `InvalidCharacterError` instead of silently accepting the payload:\n\n```js\nconst { DOMImplementation } = require('./lib');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\ntry {\n  doc.createCDATASection('SAFE]]\u003e\u003cinjected attr=\"pwn\"/\u003e');\n  console.log('VULNERABLE — no error thrown');\n} catch (e) {\n  console.log('FIXED — threw:', e.name); // InvalidCharacterError\n}\n```\n\nExpected output on patched versions:\n\n```\nFIXED — threw: InvalidCharacterError\n```\n\n---\n\n## Proof of Concept — Fix B: mutation vector now safe\n\nOn patched versions, injecting `]]\u003e` via a mutation method (`appendData`, `replaceData`, `.data =`, `.textContent =`) no longer produces injectable output. The serializer splits the terminator so the result round-trips as safe text:\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('./lib');\nconst { DOMParser } = require('./lib');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\n\n// Start with safe data, then mutate to include the terminator\nconst cdata = doc.createCDATASection('safe');\ndoc.documentElement.appendChild(cdata);\ncdata.appendData(']]\u003e\u003cinjected attr=\"pwn\"/\u003e\u003cmore\u003eTEXT\u003c/more\u003e\u003c![CDATA[');\n\nconst out = new XMLSerializer().serializeToString(doc);\nconsole.log('Serialized:', out);\n\nconst reparsed = new DOMParser().parseFromString(out, 'text/xml');\nconst injected = reparsed.getElementsByTagName('injected').length \u003e 0;\nconsole.log('Injected element found in reparsed doc:', injected);\n// VULNERABLE: true  |  FIXED: false\n```\n\nExpected output on patched versions:\n\n```\nSerialized: \u003croot\u003e\u003c![CDATA[safe]]]]\u003e\u003c![CDATA[\u003e\u003cinjected attr=\"pwn\"/\u003e\u003cmore\u003eTEXT\u003c/more\u003e\u003c![CDATA[]]\u003e\u003c/root\u003e\nInjected element found in reparsed doc: false\n```\n\n---\n\n## Fix Applied\n\nBoth mitigations were implemented:\n\n### Option A — Strict/spec-aligned: reject `]]\u003e` in `createCDATASection()`\n\n`Document.createCDATASection(data)` now throws `InvalidCharacterError` (per the [WHATWG DOM spec](https://dom.spec.whatwg.org/#dom-document-createcdatasection)) when `data` contains `]]\u003e`. This closes the direct entry point.\n\nCode that previously passed a string containing `]]\u003e` to `createCDATASection` and relied on the silent/unsafe behaviour will now receive `InvalidCharacterError`. Use a mutation method such as `appendData` if you intentionally need `]]\u003e` in a CDATASection node's data (the serializer split in Option B will keep the output safe).\n\n### Option B — Defensive serialization: split the terminator during serialization\n\n`XMLSerializer` now replaces every occurrence of `]]\u003e` in CDATA section data with the split sequence `]]]]\u003e\u003c![CDATA[\u003e` before emitting. This closes all mutation-vector paths that Option A alone cannot guard, and means the serialized output is always well-formed XML regardless of how `]]\u003e` entered the node.\n\n## Update — 2026-04-xx (0.9.10 / 0.8.13)\n\n### `splitCDATASections` is deprecated\n\nThe CDATA split behavior introduced as Option B of this fix (replacing `]]\u003e` with`]]]]\u003e\u003c![CDATA[\u003e` during serialization) is **deprecated** as of 0.9.10 / 0.8.13.\n\nThis release introduces a `requireWellFormed` option on `XMLSerializer.serializeToString()`. When `{ requireWellFormed: true }` is passed as the second argument, the serializer throws `InvalidStateError` if CDATA section data contains `]]\u003e` — this is the spec-aligned behavior (W3C DOM Parsing and Serialization, `require well-formed` flag) and the recommended migration path going forward.\nThe split behavior is now controlled by an explicit `splitCDATASections` option (default `true`, preserving the current behavior). The three serialization behaviors are:\n| `requireWellFormed` | `splitCDATASections` | Behavior ||---|---|---|| `false` (default) | `true` (default) | Split `]]\u003e` → `]]]]\u003e\u003c![CDATA[\u003e` (current behavior, deprecated) || `true` | — (ignored) | Throw `InvalidStateError` — spec-aligned, recommended |\\ `false` | `false` | Emit verbatim — same as pre-0.9.9 behavior |\n\n`requireWellFormed: true` takes precedence: the split path is unreachable when it is set.\n\n### Migration\nReplace any reliance on the default split behavior with an explicit opt-in:\n```js// Before (implicit split, deprecated): const xml = new XMLSerializer().serializeToString(doc);\n\n// After (explicit guard, spec-aligned): const xml = new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); // Throws InvalidStateError if any CDATASection contains ']]\u003e' ```\n\n### Removal timeline\nBoth the `splitCDATASections` option and the underlying `]]\u003e` → `]]]]\u003e\u003c![CDATA[\u003e` split mechanics will be removed in the next breaking (`0.10.0`) release. After removal, the only behaviors will be verbatim (default) and `requireWellFormed: true` (throws).\n\nRemoval is tracked in [xmldom/xmldom#999](https://github.com/xmldom/xmldom/issues/999).","status":"published","published":"2026-04-01T00:19:06Z","modified":"2026-04-24T23:17:46Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"cwes":["CWE-91"],"updated_at":"2026-11-02T14:39:58.897998Z"}],"nvd":[{"id":"CVE-2026-34601","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T18:16:31.933Z","modified":"2026-07-21T19:10:00.107Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high","assessor":"secondary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high","assessor":"secondary"}],"cwes":["CWE-91"],"updated_at":"2026-11-02T14:12:12.73929Z"}],"osv":[{"id":"CVE-2026-34601","title":"xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T17:47:13.209Z","modified":"2026-10-28T02:31:01.742299165Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"cwes":["CWE-91"],"updated_at":"2026-11-02T15:45:02.247031Z"},{"id":"DEBIAN-CVE-2026-34601","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T18:16:31.933Z","modified":"2026-10-01T03:03:03.960383576Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"updated_at":"2026-11-02T15:45:54.587575Z"},{"id":"GHSA-wh4c-j3r5-mjhp","title":"xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion","description":"## Summary\n\n`@xmldom/xmldom` allows attacker-controlled strings containing the CDATA terminator `]]\u003e` to be inserted into a `CDATASection` node. During serialization, `XMLSerializer` emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became **active XML markup** in the serialized output, enabling XML structure\ninjection and downstream business-logic manipulation.\n\nThe sequence `]]\u003e` is not allowed inside CDATA content and must be rejected or safely handled during serialization. ([MDN Web Docs](https://developer.mozilla.org/))\n\n### Attack surface\n\n`Document.createCDATASection(data)` is the most direct entry point, but it is not the only one. The WHATWG DOM spec intentionally does not validate `]]\u003e` in mutation methods — only `createCDATASection` carries that guard. The following paths therefore also allow `]]\u003e` to enter a CDATASection node and reach the serializer:\n\n- `CharacterData.appendData()`\n- `CharacterData.replaceData()`\n- `CharacterData.insertData()`\n- Direct assignment to `.data`\n- Direct assignment to `.textContent`\n\n(Note: assigning to `.nodeValue` does **not** update `.data` in this implementation — the serializer reads `.data` directly — so `.nodeValue` is not an exploitable path.)\n\n### Parse path\n\nParsing XML that contains a CDATA section is **not** affected. The SAX parser's non-greedy `CDSect` regex stops at the first `]]\u003e`, so parsed CDATA data never contains the terminator.\n\n---\n\n## Impact\n\nIf an application uses `xmldom` to generate \"trusted\" XML documents that embed **untrusted user input** inside CDATA (a common pattern in exports, feeds, SOAP/XML integrations, etc.), an attacker can inject additional XML elements/attributes into the generated document.\n\nThis can lead to:\n\n- Integrity violation of generated XML documents.\n- Business-logic injection in downstream consumers (e.g., injecting `\u003capproved\u003etrue\u003c/approved\u003e`,  `\u003crole\u003eadmin\u003c/role\u003e`, workflow flags, or other security-relevant elements).\n- Unexpected privilege/workflow decisions if downstream logic assumes injected nodes cannot appear.\n\nThis issue does **not** require malformed parsers or browser behavior; it is caused by serialization producing attacker-influenced XML markup.\n\n---\n\n## Root Cause (with file + line numbers)\n\n**File:** `lib/dom.js`\n\n### 1. No validation in `createCDATASection`\n\n`createCDATASection: function (data)` accepts any string and appends it directly.\n\n- **Lines 2216–2221** (0.9.8)\n\n### 2. Unsafe CDATA serialization\n\nSerializer prints CDATA sections as:\n\n```\n\u003c![CDATA[ + node.data + ]]\u003e\n```\n\nwithout handling `]]\u003e` in the data.\n\n- **Lines 2919–2920** (0.9.8)\n\nBecause CDATA content is emitted verbatim, an embedded `]]\u003e` closes the CDATA section early and the remainder of the attacker-controlled payload is interpreted as markup in the serialized XML.\n\n---\n\n## Proof of Concept — Fix A: `createCDATASection` now throws\n\nOn patched versions, passing `]]\u003e` directly to `createCDATASection` throws `InvalidCharacterError` instead of silently accepting the payload:\n\n```js\nconst { DOMImplementation } = require('./lib');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\ntry {\n  doc.createCDATASection('SAFE]]\u003e\u003cinjected attr=\"pwn\"/\u003e');\n  console.log('VULNERABLE — no error thrown');\n} catch (e) {\n  console.log('FIXED — threw:', e.name); // InvalidCharacterError\n}\n```\n\nExpected output on patched versions:\n\n```\nFIXED — threw: InvalidCharacterError\n```\n\n---\n\n## Proof of Concept — Fix B: mutation vector now safe\n\nOn patched versions, injecting `]]\u003e` via a mutation method (`appendData`, `replaceData`, `.data =`, `.textContent =`) no longer produces injectable output. The serializer splits the terminator so the result round-trips as safe text:\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('./lib');\nconst { DOMParser } = require('./lib');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\n\n// Start with safe data, then mutate to include the terminator\nconst cdata = doc.createCDATASection('safe');\ndoc.documentElement.appendChild(cdata);\ncdata.appendData(']]\u003e\u003cinjected attr=\"pwn\"/\u003e\u003cmore\u003eTEXT\u003c/more\u003e\u003c![CDATA[');\n\nconst out = new XMLSerializer().serializeToString(doc);\nconsole.log('Serialized:', out);\n\nconst reparsed = new DOMParser().parseFromString(out, 'text/xml');\nconst injected = reparsed.getElementsByTagName('injected').length \u003e 0;\nconsole.log('Injected element found in reparsed doc:', injected);\n// VULNERABLE: true  |  FIXED: false\n```\n\nExpected output on patched versions:\n\n```\nSerialized: \u003croot\u003e\u003c![CDATA[safe]]]]\u003e\u003c![CDATA[\u003e\u003cinjected attr=\"pwn\"/\u003e\u003cmore\u003eTEXT\u003c/more\u003e\u003c![CDATA[]]\u003e\u003c/root\u003e\nInjected element found in reparsed doc: false\n```\n\n---\n\n## Fix Applied\n\nBoth mitigations were implemented:\n\n### Option A — Strict/spec-aligned: reject `]]\u003e` in `createCDATASection()`\n\n`Document.createCDATASection(data)` now throws `InvalidCharacterError` (per the [WHATWG DOM spec](https://dom.spec.whatwg.org/#dom-document-createcdatasection)) when `data` contains `]]\u003e`. This closes the direct entry point.\n\nCode that previously passed a string containing `]]\u003e` to `createCDATASection` and relied on the silent/unsafe behaviour will now receive `InvalidCharacterError`. Use a mutation method such as `appendData` if you intentionally need `]]\u003e` in a CDATASection node's data (the serializer split in Option B will keep the output safe).\n\n### Option B — Defensive serialization: split the terminator during serialization\n\n`XMLSerializer` now replaces every occurrence of `]]\u003e` in CDATA section data with the split sequence `]]]]\u003e\u003c![CDATA[\u003e` before emitting. This closes all mutation-vector paths that Option A alone cannot guard, and means the serialized output is always well-formed XML regardless of how `]]\u003e` entered the node.\n\n## Update — 2026-04-xx (0.9.10 / 0.8.13)\n\n### `splitCDATASections` is deprecated\n\nThe CDATA split behavior introduced as Option B of this fix (replacing `]]\u003e` with`]]]]\u003e\u003c![CDATA[\u003e` during serialization) is **deprecated** as of 0.9.10 / 0.8.13.\n\nThis release introduces a `requireWellFormed` option on `XMLSerializer.serializeToString()`. When `{ requireWellFormed: true }` is passed as the second argument, the serializer throws `InvalidStateError` if CDATA section data contains `]]\u003e` — this is the spec-aligned behavior (W3C DOM Parsing and Serialization, `require well-formed` flag) and the recommended migration path going forward.\nThe split behavior is now controlled by an explicit `splitCDATASections` option (default `true`, preserving the current behavior). The three serialization behaviors are:\n| `requireWellFormed` | `splitCDATASections` | Behavior ||---|---|---|| `false` (default) | `true` (default) | Split `]]\u003e` → `]]]]\u003e\u003c![CDATA[\u003e` (current behavior, deprecated) || `true` | — (ignored) | Throw `InvalidStateError` — spec-aligned, recommended |\\ `false` | `false` | Emit verbatim — same as pre-0.9.9 behavior |\n\n`requireWellFormed: true` takes precedence: the split path is unreachable when it is set.\n\n### Migration\nReplace any reliance on the default split behavior with an explicit opt-in:\n```js// Before (implicit split, deprecated): const xml = new XMLSerializer().serializeToString(doc);\n\n// After (explicit guard, spec-aligned): const xml = new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); // Throws InvalidStateError if any CDATASection contains ']]\u003e' ```\n\n### Removal timeline\nBoth the `splitCDATASections` option and the underlying `]]\u003e` → `]]]]\u003e\u003c![CDATA[\u003e` split mechanics will be removed in the next breaking (`0.10.0`) release. After removal, the only behaviors will be verbatim (default) and `requireWellFormed: true` (throws).\n\nRemoval is tracked in [xmldom/xmldom#999](https://github.com/xmldom/xmldom/issues/999).","status":"published","published":"2026-04-01T00:19:06Z","modified":"2026-09-10T03:51:03.715395225Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"cwes":["CWE-91"],"updated_at":"2026-11-02T15:46:58.767335Z"},{"id":"UBUNTU-CVE-2026-34601","description":"xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. In xmldom versions 0.6.0 and prior and @xmldom/xmldom prior to versions 0.8.12 and 0.9.9, xmldom/xmldom allows attacker-controlled strings containing the CDATA terminator ]]\u003e to be inserted into a CDATASection node. During serialization, XMLSerializer emitted the CDATA content verbatim without rejecting or safely splitting the terminator. As a result, data intended to remain text-only became active XML markup in the serialized output, enabling XML structure injection and downstream business-logic manipulation. This issue has been patched in xmldom version 0.6.0 and @xmldom/xmldom versions 0.8.12 and 0.9.9.","status":"published","published":"2026-04-02T18:16:00Z","modified":"2026-10-17T12:58:11.916347758Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"high"}],"updated_at":"2026-11-02T16:56:16.036833Z"},{"id":"CLSA-2026-1778114662","title":"TuxCare security update for xmldom (3 CVEs)","description":"TuxCare rebuilt xmldom to address 3 CVEs. Fixed in 0.6.0-tuxcare.2.","status":"published","published":"2026-08-04T14:52:30Z","modified":"2026-11-05T13:28:26.23619373Z","updated_at":"2026-11-05T16:37:45.64821Z"},{"id":"CLSA-2026-1791987635","title":"TuxCare security update for @xmldom/xmldom (1 CVE)","description":"TuxCare rebuilt @xmldom/xmldom to address 1 CVE. Fixed in 0.7.13-tuxcare.2.","status":"published","published":"2026-10-14T14:20:35Z","modified":"2026-10-15T09:47:40.49540081Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1791987856","title":"TuxCare security update for @xmldom/xmldom (4 CVEs)","description":"TuxCare rebuilt @xmldom/xmldom to address 4 CVEs. Fixed in 0.9.8-tuxcare.5.","status":"published","published":"2026-10-14T14:24:16Z","modified":"2026-10-15T09:48:00.183619915Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"openSUSE-SU-2026:10497-1","title":"heroic-games-launcher-2.20.1-5.1 on GA media","description":"These are all security issues fixed in the heroic-games-launcher-2.20.1-5.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-04-07T00:00:00Z","modified":"2026-04-08T07:50:16.147007Z","updated_at":"2026-11-02T16:58:38.415593Z"},{"id":"openSUSE-SU-2026:10506-1","title":"jupyter-jupyterlab-templates-0.5.3-1.1 on GA media","description":"These are all security issues fixed in the jupyter-jupyterlab-templates-0.5.3-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-04-08T00:00:00Z","modified":"2026-04-09T08:00:55.461761Z","updated_at":"2026-11-02T16:58:38.415593Z"}]}}
