Skip to content

xmldom has XML node injection through unvalidated comment serialization

High severity GitHub Reviewed Published Apr 18, 2026 in xmldom/xmldom • Updated Apr 22, 2026

Package

npm @xmldom/xmldom (npm)

Affected versions

< 0.8.13
>= 0.9.0, < 0.9.10

Patched versions

0.8.13
0.9.10
npm xmldom (npm)
<= 0.6.0
None

Description

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

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, 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

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.

References

@karfau karfau published to xmldom/xmldom Apr 18, 2026
Published to the GitHub Advisory Database Apr 22, 2026
Reviewed Apr 22, 2026
Last updated Apr 22, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v4 base metrics

Exploitability Metrics
Attack Vector Network
Attack Complexity Low
Attack Requirements None
Privileges Required None
User interaction None
Vulnerable System Impact Metrics
Confidentiality None
Integrity High
Availability None
Subsequent System Impact Metrics
Confidentiality None
Integrity None
Availability None

CVSS v4 base metrics

Exploitability Metrics
Attack Vector: This metric reflects the context by which vulnerability exploitation is possible. This metric value (and consequently the resulting severity) will be larger the more remote (logically, and physically) an attacker can be in order to exploit the vulnerable system. The assumption is that the number of potential attackers for a vulnerability that could be exploited from across a network is larger than the number of potential attackers that could exploit a vulnerability requiring physical access to a device, and therefore warrants a greater severity.
Attack Complexity: This metric captures measurable actions that must be taken by the attacker to actively evade or circumvent existing built-in security-enhancing conditions in order to obtain a working exploit. These are conditions whose primary purpose is to increase security and/or increase exploit engineering complexity. A vulnerability exploitable without a target-specific variable has a lower complexity than a vulnerability that would require non-trivial customization. This metric is meant to capture security mechanisms utilized by the vulnerable system.
Attack Requirements: This metric captures the prerequisite deployment and execution conditions or variables of the vulnerable system that enable the attack. These differ from security-enhancing techniques/technologies (ref Attack Complexity) as the primary purpose of these conditions is not to explicitly mitigate attacks, but rather, emerge naturally as a consequence of the deployment and execution of the vulnerable system.
Privileges Required: This metric describes the level of privileges an attacker must possess prior to successfully exploiting the vulnerability. The method by which the attacker obtains privileged credentials prior to the attack (e.g., free trial accounts), is outside the scope of this metric. Generally, self-service provisioned accounts do not constitute a privilege requirement if the attacker can grant themselves privileges as part of the attack.
User interaction: This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable system. This metric determines whether the vulnerability can be exploited solely at the will of the attacker, or whether a separate user (or user-initiated process) must participate in some manner.
Vulnerable System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the VULNERABLE SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the VULNERABLE SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the VULNERABLE SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
Subsequent System Impact Metrics
Confidentiality: This metric measures the impact to the confidentiality of the information managed by the SUBSEQUENT SYSTEM due to a successfully exploited vulnerability. Confidentiality refers to limiting information access and disclosure to only authorized users, as well as preventing access by, or disclosure to, unauthorized ones.
Integrity: This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. Integrity of the SUBSEQUENT SYSTEM is impacted when an attacker makes unauthorized modification of system data. Integrity is also impacted when a system user can repudiate critical actions taken in the context of the system (e.g. due to insufficient logging).
Availability: This metric measures the impact to the availability of the SUBSEQUENT SYSTEM resulting from a successfully exploited vulnerability. While the Confidentiality and Integrity impact metrics apply to the loss of confidentiality or integrity of data (e.g., information, files) used by the system, this metric refers to the loss of availability of the impacted system itself, such as a networked service (e.g., web, database, email). Since availability refers to the accessibility of information resources, attacks that consume network bandwidth, processor cycles, or disk space all impact the availability of a system.
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

EPSS score

Weaknesses

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. Learn more on MITRE.

CVE ID

CVE-2026-41672

GHSA ID

GHSA-j759-j44w-7fr8

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.