GitHub Advisoryadvisory · npm
GHSA-27p8-2357-5qqv xmldom: DocType `name` Injection Bypasses requireWellFormed
HIGH
Official page ↗
- Aliases
- CVE-2026-83608
- 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
The `@xmldom/xmldom` serializer emits `DocumentType.name` verbatim into the
`` declaration with no well-formedness guard. GHSA-f6ww-3ggp-fr8h
(CVE-2026-41674) hardened the serializer's `requireWellFormed` path for a
DocumentType's sibling fields — `publicId`, `systemId`, and `internalSubset` —
but it did not add any check for `name`. A `>` (or whitespace) in the name
terminates the doctype declaration early, letting the remaining characters
become sibling markup in the serialized output.
Because `requireWellFormed: true` — the recommended mitigation for the prior
xmldom injection CVEs — performs no validation on the DocType `name`, this is a
bypass of that control, in the same family as the open element-name
(GHSA-w2rr-34g9-rvrj) and attribute-name (GHSA-4w3w-2rp5-g8jm) name-injection advisories.
Details
The serializer's `DOCUMENT_TYPE_NODE` case runs the `requireWellFormed` block
only against `publicId`, `systemId`, and `internalSubset`, then pushes
`n.name` directly into the buffer between the ``:
• 0.9.x (v0.9.10, `bb7a085`):
serializer DocType case, `lib/dom.js#L3256-L3283`
— the `requireWellFormed` block (#L3259-L3269)
validates `publicId`/`systemId`/`internalSubset` but not `name`, which is
emitted verbatim at #L3270.
• 0.8.x (v0.8.13, `e5c1480`):
serializer DocType case, `lib/dom.js#L1914-L1946`
— same structure; `name` is emitted verbatim at #L1928.
• unscoped `xmldom` (v0.6.0, `c80a161`):
`lib/dom.js#L1105`
emits `node.name` verbatim; this line predates `requireWellFormed`, so there
is no well-formedness path at all.
Enabling write paths
`DocumentType.name` is a plain, writable own-property, so the enabling vector
differs by line:
• 0.9.x — `createDocumentType()` validates the name via
`validateQualifiedName` (`lib/dom.js#L925-L936`,
validation at #L926),
so the deliverable vector is a direct property write
(`dt.name = 'html>…'`) to the unguarded own-property.
• 0.8.x — `createDocumentType()` does not validate the name
(`lib/dom.js#L456-L464`),
so the malicious name is reachable directly through `createDocumentType()` as
well as via direct property write.
• unscoped `xmldom` (`.
Proof of Concept
Run against `@xmldom/xmldom` v0.9.10 (commit `bb7a085`):
```javascript
const { DOMImplementation, XMLSerializer, DOMParser } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const serializer = new XMLSerializer();
// 0.9.x createDocumentType validates the name, so overwrite it via direct property write
const dt = impl.createDocumentType('html', '', '');
dt.name = 'html>alert(1)alert(1)
//
// requireWellFormed: true did NOT prevent the injection (no exception thrown).
// The injected is well-formed XHTML that a browser would execute.
```
Confirmed runtime behavior:
• 0.9.x — `createDocumentType()` rejects the malicious name at creation
(`InvalidCharacterError`); a direct write to `dt.name` bypasses that, and
`serializeToString(…, { requireWellFormed: true })` emits the breakout with no
exception.
• Re-parse confirmation — re-parsing the output shows the injected
`` is a real second top-level element originating entirely from the
DocType name: the parser rejects it with
`HierarchyRequestError: Only one element can be added and only after doctype`.
A comment-injection variant (`dt.name = 'html>`) re-parses cleanly and the injected
comment node is enumerable, confirming the injected node is structurally live.
• 0.8.x (v0.8.13, `e5c1480`) — `createDocumentType('html>…', '', '')`
accepts the malicious name directly (no creation-time validation), and
`serializeToString(doc, null, null, { requireWellFormed: true })` produces
`alert(1)` with no exception.
A browser reproduction does not apply: browsers keep `DocumentType.name`
`readonly`, so the direct-write vector cannot be reproduced in a browser DOM.
The injection is specific to xmldom exposing `name` as writable and serializing
it without a guard.
Impact
Applications that build a `DocumentType` node with an attacker-influenced
`name` — via direct property write on any affected line, or via
`createDocumentType()` on 0.8.x and the unscoped package — and serialize the
document are vulnerable to XML/markup injection:
• XML structure injection — breaking out of the `` declaration
to inject arbitrary sibling elements, comments, or additional markup into the
output.
• XSS via XHTML — if the serialized output is served as XHTML or processed
by a browser-based XML parser, an injected `` element (in the XHTML
namespace) executes.
• requireWellFormed bypass — applications that adopted
`requireWellFormed: true` as a mitigation for the prior injection CVEs
(including the sibling DocType fields fixed by GHSA-f6ww-3ggp-fr8h) remain
vulnerable through the DocType `name`.
Fix Applied
Under `requireWellFormed`, the serializer validates the DocType `name` as a well-formed XML
`Name` and throws `InvalidStateError` when it is not — matching the sibling
`publicId`/`systemId`/`internalSubset` checks. Non-breaking and opt-in; ships on both maintained versions. No
creation-time change is made: 0.9.x already validates the name at `createDocumentType`, and the
0.8.x/unscoped creation gap cannot be closed without a breaking change, so it is left
unfixed. 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()` …
Related
Alias · CVE (1)
- CVE-2026-83608 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-27p8-2357-5qqv web
- https://nvd.nist.gov/vuln/detail/CVE-2026-83608 advisory
- https://github.com/xmldom/xmldom/pull/1071 web
- https://github.com/xmldom/xmldom/pull/1072 web
- https://github.com/xmldom/xmldom/commit/57aec90ac57b4408ae7c5d1746bf2a693b5ed90e web
- https://github.com/xmldom/xmldom/commit/85f12eb4d14b44de33216cfb72b50af4d24e9fdd 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