# Sanitizer denylist bypass via parser quirks

Sanitizers matching bare tag names or one HTTP method/header while the real parse or fetch differs

_Area: Web injection and code execution. Seen in 3 disclosed reports; latest 2026-04-01. Updated 2026-11-02._

## How it works
A sanitizer decides safety by comparing what it sees against a list of known-bad names or by checking a resource one way and using it another. The assumption that breaks is that the sanitizer's view of the input equals what the downstream parser or fetcher will actually do. Parsers keep namespace prefixes in the qualified tag name, so a blocklist of bare local names (`script`) misses `x:script`. HTML parsers also mutate markup (MathML/SVG foreign content reinterprets `<style>`), so a string that looked inert when checked becomes executable when reparsed. And a check performed with one HTTP method or `Accept` header does not bind the later fetch, so the server can answer differently.

## Where it shows up
- Blocklists comparing lowercased tag names without stripping or rejecting namespace prefixes (`x:`, `svg:`, `math:`).
- Sanitizers that operate on a string or DOM snapshot but output is later reparsed by a different parser (XML/SVG served as `image/svg+xml`).
- Rich-text editors sanitizing pasted HTML, especially `data-*` attachment JSON that is re-rendered.
- MathML/SVG foreign-content constructs (`<math><mtext><table><mglyph><style>`) that reparse into executable elements.
- Fixes that validate a remote URL with HEAD but fetch with GET; sanitization that branches on `Accept`.
- Output served without CSP, or with permissive CORS (`Access-Control-Allow-Origin: *`).

## How to review
The invariant: the exact token the sanitizer approves must be the exact token the final parser executes, and the resource checked must be the resource used. Check that the sanitizer compares against the parser's real output (qualified names, post-mutation tree), not a pre-parse string or local name. Confirm namespace prefixes are rejected or normalized, and that output is re-serialized and re-parsed before trust. For remote fetches, require the same method and headers for validation and retrieval, or validate the fetched bytes themselves. Verify CSP and CORS do not amplify a surviving payload.

## How finders spotted it
- Read the SanitizeSVG fix and noticed the blocklist compared `c.Data` to bare tag names; tested the parser and saw prefixed elements keep the prefix in `Node.Data` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3.md).
- Recalled a known MathML mutation bypass of DOMPurify and hypothesized Trix's sanitizer shared the weakness, delivering it via copy-paste attachments [E2](https://bugbountylibrary.com/r/h1%3A2819573.md).
- Noticed the patch validated Content-Type with HEAD, hypothesized method-divergent responses, and observed Safari's `Accept: */*` skipped sanitization [E3](https://bugbountylibrary.com/r/h1%3A2106708.md).

## Variants seen
- Namespace-prefixed tag bypass of a Go HTML5-parser-based SVG sanitizer [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3.md).
- MathML mutation XSS through a rich-text editor's paste/attachment path [E2](https://bugbountylibrary.com/r/h1%3A2819573.md).
- Method-dependent (HEAD vs GET) and `Accept`-dependent sanitization bypass in a resource proxy [E3](https://bugbountylibrary.com/r/h1%3A2106708.md).

## Impact and severity
E1 gave unauthenticated same-origin script execution; combined with `Access-Control-Allow-Origin: *` it reached full API access with the victim's session, enabling data theft and settings changes [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3.md). E2 executed arbitrary JS in the user's session via stored XSS [E2](https://bugbountylibrary.com/r/h1%3A2819573.md). E3 was reflected XSS limited to Safari [E3](https://bugbountylibrary.com/r/h1%3A2106708.md). Impact rose where the payload ran at the server origin and CORS widened reach [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3.md).

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-73g7-86qr-jrg3.md
- E2: https://bugbountylibrary.com/r/h1%3A2819573.md
- E3: https://bugbountylibrary.com/r/h1%3A2106708.md
