# Sanitizer allowlist misses foreign content

Allowlisted SVG/MathML/style containers, referenced data-URI documents, and substring-matched allowlists that skip real content

_Area: Web injection and code execution. Seen in 8 disclosed reports; 5 confirmed bounties, median $2,400, range $2,400-$10,000; latest 2025-06-27. Updated 2026-10-23._

## How it works
An HTML sanitizer walks a parsed tree and keeps only nodes matching an allowlist. The broken assumption: the sanitizer's parse equals the renderer's parse, and allowlisting a container means its contents are still checked. Foreign-content elements (SVG, MathML) and raw-text containers (style, select, xmp) are parsed differently, and referenced documents (data: URIs, external SVGs) may not be walked at all. When the two parses disagree, or a referenced resource is never visited, disallowed markup survives into rendered output.

## Where it shows up
- Allowlists including `svg`, `math`, `style`, `select`, `use`, `animate`, or `xmp`, alone or combined.
- SVG/XML input with DOCTYPE entity declarations, CDATA, or namespaces [E1](https://bugbountylibrary.com/r/h1%3A232174.md).
- `data:image/svg+xml` or external URIs referenced by `use`/`image`/`xlink:href` [E2](https://bugbountylibrary.com/r/h1%3A831962.md)[E4](https://bugbountylibrary.com/r/h1%3A1805873.md).
- SVG animation elements (`animate`) carrying `javascript:` URIs in attributes [E6](https://bugbountylibrary.com/r/h1%3A271007.md).
- Markdown/template pipelines where a second parser (e.g. MathJax) reinterprets escaped or entity-encoded tags [E3](https://bugbountylibrary.com/r/blog%3Asaajan-bhujel-cyou-blog-web-2022-10-16-how-i-got-10000-from-github-for-bypassing-filtration-of-html-tags.md).
- Rich-text editors sanitizing pasted attachment HTML [E5](https://bugbountylibrary.com/r/h1%3A2819573.md).

## How to review
Identify every parser in the path and confirm the sanitizer walks the same tree the renderer will. The invariant: no node or attribute outside the allowlist may reach output, including inside foreign-content containers, raw-text elements, entity declarations, animation attributes, and referenced data-URI documents. Test allowlist combinations as a matrix (svg+style, math+style, select+style) [E7](https://bugbountylibrary.com/r/h1%3A1805899.md)[E8](https://bugbountylibrary.com/r/h1%3A1599573.md), confirm nested SVG in data URIs is recursively sanitized [E4](https://bugbountylibrary.com/r/h1%3A1805873.md), check entity/DOCTYPE input cannot make the validator skip a subtree [E1](https://bugbountylibrary.com/r/h1%3A232174.md), and verify attribute-value sinks like `animate`/`xlink:href` reject `javascript:` [E6](https://bugbountylibrary.com/r/h1%3A271007.md). For multi-parser pipelines, verify the sanitizer runs after the last parser that can synthesize tags [E3](https://bugbountylibrary.com/r/blog%3Asaajan-bhujel-cyou-blog-web-2022-10-16-how-i-got-10000-from-github-for-bypassing-filtration-of-html-tags.md).

## Variants seen
- XML entity declaration makes the SVG whitelist skip enforcement [E1](https://bugbountylibrary.com/r/h1%3A232174.md).
- External SVG via `use`/`xlink:href` with `foreignObject` runs script in Firefox only [E2](https://bugbountylibrary.com/r/h1%3A831962.md).
- Nested SVG inside a `data:` URI referenced by `use` escapes sanitization [E4](https://bugbountylibrary.com/r/h1%3A1805873.md).
- `svg`+`style` or `math`+`style` allowlisted together leaves script/onerror intact [E7](https://bugbountylibrary.com/r/h1%3A1805899.md).
- `select`+`style` leaves nested script; parser backend (Java vs Ruby) changes the tree [E8](https://bugbountylibrary.com/r/h1%3A1599573.md).
- `animate` with `javascript:` `xlink:href` survives Markdown SVG filtering [E6](https://bugbountylibrary.com/r/h1%3A271007.md).
- Backslash-escaped and entity-encoded tags inside MathJax braces bypass Markdown filtering [E3](https://bugbountylibrary.com/r/blog%3Asaajan-bhujel-cyou-blog-web-2022-10-16-how-i-got-10000-from-github-for-bypassing-filtration-of-html-tags.md).
- MathML mutation vectors reparse `<style>` content into executable tags in a rich-text editor [E5](https://bugbountylibrary.com/r/h1%3A2819573.md).

## Impact and severity
Outcomes ranged from stored XSS on admin panels and partner portals [E1](https://bugbountylibrary.com/r/h1%3A232174.md) to reflected XSS in widely used Rails helpers [E4](https://bugbountylibrary.com/r/h1%3A1805873.md)[E7](https://bugbountylibrary.com/r/h1%3A1805899.md)[E8](https://bugbountylibrary.com/r/h1%3A1599573.md), CSS/UI spoofing via MathJax [E3](https://bugbountylibrary.com/r/blog%3Asaajan-bhujel-cyou-blog-web-2022-10-16-how-i-got-10000-from-github-for-bypassing-filtration-of-html-tags.md), and stored XSS in a note app and editor [E5](https://bugbountylibrary.com/r/h1%3A2819573.md)[E6](https://bugbountylibrary.com/r/h1%3A271007.md). Severity rose where the sink was an authenticated admin surface or a shared library, and where a single allowlist combination sufficed.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A232174.md
- E2: https://bugbountylibrary.com/r/h1%3A831962.md
- E3: https://bugbountylibrary.com/r/blog%3Asaajan-bhujel-cyou-blog-web-2022-10-16-how-i-got-10000-from-github-for-bypassing-filtration-of-html-tags.md
- E4: https://bugbountylibrary.com/r/h1%3A1805873.md
- E5: https://bugbountylibrary.com/r/h1%3A2819573.md
- E6: https://bugbountylibrary.com/r/h1%3A271007.md
- E7: https://bugbountylibrary.com/r/h1%3A1805899.md
- E8: https://bugbountylibrary.com/r/h1%3A1599573.md
