# Denylist sanitizer misses modern markup

Sanitizer blocks known-bad names, not known-good; check parser, URI and delimiter gaps

_Area: Web injection and code execution. Seen in 3 disclosed reports; one confirmed bounty: $10,000; latest 2026-07-10. Updated 2026-10-24._

## How it works
A sanitizer decides what to strip by listing known-bad names (event handlers, tags) instead of listing what is allowed. That list is incomplete by construction: new DOM handlers appear, regexes assume a delimiter before `on*=`, and parsers still process markup the filter never inspects. Whatever the filter does not recognize reaches an HTML sink and is parsed by the browser.

## Where it shows up
- Handler denylist copied from a legacy list (e.g. w3schools) rather than the full modern DOM event set [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md)
- Regex requiring whitespace/quote/slash before `on*=` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md)
- Dangerous-tag blacklist missing `svg`, `math`, `option`, `select` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md)
- `innerHTML` sink with no DOMPurify and no CSP [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md)
- Parser-gap variant: a renderer strips a leading tag but not entity-encoded tags inside a delimiter it still processes, e.g. MathJax `{}` [E2](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)

## How to review
Check the exact property: the sanitizer must be an allowlist of tags and attributes, and its output must be re-parsed by the same parser the browser uses. For each sink, confirm the sanitizer runs on the final string, not an intermediate one. Diff any handler denylist against the full modern event set (pointer, beforetoggle, focusin, animation, transition) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md). Probe attribute boundaries where a handler name is not preceded by a delimiter [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md). Probe entity-encoded tags inside parser delimiters [E2](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). Confirm CSP is present as defense in depth [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md).

## Variants seen
- Modern event-handler omission in a name denylist [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md)
- Regex delimiter assumption missing unquoted handlers [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md)
- Missing dangerous tags (svg/math/option/select) in a tag blacklist [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md)
- Entity-encoded tags inside MathJax braces bypassing a Markdown filter, yielding CSS/UI spoofing, not script execution [E2](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)

## Why fixes fail
E2 shows filter-gap iteration, not script execution: after each GitHub fix, the researcher changed delimiter/encoding to find the next gap, so the filter was patched per-payload rather than converted to an allowlist. E1's denylist was copied from a legacy list, so it was stale by construction.

## Impact and severity
Stored XSS in privileged origins: Administrator's SiYuan origin on hover [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md), Super Admin nonce exfiltration chainable to RCE in Grav [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md). E2 was CSS/UI spoofing in GitHub Markdown, not script execution. Severity is high because the sink runs in an admin or app origin; it worsens when no CSP exists [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md) and when the payload is stored and triggered on view [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md).

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-w7cg-whh7-xp28.md
- E2: 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
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-w8cg-7jcj-4vv2.md
