GitHub Advisoryadvisory · npm
GHSA-wh4c-j3r5-mjhp xmldom: XML injection via unsafe CDATA serialization allows attacker-controlled markup insertion
HIGH
Official page ↗
- Aliases
- CVE-2026-34601
- Ecosystem
- npm
- Packages
- xmldom, @xmldom/xmldom
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
- Weaknesses
- CWE-91
- Reviewed
- 2026-04-01
Affected packages
• npm xmldom: <= 0.6.0 (no fix yet)
• npm @xmldom/xmldom: < 0.8.12 → fixed in 0.8.12
• npm @xmldom/xmldom: >= 0.9.0, < 0.9.9 → fixed in 0.9.9
Description
Summary
`@xmldom/xmldom` allows attacker-controlled strings containing the CDATA terminator `]]>` 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.
The sequence `]]>` is not allowed inside CDATA content and must be rejected or safely handled during serialization. (MDN Web Docs)
Attack surface
`Document.createCDATASection(data)` is the most direct entry point, but it is not the only one. The WHATWG DOM spec intentionally does not validate `]]>` in mutation methods — only `createCDATASection` carries that guard. The following paths therefore also allow `]]>` to enter a CDATASection node and reach the serializer:
• `CharacterData.appendData()`
• `CharacterData.replaceData()`
• `CharacterData.insertData()`
• Direct assignment to `.data`
• Direct assignment to `.textContent`
(Note: assigning to `.nodeValue` does not update `.data` in this implementation — the serializer reads `.data` directly — so `.nodeValue` is not an exploitable path.)
Parse path
Parsing XML that contains a CDATA section is not affected. The SAX parser's non-greedy `CDSect` regex stops at the first `]]>`, so parsed CDATA data never contains the terminator.
---
Impact
If 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.
This can lead to:
• Integrity violation of generated XML documents.
• Business-logic injection in downstream consumers (e.g., injecting `true`, `admin`, workflow flags, or other security-relevant elements).
• Unexpected privilege/workflow decisions if downstream logic assumes injected nodes cannot appear.
This issue does not require malformed parsers or browser behavior; it is caused by serialization producing attacker-influenced XML markup.
---
Root Cause (with file + line numbers)
File: `lib/dom.js`
1. No validation in `createCDATASection`
`createCDATASection: function (data)` accepts any string and appends it directly.
• Lines 2216–2221 (0.9.8)
2. Unsafe CDATA serialization
Serializer prints CDATA sections as:
```
```
without handling `]]>` in the data.
• Lines 2919–2920 (0.9.8)
Because CDATA content is emitted verbatim, an embedded `]]>` closes the CDATA section early and the remainder of the attacker-controlled payload is interpreted as markup in the serialized XML.
---
Proof of Concept — Fix A: `createCDATASection` now throws
On patched versions, passing `]]>` directly to `createCDATASection` throws `InvalidCharacterError` instead of silently accepting the payload:
```js
const { DOMImplementation } = require('./lib');
const doc = new DOMImplementation().createDocument(null, 'root', null);
try {
doc.createCDATASection('SAFE]]>');
console.log('VULNERABLE — no error thrown');
} catch (e) {
console.log('FIXED — threw:', e.name); // InvalidCharacterError
}
```
Expected output on patched versions:
```
FIXED — threw: InvalidCharacterError
```
---
Proof of Concept — Fix B: mutation vector now safe
On patched versions, injecting `]]>` 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:
```js
const { DOMImplementation, XMLSerializer } = require('./lib');
const { DOMParser } = require('./lib');
const doc = new DOMImplementation().createDocument(null, 'root', null);
// Start with safe data, then mutate to include the terminator
const cdata = doc.createCDATASection('safe');
doc.documentElement.appendChild(cdata);
cdata.appendData(']]>TEXT 0;
console.log('Injected element found in reparsed doc:', injected);
// VULNERABLE: true | FIXED: false
```
Expected output on patched versions:
```
Serialized: TEXT
Injected element found in reparsed doc: false
```
---
Fix Applied
Both mitigations were implemented:
Option A — Strict/spec-aligned: reject `]]>` in `createCDATASection()`
`Document.createCDATASection(data)` now throws `InvalidCharacterError` (per the WHATWG DOM spec) when `data` contains `]]>`. This closes the direct entry point.
Code that previously passed a string containing `]]>` to `createCDATASection` and relied on the silent/unsafe behaviour will now receive `InvalidCharacterError`. Use a mutation method such as `appendData` if you intentionally need `]]>` in a CDATASection node's data (the serializer split in Option B will keep the output safe).
Option B — Defensive serialization: split the terminator during serialization
`XMLSerializer` now replaces every occurrence of `]]>` in CDATA section data with the split sequence `]]]]>` 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 `]]>` entered the node.
Update — 2026-04-xx (0.9.10 / 0.8.13)
`splitCDATASections` is deprecated
The CDATA split behavior introduced as Option B of this fix (replacing `]]>` with`]]]]>` during serialization) is deprecated as of 0.9.10 / 0.8.13.
This 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 `]]>` — this is the spec-aligned behavior (W3C DOM Parsing and Serialization, `require well-formed` flag) and the recommended migration path going forward.
The split behavior is now controlled by an explicit `splitCDATASections` option (default `true`, preserving the current behavior). The three serialization behaviors are:
| `requireWellFormed` | `splitCDATASections` | Behavior ||---|---|---|| `false` (default) | `true` (default) | Split `]]>` → `]]]]>` (current behavior, deprecated) || `true` | — (ignored) | Throw `InvalidStateError` — spec-aligned, recommended |\ `false` | `false` | Emit verbatim — same as pre-0.9.9 behavior |
`requireWellFormed: true` takes precedence: the split path is unreachable when it is set.
Migration
Replace any reliance on the default split behavior with an explicit opt-in:
```js// Before (implicit split, deprecated): const xml = new XMLSerializer().serializeToString(doc);
// After (explicit guard, spec-aligned): const xml = new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); // Throws InvalidStateError if any CDATASection contains ']]>' ```
Removal timeline
Both the `splitCDATASections` option and the underlying `]]>` → `]]]]><.
Related
Alias · CVE (1)
- CVE-2026-34601 xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) `DOMParser` and `XMLSerializer` module. vulnerability
Weakness · CWE (1)
- CWE-91 XML Injection weakness
References
- https://github.com/xmldom/xmldom/security/advisories/GHSA-wh4c-j3r5-mjhp web
- https://nvd.nist.gov/vuln/detail/CVE-2026-34601 advisory
- https://github.com/xmldom/xmldom/commit/2b852e836ab86dbbd6cbaf0537f584dd0b5ac184 web
- https://github.com/xmldom/xmldom package
- https://github.com/xmldom/xmldom/releases/tag/0.8.12 web
- https://github.com/xmldom/xmldom/releases/tag/0.9.9 web