# Home Assistant: XSS in Statistics Graph Card

Statistics Graph card interpolates entity names into ECharts tooltips as raw HTML, so a malicious entity name executes XSS on hover.

- Payout: Research
- Severity: critical
- Program: Home Assistant
- Found by: pwnpanda
- Disclosed: 2026-09-22
- CVE: CVE-2026-91130
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-wx4m-69m9-gx3m
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-wx4m-69m9-gx3m
- Tags: area:web domain:oss domain:web impact:ato impact:integrity kind:research platform:ghsa sev:critical technique:incomplete-fix vuln:xss

**Target:** Frontend Statistics Graph card (statistics-chart.ts) rendering ECharts tooltips
**Root cause:** `param.seriesName` is interpolated verbatim into the tooltip HTML string in `statistics-chart.ts` without calling `filterXSS()`, unlike the already-patched Energy dashboard chart. Entity names flow from `getStatisticLabel`/`computeStateName` with only underscore-to-space replacement, no HTML encoding.
**Steps:**
1. Create a Template sensor helper with Name `test <img src=x onerror=alert(document.domain) />`, unit kWh, state class Measurement.
2. Add a Statistics Graph card targeting that sensor; set time window to 5 minutes.
3. Ensure chart type is Line (default) and Mean/State/Sum/Change fields are selected (default).
4. Hover over any data point on the chart.
5. `onerror` fires and `alert(document.domain)` executes.
**How found:** Noticed the Energy dashboard chart was patched with `filterXSS()` for CVE-2025-62172 but the shared `statistics-chart` component was not updated, then traced the name flow through `getStatisticLabel` and `computeStateName` to confirm no encoding.
**Exploitation:** An authenticated user (or a malicious integration provider naming entities, e.g. Tibber/Shelly/HACS) sets an entity name containing an HTML/JS payload. When any victim views a Statistics Graph card containing that entity and hovers a data point, the payload executes in their browser session.
**Escalation:** Supply-chain vector: integration providers auto-name entities, delivering the payload without any account on the target instance, mirroring CVE-2025-62172.
**Insight:** When a fix lands for one chart/renderer, grep for sibling components sharing the same data-to-HTML interpolation path; incomplete-fix scope gaps are common in shared frontend chart code.

## Proof of concept (copied exactly from the original report)

**Malicious template sensor name payload**

```
**Name:** `test <img src=x onerror=alert(document.domain) />`
   - **State template:** `{{0.00000001*as_timestamp(states('sensor.date_time_iso'))}}`
   - **Unit of measurement:** `kWh`
   - **State class:** `Measurement`
```

**Vulnerable tooltip interpolation line**

```
return `${time}${param.marker} ${param.seriesName}: ${value}`;
```

