GitHub Advisoryadvisory · npm

GHSA-27p8-2357-5qqv xmldom: DocType `name` Injection Bypasses requireWellFormed

HIGH Published 2026-09-08 Modified 2026-09-08 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