# XML name validation bypass via line terminator

Anchored name/QName regexes with the m flag, and unvalidated name fields emitted verbatim into markup

_Area: Web injection and code execution. Seen in 3 disclosed reports; latest 2026-09-08. Updated 2026-11-07._

## How it works

XML serializers must reject names that would break out of the syntactic construct they are written into. Two assumptions break that check:

1. **Anchors are whole-string anchors.** A shared regexp builder compiles anchored QName productions (`^...$`) with the `m` flag. Under `m`, `^` and `$` match at line boundaries, so `.test(name)` only requires *one* conforming line. A name whose first line is valid, followed by a line terminator and breakout markup, passes validation and is then serialized verbatim [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).
2. **Every field in a construct is validated.** A hardening patch added `requireWellFormed` checks for `publicId`, `systemId` and `internalSubset` in the DOCTYPE path but never checked `name`, which is emitted verbatim between `<!DOCTYPE ` and `>` [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).

In both cases the validator says "safe" and the serializer writes attacker-controlled text into markup.

## Where it shows up

- Regex validators for XML names/QNames, especially ones built by a shared helper that sets flags globally [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).
- Any `^...$` pattern with the `m` flag used as a full-string matcher.
- Serializer code that concatenates a DOM property into markup: element names, attribute names, `DocumentType.name` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).
- `requireWellFormed` blocks that validate some sibling fields but not all [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Name-like fields that are plain writable own-properties, settable without going through a validating constructor [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Creation-time (`createElement`, `createDocumentType`, etc.) validation that differs from serialization-time validation [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).

## How to review

The invariant: **for every field written into a syntactic construct, the validated value and the emitted value are the same string, and the validator rejects any string containing a character that can terminate the construct.**

Check:
- Does the name regex use `m` (or `s`/`i` where it changes anchoring)? Test a name with a valid first line, `\n`, then `><script>`-style markup; it must be rejected [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).
- Enumerate every field emitted into each construct (element, attribute, DOCTYPE) and confirm each has a check in the same code path [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Confirm the property cannot be written directly, bypassing the constructor's validation [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Confirm creation-time and serialization-time validation share the same rules; a strict serializer option must not inherit the same blind spot [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).

## How finders spotted it

- Read the shared regexp builder and noticed anchored productions compiled with the `m` flag, so the full-string matcher was really per-line; hypothesized a multi-line name with one valid line would pass and confirmed with a control case [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md).
- Noticed a prior fix hardened `publicId`/`systemId`/`internalSubset` but left `name` unguarded in the same block, then checked whether `name` was writable and whether `createDocumentType` validated it [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Same `m`-flag observation, then tested the `create*` APIs with a line terminator plus breakout markup [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).

Habit: when auditing a validator, read the flag list of its regex builder before trusting its anchors.

## Variants seen

- Element/attribute name injection via `m`-flagged QName regex at serialization time [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md).
- DOCTYPE `name` injection via a missing field check [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md).
- Creation-time QName validation bypass, affecting the default serialization path with no serializer option to stop it [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md).

## Impact and severity

Markup injection into serialized XML; becomes XSS where output is rendered in an HTML context [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md). E2 bypasses the `requireWellFormed:true` mitigation recommended for prior xmldom injection CVEs, so it defeats the documented defense [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md). Severity rises with downstream HTML rendering and with fixes that leave sibling fields or alternate code paths open.

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **@xmldom/xmldom (npm)** = 0.9.11, fixed in 0.9.12: xmldom: requireWellFormed element/attribute name validation is bypassable via an embedded line terminator (CVE-2026-83617). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md. Fix: https://github.com/xmldom/xmldom/commit/7b2ec67e1750daadd0bb06c92e875e726544a362
- **@xmldom/xmldom (npm)** >= 0.7.0, <= 0.8.14, fixed in 0.8.15: xmldom: DocType `name` Injection Bypasses requireWellFormed (CVE-2026-83608). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md. Fix: https://github.com/xmldom/xmldom/commit/57aec90ac57b4408ae7c5d1746bf2a693b5ed90e
- **@xmldom/xmldom (npm)** >= 0.9.0, <= 0.9.11, fixed in 0.9.12: xmldom: Creation-time XML Name/QName validation is bypassable via an embedded line terminator, allowing injection on the (CVE-2026-83609). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md. Fix: https://github.com/xmldom/xmldom/commit/7b2ec67e1750daadd0bb06c92e875e726544a362

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-jxjr-3g7g-3944.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-27p8-2357-5qqv.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-3px3-54cx-rmw9.md
