# Stored XSS via raw HTML rendering

Untrusted strings or objects reaching raw HTML sinks (dangerouslySetInnerHTML, contenteditable, unescaped templates)

_Area: Web injection and code execution. Seen in 3 disclosed reports; 2 confirmed bounties: $5,000 and $6,267; latest 2022-12-17. Updated 2026-10-24._

## How it works
A field the code treats as plain text is actually attacker-controlled and reaches an HTML sink without escaping. The broken assumption is that a value is "just a string" or "just an image" or "already sanitized." When that value is rendered as HTML, markup and script run in the victim's session. Persistence makes it stored: the payload is saved once and fires for every viewer.

## Where it shows up
- React children built from server JSON that may be objects, not strings; look for `dangerouslySetInnerHTML` and `react-contenteditable` [E2](https://bugbountylibrary.com/r/h1%3A49652.md)[E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md)
- Client-side templates fetched by URL fragment and rendered unescaped [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md)
- User uploads served from the same origin as the app [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md)
- Regex-based HTML strippers, especially `/g` without `/m` [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md)
- Paste handlers reading `text/html` and deserializing into rendered models [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md)
- Fields typed as string on the client but accepting arbitrary JSON on the server [E2](https://bugbountylibrary.com/r/h1%3A49652.md)

## How to review
Trace each rendered field from input to sink. The invariant: every value reaching an HTML sink is either a trusted constant or escaped/sanitized by a parser-based sanitizer, never a regex. Check that server-side type validation rejects non-strings for fields rendered as text [E2](https://bugbountylibrary.com/r/h1%3A49652.md). Check that sanitizers run on the final HTML, not before transformation [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md). Check that uploaded content is served from a separate origin or with a safe content type [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md). For templates, confirm escaping is on by default and the fragment cannot select an unescaped template [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md).

## Variants seen
- Spoofed React element object (`_isReactElement`, `type`, `props.dangerouslySetInnerHTML`) trusted as a child [E2](https://bugbountylibrary.com/r/h1%3A49652.md)
- Polymorphic image surviving re-encoding, served same-origin, combined with unescaped hash templates [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md)
- Clipboard HTML deserialized into protobuf notes rendered via contenteditable; newline-bearing tags bypass the regex [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md)

## Why fixes fail
Regex sanitizers miss multiline tags when `/m` is absent [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md). Re-encoding uploads does not strip payload bytes in metadata segments [E1](https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md). Type checks that only validate on one side leave the other open [E2](https://bugbountylibrary.com/r/h1%3A49652.md).

## Impact and severity
Arbitrary script in victims' sessions, including other team members viewing reports [E2](https://bugbountylibrary.com/r/h1%3A49652.md) and all whiteboard viewers [E3](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md). Stored payloads widen reach and persistence; same-origin hosting and broadcast channels raise severity.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-04-30-polymorphic-images-for-xss-html.md
- E2: https://bugbountylibrary.com/r/h1%3A49652.md
- E3: https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-analyzing-clipboardevent-listeners-stored-xss.md
