CVE-2026-41672: @xmldom/xmldom Xmldom Has XML Node Injection Vulnerability

HIGH
Advisory Identifier: GHSA-j759-j44w-7fr8
CWE-91
Published
Advisory Summary

CVE-2026-41672 is a HIGH xmldom has xml node injection vulnerability (CWE-91: XML Injection (aka Blind XPath Injection)) affecting @xmldom/xmldom libraries in the npm ecosystem. The issue involves xmldom has xml node injection through unvalidated comment serialization. Remediation is available by upgrading affected packages to version 0.8.13, 0.9.10.

CVE-2026-41672 at a Glance

CVE Identifier CVE-2026-41672
GHSA Identifier GHSA-j759-j44w-7fr8
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-91: XML Injection (aka Blind XPath Injection)
Affected Product @xmldom/xmldom
Package Ecosystem npm
Published Date
Last Updated

How to Fix CVE-2026-41672

To remediate this vulnerability, upgrade all affected @xmldom/xmldom dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

@xmldom/xmldom fixed: 0.8.13
npm install @xmldom/xmldom@0.8.13
@xmldom/xmldom fixed: 0.9.10
npm install @xmldom/xmldom@0.9.10
xmldom fixed: Latest secure release
npm install xmldom@Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls @xmldom/xmldom

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-41672, inspect your project dependencies using npm:

npm ls @xmldom/xmldom

Analyze the terminal output to locate any version of @xmldom/xmldom in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.8.13), your application is affected.

Note: The npm ls @xmldom/xmldom command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-91: XML Injection (aka Blind XPath Injection), where the product does not properly neutralize special elements that are used in xml, allowing attackers to modify the syntax, content, or commands of the xml before it is processed by an end system. In @xmldom/xmldom, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The package allows attacker-controlled comment content to be serialized into XML without validating or neutralizing comment breaking sequences. As a result, an attacker can terminate the comment early and inject arbitrary XML nodes into the serialized output. --- ## Details The issue is in the DOM construction and serialization flow for comment nodes. When `createComment(data)` is called, the supplied string is stored as comment data through the generic character-data handling path. That content is kept as-is. Later, when the document is serialized, the serializer writes comment nodes by concatenating the XML comment delimiters with the stored `node.data` value directly. That behavior is unsafe because XML comments are a syntax-sensitive context. If attacker-controlled input contains a sequence that closes the comment, the serializer does not preserve it as literal comment text. Instead, it emits output where the remainder of the payload is treated as live XML markup. This is a real injection bug, not a formatting issue. The serializer already applies context-aware handling in other places, such as escaping text nodes and rewriting unsafe CDATA terminators. Comment content does not receive equivalent treatment. Because of that gap, untrusted data can break out of the comment boundary and modify the structure of the final XML document. --- ## PoC ```js const { DOMImplementation, DOMParser, XMLSerializer } = require('@xmldom/xmldom'); const doc = new DOMImplementation().createDocument(null, 'root', null); doc.documentElement.appendChild( doc.createComment('--><injected attr="1"/><!--') ); const xml = new XMLSerializer().serializeToString(doc); console.log(xml); // <root><!----><injected attr="1"/><!----></root> const reparsed = new DOMParser().parseFromString(xml, 'text/xml'); console.log(reparsed.documentElement.childNodes.item(1).nodeName); // injected ``` --- ## Impact An application that uses the package to build XML from untrusted input can be made to emit attacker-controlled elements outside the intended comment boundary. That allows the attacker to alter the meaning and structure of generated XML documents. In practice, this can affect any workflow that generates XML and then stores it, forwards it, signs it, or hands it to another parser. Realistic targets include XML-based configuration, policy documents, and message formats where downstream consumers trust the serialized structure. --- ## Disclosure This vulnerability was publicly disclosed at 2026-04-06T11:25:07Z via [xmldom/xmldom#987](https://github.com/xmldom/xmldom/pull/987), which was subsequently closed without being merged. --- ## Fix Applied > **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain > vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that pass > untrusted data to `createComment()` or mutate comment nodes with untrusted input (via > `appendData`, `insertData`, `replaceData`, `.data =`, or `.textContent =`) should audit all > `serializeToString()` call sites and add the option. `XMLSerializer.serializeToString()` now accepts an options object as a second argument. When `{ requireWellFormed: true }` is passed, the serializer throws `InvalidStateError` before emitting a Comment node whose `.data` would produce malformed XML. On `@xmldom/xmldom` ≥ 0.9.10, the full W3C DOM Parsing §3.2.1.4 check is applied: throws if `.data` contains `--` anywhere, ends with `-`, or contains characters outside the XML Char production. On `@xmldom/xmldom` ≥ 0.8.13 (LTS), only the `-->` injection sequence is checked. The `0.8.x` SAX parser accepts comments containing `--` (without `>`), so throwing on bare `--` would break a previously-working round-trip on that branch. The `-->` check is sufficient to prevent injection. ### PoC — fixed path ```js const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom'); const doc = new DOMImplementation().createDocument(null, 'root', null); doc.documentElement.appendChild(doc.createComment('--><injected attr="1"/><!--')); // Default (unchanged): verbatim — injection present const unsafe = new XMLSerializer().serializeToString(doc); console.log(unsafe); // <root><!----><injected attr="1"/><!----></root> // Opt-in guard: throws InvalidStateError before serializing try { new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); } catch (e) { console.log(e.name, e.message); // InvalidStateError: The comment node data contains "--" or ends with "-" (0.9.x) // InvalidStateError: The comment node data contains "-->" (0.8.x — only --> is checked) } ``` ### Why the default stays verbatim The W3C DOM Parsing and Serialization spec §3.2.1.4 defines a `require well-formed` flag whose **default value is `false`**. With the flag unset, the spec explicitly permits serializing ill-formed comment content verbatim — this is also the behavior of browser implementations (Chrome, Firefox, Safari): `new XMLSerializer().serializeToString(doc)` produces the injection sequence without error in all major browsers. Unconditionally throwing would be a behavioral breaking change with no spec justification. The opt-in `requireWellFormed: true` flag allows applications that require injection safety to enable strict mode without breaking existing deployments. ### Residual limitation The fix operates at serialization time only. There is no creation-time check in `createComment` — the spec does not require one for comment data. Any path that leads to a Comment node with `--` in its data (`createComment`, `appendData`, `.data =`, etc.) produces a node that serializes safely only when `{ requireWellFormed: true }` is passed to `serializeToString`.

CVE-2026-41672 Affected @xmldom/xmldom Versions

3 component(s)
Fixed Versions:
0.8.130.9.10
Product / Package Ecosystem Affected Versions Fixed Version
@xmldom/xmldom
npm
< 0.8.13
0.8.13
@xmldom/xmldom
npm
≥ 0.9.0
< 0.9.10
0.9.10
xmldom
npm
≤ 0.6.0

References

Frequently Asked Questions

What is CVE-2026-41672?
CVE-2026-41672 is a security vulnerability in @xmldom/xmldom involving xmldom has XML node injection through unvalidated comment serialization under CWE-91: XML Injection (aka Blind XPath Injection).
Which @xmldom/xmldom versions are affected?
Affected versions include releases prior to 0.8.13, 0.9.10 across affected packages: @xmldom/xmldom, xmldom.
What version fixes CVE-2026-41672?
Upgrading to 0.8.13, 0.9.10 (or the latest patched release for your respective branch) resolves the vulnerability.
Is @xmldom/xmldom affected?
Yes, @xmldom/xmldom is affected in releases prior to 0.8.13, 0.9.10. Upgrade your dependency to 0.8.13, 0.9.10 or later.
Is xmldom affected?
Yes, xmldom is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What CWE is associated with CVE-2026-41672?
CVE-2026-41672 is classified under CWE-91: XML Injection (aka Blind XPath Injection) (The product does not properly neutralize special elements that are used in XML, allowing attackers to modify the syntax, content, or commands of the XML before it is processed by an end system.).
How can I check npm dependencies for CVE-2026-41672?
Run the dependency tree command: 'npm ls @xmldom/xmldom'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-j759-j44w-7fr8",
  "modified" : "2026-05-08T20:10:07Z",
  "published" : "2026-04-22T20:16:07Z",
  "aliases" : [ "CVE-2026-41672" ],
  "summary" : "xmldom has XML node injection through unvalidated comment serialization",
  "details" : "## Summary\n\nThe package allows attacker-controlled comment content to be serialized into XML without validating or neutralizing comment breaking sequences. As a result, an attacker can terminate the comment early and inject arbitrary XML nodes into the serialized output.\n\n---\n\n## Details\n\nThe issue is in the DOM construction and serialization flow for comment nodes.\n\nWhen `createComment(data)` is called, the supplied string is stored as comment data through the generic character-data handling path. That content is kept as-is. Later, when the document is serialized, the serializer writes comment nodes by concatenating the XML comment delimiters with the stored `node.data` value directly.\n\nThat behavior is unsafe because XML comments are a syntax-sensitive context. If attacker-controlled input contains a sequence that closes the comment, the serializer does not preserve it as literal comment text. Instead, it emits output where the remainder of the payload is treated as live XML markup.\n\nThis is a real injection bug, not a formatting issue. The serializer already applies context-aware handling in other places, such as escaping text nodes and rewriting unsafe CDATA terminators. Comment content does not receive equivalent treatment. Because of that gap, untrusted data can break out of the comment boundary and modify the structure of the final XML document.\n\n---\n\n## PoC\n\n```js\nconst { DOMImplementation, DOMParser, XMLSerializer } = require('@xmldom/xmldom');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\n\ndoc.documentElement.appendChild(\n  doc.createComment('--><injected attr=\"1\"/><!--')\n);\n\nconst xml = new XMLSerializer().serializeToString(doc);\nconsole.log(xml);\n// <root><!----><injected attr=\"1\"/><!----></root>\n\nconst reparsed = new DOMParser().parseFromString(xml, 'text/xml');\nconsole.log(reparsed.documentElement.childNodes.item(1).nodeName);\n// injected\n```\n\n---\n\n## Impact\n\nAn application that uses the package to build XML from untrusted input can be made to emit attacker-controlled elements outside the intended comment boundary. That allows the attacker to alter the meaning and structure of generated XML documents.\n\nIn practice, this can affect any workflow that generates XML and then stores it, forwards it, signs it, or hands it to another parser. Realistic targets include XML-based configuration, policy documents, and message formats where downstream consumers trust the serialized structure.\n\n---\n\n## Disclosure\n\nThis vulnerability was publicly disclosed at 2026-04-06T11:25:07Z via [xmldom/xmldom#987](https://github.com/xmldom/xmldom/pull/987), which was subsequently closed without being merged.\n\n---\n\n## Fix Applied\n\n> **⚠ Opt-in required.** Protection is not automatic. Existing serialization calls remain\n> vulnerable unless `{ requireWellFormed: true }` is explicitly passed. Applications that pass\n> untrusted data to `createComment()` or mutate comment nodes with untrusted input (via\n> `appendData`, `insertData`, `replaceData`, `.data =`, or `.textContent =`) should audit all\n> `serializeToString()` call sites and add the option.\n\n`XMLSerializer.serializeToString()` now accepts an options object as a second argument. When `{ requireWellFormed: true }` is passed, the serializer throws `InvalidStateError` before emitting a Comment node whose `.data` would produce malformed XML.\n\nOn `@xmldom/xmldom` ≥ 0.9.10, the full W3C DOM Parsing §3.2.1.4 check is applied: throws if `.data` contains `--` anywhere, ends with `-`, or contains characters outside the XML Char production.\n\nOn `@xmldom/xmldom` ≥ 0.8.13 (LTS), only the `-->` injection sequence is checked. The `0.8.x` SAX parser accepts comments containing `--` (without `>`), so throwing on bare `--` would break a previously-working round-trip on that branch. The `-->` check is sufficient to prevent injection.\n\n### PoC — fixed path\n\n```js\nconst { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');\n\nconst doc = new DOMImplementation().createDocument(null, 'root', null);\ndoc.documentElement.appendChild(doc.createComment('--><injected attr=\"1\"/><!--'));\n\n// Default (unchanged): verbatim — injection present\nconst unsafe = new XMLSerializer().serializeToString(doc);\nconsole.log(unsafe);\n// <root><!----><injected attr=\"1\"/><!----></root>\n\n// Opt-in guard: throws InvalidStateError before serializing\ntry {\n  new XMLSerializer().serializeToString(doc, { requireWellFormed: true });\n} catch (e) {\n  console.log(e.name, e.message);\n  // InvalidStateError: The comment node data contains \"--\" or ends with \"-\"  (0.9.x)\n  // InvalidStateError: The comment node data contains \"-->\"  (0.8.x — only --> is checked)\n}\n```\n\n### Why the default stays verbatim\n\nThe W3C DOM Parsing and Serialization spec §3.2.1.4 defines a `require well-formed` flag whose **default value is `false`**. With the flag unset, the spec explicitly permits serializing ill-formed comment content verbatim — this is also the behavior of browser implementations (Chrome, Firefox, Safari): `new XMLSerializer().serializeToString(doc)` produces the injection sequence without error in all major browsers.\n\nUnconditionally throwing would be a behavioral breaking change with no spec justification. The opt-in `requireWellFormed: true` flag allows applications that require injection safety to enable strict mode without breaking existing deployments.\n\n### Residual limitation\n\nThe fix operates at serialization time only. There is no creation-time check in `createComment` — the spec does not require one for comment data. Any path that leads to a Comment node with `--` in its data (`createComment`, `appendData`, `.data =`, etc.) produces a node that serializes safely only when `{ requireWellFormed: true }` is passed to `serializeToString`.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "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"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "@xmldom/xmldom"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.8.13"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "@xmldom/xmldom"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0.9.0"
      }, {
        "fixed" : "0.9.10"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "xmldom"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "0.6.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/security/advisories/GHSA-j759-j44w-7fr8"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-41672"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/pull/987"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/commit/b397540889086da868c30c366ad5c220d1a750c7"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/commit/fda7cc313de30243fea35cada64e0bb12099c2a1"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/xmldom/xmldom"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/releases/tag/0.8.13"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/xmldom/xmldom/releases/tag/0.9.10"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-91" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-04-22T20:16:07Z",
    "nvd_published_at" : "2026-05-07T04:16:33Z"
  }
}