# Unescaped attribute markup injection

User values placed in title, tooltip, or image attributes and parsed as HTML

_Area: Web injection and code execution. Seen in 3 disclosed reports; one confirmed bounty: $300; latest 2026-09-04. Updated 2026-10-23._

## How it works
A value the user controls is written into an HTML attribute (tooltip, title, image URL) without encoding, and a downstream renderer treats that attribute's contents as markup. The broken assumption is that an attribute is a safe place to put data. When the sink parses the attribute as HTML (via a sanitizer-allowed gadget, a tooltip library's HTML mode, or an attribute-breaking quote), the value escapes its intended context and becomes live markup or script [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)[E2](https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md)[E3](https://bugbountylibrary.com/r/h1%3A1397940.md).

## Where it shows up
- Values flowing into `data-tooltip-html`, `title`, or image `src`/URL fields without escaping [E2](https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md)[E3](https://bugbountylibrary.com/r/h1%3A1397940.md)
- React `dangerouslySetInnerHTML` or `v-safe-html` fed from user fields [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)[E2](https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md)
- Sanitizer configs that permit gadget elements/attributes (e.g. feature-highlight with data attributes) [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)
- Data loaded asynchronously after initial render, so sanitizer timing differs from page load [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)
- Secondary surfaces (email previews, template renderers) that embed user content in iframes [E3](https://bugbountylibrary.com/r/h1%3A1397940.md)

## How to review
Trace each user-controlled field to its render sink. The invariant: any value placed in an attribute must be HTML-encoded so it cannot break out of the attribute, and any sink that parses HTML must run a sanitizer whose allowlist excludes script-capable gadgets. Check that tooltip/HTML-mode libraries receive escaped text, not raw markup. For sanitizer-based sinks, confirm the config blocks attribute-driven gadgets and that async-loaded data is sanitized the same as initial data. For self-XSS claims, verify whether a framing surface exists that lets an attacker trigger the victim's own action [E3](https://bugbountylibrary.com/r/h1%3A1397940.md).

## Variants seen
- Unencoded issue title rendered via v-safe-html, bypassing DOMPurify through a gadget plus history-cache timing [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)
- Issue summary stored in `data-tooltip-html` and rendered via `dangerouslySetInnerHTML`, allowing iframe srcdoc [E2](https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md)
- Unsanitized image URL with attribute-breaking payload, escalated from self-XSS via XSSJacking in an email preview iframe [E3](https://bugbountylibrary.com/r/h1%3A1397940.md)

## Impact and severity
Led to stored injection enabling arbitrary POST requests as the victim [E1](https://bugbountylibrary.com/r/h1%3A1533976.md), persistent phishing via injected iframe [E2](https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md), and script execution in victim context [E3](https://bugbountylibrary.com/r/h1%3A1397940.md). Severity rises when the value is persisted (stored, affects many viewers), when the sink is a shared component (tooltip library), and when framing or cache quirks remove the self-XSS barrier [E1](https://bugbountylibrary.com/r/h1%3A1533976.md)[E3](https://bugbountylibrary.com/r/h1%3A1397940.md).

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1533976.md
- E2: https://bugbountylibrary.com/r/bc%3A5c4b4697-bb0f-474f-b3e1-954d3758194e.md
- E3: https://bugbountylibrary.com/r/h1%3A1397940.md
