Back to Blog
high SEVERITY7 min read

How XML Node Injection happens in JavaScript XML parsing and how to fix it

CVE-2026-41672 is a high-severity XML node injection vulnerability in the `@xmldom/xmldom` package, caused by insufficient validation during comment serialization that allows attackers to inject arbitrary XML nodes into a document. The fix upgrades `@xmldom/xmldom` from version 0.8.12 to 0.8.13 (and 0.9.x to 0.9.10), closing the injection path by tightening how untrusted comment content is handled before it reaches the serializer.

O
By Orbis AppSec
Published August 5, 2026Reviewed August 5, 2026

Answer Summary

CVE-2026-41672 is a high-severity XML node injection vulnerability (CWE-91: XML Injection) in the `@xmldom/xmldom` JavaScript library. The root cause is that comment node content was serialized without validating or escaping the `--` sequence, allowing an attacker who controls comment text to break out of the comment and inject arbitrary XML nodes. The fix is to upgrade `@xmldom/xmldom` to version 0.8.13 or 0.9.10, which adds strict validation that rejects or sanitizes comment content containing `--` before serialization. In this project the upgrade was applied via `package-lock.json` and a `package.json` `overrides` entry to ensure the patched version is used throughout the dependency tree.

Vulnerability at a Glance

cweCWE-91 (XML Injection)
fixUpgrade `@xmldom/xmldom` to 0.8.13 / 0.9.10, which validates comment content before serialization and rejects strings containing `--`
riskAttacker-controlled comment text can escape the comment boundary and inject arbitrary XML nodes, potentially altering document structure, bypassing security checks, or corrupting downstream XML processing
languageJavaScript / Node.js
root cause`@xmldom/xmldom` 0.8.12 serialized comment node content without rejecting the `--` sequence that terminates an XML comment, enabling comment-escape injection
vulnerabilityXML Node Injection via unvalidated comment serialization

How XML Node Injection Happens in JavaScript XML Parsing and How to Fix It

The Incident

In the project's package-lock.json, the dependency @xmldom/xmldom was pinned to version 0.8.12 — a version carrying CVE-2026-41672, a high-severity XML node injection flaw. The Trivy scanner flagged the package in the dependency tree, and Orbis AppSec automatically opened a pull request upgrading it to 0.8.13 (and adding a package.json overrides entry to lock the patched version across the entire dependency graph).

This post explains exactly what the vulnerability is, how comment serialization becomes an injection vector, and what the two-file fix actually does.


The Vulnerability Explained

XML Comments and the -- Problem

The XML specification (§2.5) states that a comment looks like this:

<!-- this is a comment -->

It also explicitly forbids the string -- (double hyphen) from appearing inside comment content, because the parser uses --> to close a comment and -- anywhere else is illegal — but many real-world parsers are lenient and will treat -- as ending the comment early.

@xmldom/xmldom 0.8.12 serialized comment nodes by taking the raw text content and wrapping it in <!-- / --> without first checking whether that content contained --. This means that if an application built a comment node from user-controlled input — for example:

// Vulnerable pattern using @xmldom/xmldom 0.8.12
const doc = new DOMParser().parseFromString('<root/>', 'text/xml');
const comment = doc.createComment(userInput); // userInput is attacker-controlled
doc.documentElement.appendChild(comment);
const serialized = new XMLSerializer().serializeToString(doc);

…and userInput was something like:

safe text --> <script>evil()</script> <!--

The serialized output would become:

<root><!-- safe text --> <script>evil()</script> <!----></root>

The --> in the comment content closes the comment early, and <script>evil()</script> is now a real XML element in the serialized document — not comment text.

Why This Is Dangerous in Practice

Any downstream consumer of the serialized XML — another XML parser, an XSLT processor, an XML-based access-control policy engine, a SOAP handler — will see the injected nodes as legitimate document content. Depending on the application, this can:

  • Bypass XML-based authorization checks (e.g., injecting a <role>admin</role> node)
  • Corrupt SOAP or SAML payloads processed by a security-sensitive service
  • Introduce executable content in contexts where XML is further processed (e.g., SVG, XHTML)
  • Break XML schema validation or cause denial-of-service in strict parsers

The vulnerable version in this project was 0.8.12, recorded in package-lock.json:

"node_modules/@xmldom/xmldom": {
  "version": "0.8.12",
  "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
  "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg=="
}

The Fix

Two Files, Two Complementary Changes

The pull request modifies exactly two files: package-lock.json and package.json. Each plays a distinct role.

1. package-lock.json — Pinning the Patched Version

The lock file update swaps the resolved tarball and its integrity hash from 0.8.12 to 0.8.13:

 "node_modules/@xmldom/xmldom": {
-  "version": "0.8.12",
-  "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
-  "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
+  "version": "0.8.13",
+  "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
+  "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
   "license": "MIT",
   "engines": {
     "node": ">=10.0.0"

The new integrity hash ensures npm ci will reject any tampered tarball — an important supply-chain safeguard.

2. package.json — The overrides Entry

Simply updating the lock file is not always enough. If another dependency in the tree declares @xmldom/xmldom as its own dependency with a range that resolves to 0.8.12, npm could install a second copy of the vulnerable version. The overrides field forces npm to use 0.8.13 for every occurrence of @xmldom/xmldom in the entire dependency tree:

-  ]
+  ],
+  "overrides": {
+    "@xmldom/xmldom": "0.8.13"
+  }
 }

This is the correct pattern for remediating transitive dependency vulnerabilities in npm projects: the lock file pins the direct resolution, and overrides enforces it transitively.

What Changed Inside @xmldom/xmldom 0.8.13

The upstream patch in 0.8.13 adds a guard in the comment serialization path that checks whether the comment's text content contains --. If it does, serialization throws an error rather than producing malformed — and potentially injectable — XML. Valid comment content (containing no --) is completely unaffected, which is why the PR description notes that "it only tightens handling of untrusted input and leaves valid inputs unaffected."


Prevention & Best Practices

1. Validate Comment Content Before Creating Comment Nodes

If your application constructs XML comment nodes from any external input, validate the content before passing it to createComment():

function safeCreateComment(doc, text) {
  // XML comments must not contain '--' (double hyphen)
  if (text.includes('--')) {
    throw new Error('Invalid comment content: "--" is not allowed in XML comments');
  }
  return doc.createComment(text);
}

2. Use npm overrides for Transitive Vulnerabilities

When a vulnerable package is a transitive dependency (pulled in by another package, not directly by your code), use overrides in package.json to force the patched version:

"overrides": {
  "@xmldom/xmldom": ">=0.8.13"
}

Using a range (>=0.8.13) rather than an exact version allows future patch releases to be picked up automatically.

3. Automate Dependency Scanning in CI

Add Trivy, Snyk, or npm audit to your CI pipeline so that newly published CVEs are caught before they reach production:

# Example GitHub Actions step
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'

4. Keep package-lock.json in Version Control

A committed lock file means every developer and CI run installs exactly the same dependency versions. Without it, npm install may silently resolve to a vulnerable version even after you've patched your package.json.

5. Relevant Standards

  • CWE-91: XML Injection — covers injection of malicious content into XML documents
  • OWASP — XML Security Cheat Sheet: recommends validating all XML input and using schema validation
  • XML Specification §2.5: defines the legal grammar for XML comments and explicitly prohibits -- in comment content

Key Takeaways

  • @xmldom/xmldom 0.8.12 did not validate the -- sequence in comment content, allowing a single injected string to break out of a comment node and introduce arbitrary XML elements into a serialized document.
  • The package-lock.json change alone is insufficient when the vulnerable package also appears as a transitive dependency; the package.json overrides entry is what guarantees the patched version is used everywhere.
  • XML comment injection is a real, exploitable attack vector — not just a theoretical flaw — because serialized XML is often consumed by security-sensitive downstream systems (SAML processors, XSLT engines, SOAP handlers).
  • Trivy detected this vulnerability statically from the version string in package-lock.json, demonstrating that dependency-tree scanning catches issues even before a code-path trace is confirmed.
  • Always treat XML comment text as untrusted input and reject or escape -- before passing it to any XML serializer.

How Orbis AppSec Detected This

  • Source: The @xmldom/xmldom package version recorded in package-lock.json (version 0.8.12) — a value that enters the dependency tree at install time and may originate from any caller that passes user-controlled strings to createComment().
  • Sink: The comment serialization path inside @xmldom/xmldom's XMLSerializer implementation, which writes raw comment content between <!-- and --> without validating the -- sequence.
  • Missing control: No check for the -- substring in comment node content prior to serialization, violating the XML specification's §2.5 constraint.
  • CWE: CWE-91 — XML Injection
  • Fix: Upgraded @xmldom/xmldom from 0.8.12 to 0.8.13 in package-lock.json and added an overrides entry in package.json to enforce the patched version across the full dependency tree.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-41672 is a sharp reminder that XML is not a safe serialization format by default — even something as innocuous-looking as a comment node can become an injection vector when library-level validation is absent. The @xmldom/xmldom 0.8.12 → 0.8.13 upgrade closes this specific path by enforcing the XML specification's prohibition on -- inside comment content.

For Node.js projects, the two-file fix pattern demonstrated here — updating package-lock.json and adding a package.json overrides entry — is the correct way to remediate transitive dependency vulnerabilities. Pair that with automated scanning in CI and you'll catch the next CVE before it ever ships.


References

Frequently Asked Questions

What is XML node injection?

XML node injection is an attack where an adversary supplies malicious text that breaks out of its intended XML context — such as a comment or attribute — and introduces new, unintended XML nodes or markup into a document, altering its structure or meaning.

How do you prevent XML node injection in JavaScript?

Always validate or sanitize data before inserting it into XML structures; use a well-maintained XML library that enforces the XML specification (e.g., rejecting `--` inside comments), and keep dependencies up to date so that known CVEs like CVE-2026-41672 are patched promptly.

What CWE is XML node injection?

XML node injection maps to CWE-91 (XML Injection), which covers cases where user-controlled data is incorporated into XML without sufficient validation, allowing structural manipulation of the document.

Is HTML-encoding enough to prevent XML node injection?

Not always. HTML-encoding targets a different character set than XML validation rules. For XML comments specifically, the dangerous sequence `--` consists of ordinary ASCII characters that HTML encoding does not transform, so a library-level fix that rejects `--` in comment content is required.

Can static analysis detect XML node injection?

Yes. Tools like Trivy (which flagged this exact vulnerability), Semgrep, and Snyk can identify known-vulnerable versions of `@xmldom/xmldom` in a dependency tree and, in some cases, trace tainted data flow into XML serialization sinks.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #32

Related Articles

high

How Denial of Service via Exponential Time Complexity happens in brace-expansion and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-13149) was discovered in the brace-expansion npm package, where specially crafted input patterns could trigger exponential time complexity, potentially freezing Node.js applications. The fix upgrades multiple versions of brace-expansion (1.1.18 → 1.1.16, 2.1.1 → 2.1.2, and 5.0.6 → 5.0.7) through yarn resolutions to ensure all dependency paths use patched versions.

high

How Remote Code Execution via serialize-javascript happens in Node.js and how to fix it

The `serialize-javascript` package version 6.0.2 contained a high-severity Remote Code Execution (RCE) vulnerability (GHSA-5c6j-r48x-rmvq) exploitable through crafted `RegExp.flags` and `Date.prototype.toISOString()` payloads. Upgrading to version 7.0.3 eliminates the vulnerable serialization logic and removes the `randombytes` dependency that was part of the attack surface. This fix was applied via a `package.json` override and `package-lock.json` update.

high

How Unicode Normalization Infinite Loops Happen in Go and How to Fix CVE-2026-56852

CVE-2026-56852 is a high-severity vulnerability in golang.org/x/text that allows the Unicode normalization iterator to enter an infinite loop when processing specially crafted input. This fix upgrades the dependency from v0.37.0 to v0.39.0, tightening input validation and preventing denial-of-service attacks in applications that process untrusted Unicode text.

high

How denial of service via malformed HTTP header decoding happens in Node.js OpenTelemetry and how to fix it

A high-severity denial of service vulnerability (CVE-2026-59892) was discovered in the @opentelemetry/propagator-jaeger package, where malformed HTTP headers could crash Node.js applications. The fix involved upgrading from version 2.8.0 to 2.9.0, which includes proper input validation for Jaeger trace context headers.

high

How API key exposure and ReDoS happens in Node.js and how to fix it

A critical vulnerability in `roll/openai.js` could expose OpenAI API keys to client-side JavaScript bundles, allowing attackers to extract secrets from browser developer tools. Additionally, a Regular Expression Denial of Service (ReDoS) pattern in the `generateErrorMessage()` method could crash the process. Both issues were fixed with targeted, minimal code changes.