Sep 22, 2026·5 min read·4 visits
Unescaped entity friendly names in Home Assistant Statistics Graph tooltips allow DOM-based XSS, leading to potential administrative session hijacking.
CVE-2026-91130 is a critical DOM-based Cross-Site Scripting (XSS) vulnerability in the Home Assistant open-source home automation platform. Prior to version 2026.7.0, the Statistics Graph card rendered series tooltips using raw HTML string interpolation without escaping user-controlled entity friendly names. By abusing this vulnerability, an authenticated user with low-privilege access can inject arbitrary HTML and JavaScript into entity name fields, which executes in the context of an administrative user's browser session upon hovering over a data point on an affected chart.
The Home Assistant web-based user interface relies on several custom UI cards to display real-time sensor metrics and state changes. Within this interface, the Statistics Graph card allows users to visualize historical data such as temperature, power consumption, and device metrics using the third-party Apache ECharts visualization library.\n\nBecause the Home Assistant backend utilizes a dynamic state machine, the names and friendly labels of individual entities are passed directly from backend components to the frontend visualization cards. This establishes a communication channel between backend databases, user inputs, external integration components, and the administrative dashboard.\n\nPrior to the release of version 2026.7.0, the Statistics Graph card rendered tooltips using raw string values without sanitizing individual metadata fields. This structural oversight created a DOM-based Cross-Site Scripting vulnerability, where unauthenticated or low-privileged actors can execute malicious client-side code inside the security context of other users.
The technical root cause of CVE-2026-91130 lies in the custom tooltip formatter callback assigned to the Apache ECharts instance within src/components/chart/statistics-chart.ts. The formatter function was programmed to return a raw string containing HTML markup that represented the data point series name and value.\n\nWhen ECharts processes a custom formatter that returns a raw string, it writes the string directly to the DOM using a sink that evaluates HTML markup. Since the formatter used a template literal to interpolate the param.seriesName parameter, any HTML sequence embedded in the entity friendly name is directly executed by the browser rendering engine.\n\nThis behavior bypasses standard frontend input filters because the entity name is retrieved directly from the state history store. The state history data is populated by backend processes, which trust state modifications submitted via local integrations, external API requests, or user customization utilities.
The vulnerable implementation in the frontend codebase relied on string interpolation to build the HTML string structure that was returned to the charting component.\n\ntypescript\n// Vulnerable string-based tooltip formatter\nconst rawTime = formatDateTimeWithSeconds(startTime, this.hass.locale, this.hass.config);\nconst time = index === 0 ? rawTime : \"\";\nreturn `${time}${param.marker} ${param.seriesName}: ${value}`;\n\n\nThe patched version replaces the raw string interpolation logic entirely by transitioning the tooltip formatting pipeline to use Lit templates, ensuring that variables are automatically escaped.\n\ntypescript\n// Secure Lit-based tooltip formatting implementation\nreturn html`${rows.map(\n (row, i) =>\n html`${row.time\n ? html`${row.time}<br />`\n : nothing}<ha-chart-tooltip-marker\n .color=${row.color}\n ></ha-chart-tooltip-marker>\n ${row.seriesName}:\n ${row.value}${i < rows.length - 1 ? html`<br />` : nothing}`\n)}`;\n\n\nBy passing a custom Web Component <ha-chart-tooltip-marker> and evaluating structural variables through Lit templates, the application enforces compile-time sanitization. The row.seriesName variable is handled strictly as text, rendering any embedded HTML entities harmlessly without script evaluation.
To exploit this vulnerability, an attacker requires low-privileged administrative access or control over a connected device integration to update an entity's metadata attributes. This is commonly achieved by submitting a state change to the Home Assistant REST API or modifying the core registry files.\n\nbash\ncurl -X POST \\\n -H \"Authorization: Bearer <Attacker_Token>\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"state\": \"22.5\", \"attributes\": {\"friendly_name\": \"<img src=x onerror=alert(document.domain)>\", \"state_class\": \"measurement\"}}' \\\n http://homeassistant.local:8123/api/states/sensor.vulnerable_sensor\n\n\nmermaid\ngraph LR\n A[\"Attacker Payload\"] --> B[\"API / State Registry\"]\n B --> C[\"ECharts Tooltip Component\"]\n C --> D[\"Victim Browser DOM\"]\n\n\nOnce the payload is successfully injected into the entity registry database, the attacker waits for an administrative viewer to inspect the Lovelace dashboard containing a Statistics Graph card tracking the affected entity. The moment the administrator hovers over a data point, ECharts executes the callback formatter, inserts the payload into the browser DOM, and executes the script context.
A successful exploitation of CVE-2026-91130 leads to a complete compromise of the victim's frontend browser session. Because Home Assistant utilizes active session cookies and local storage tokens to maintain administrative access, the executed payload has complete read and write authority under the administrator's profile.\n\nAn attacker can construct client-side scripts to steal the long-lived access tokens stored in browser LocalStorage. These stolen tokens allow the attacker to issue API commands remotely, control physical smart home locks, disable cameras, modify alarm configurations, or trigger internal automation events.\n\nIn environments where shell commands or command-line integrations are enabled, administrative access can be leveraged to execute shell tools. This elevates the local client-side vulnerability to remote command execution on the host operating system running the Home Assistant Core container.
Remediation requires upgrading the Home Assistant Core installation to version 2026.7.0 or higher. This upgrade replaces the vulnerable frontend component code with the secure Lit-based rendering template library.\n\nIf an immediate upgrade is not feasible, administrators should restrict dashboard configuration permissions to trusted roles and limit the use of custom integrations. Additionally, the existing configuration files and integration state database should be audited regularly to ensure no raw HTML payloads are embedded within entity friendly names.\n\nTo identify potential active payload indicators within the local state database, administrators can run the following SQL command against the SQLite state file.\n\nsql\nSELECT entity_id, attributes FROM states \nWHERE attributes LIKE '%<script%' \n OR attributes LIKE '%onerror%' \n OR attributes LIKE '%onload%';\n
| Product | Affected Versions | Fixed Version |
|---|---|---|
Home Assistant Core Home Assistant | < 2026.7.0 | 2026.7.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-80 |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 9.3 (Critical) |
| Required Privilege | Low (PR:L) |
| User Interaction | Active (UI:A) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
Home Assistant Core prior to version 2026.2.3 is vulnerable to Server-Side Request Forgery (SSRF) via the IPP integration's auto-discovery mechanism. Unauthenticated mDNS advertisements can trigger HTTP requests that follow malicious redirects to loopback interfaces.
A high-severity denial of service vulnerability exists in the emiago/sipgo Go library when parsing stream-based SIP messages. The stream parser fails to validate declared Content-Length header sizes before initiating memory allocations, allowing remote, unauthenticated attackers to trigger process memory exhaustion and application crashes.
CVE-2026-58270 identifies a Regular Expression Denial of Service (ReDoS) vulnerability in Sync-in Server prior to version 2.4.0. An authenticated attacker can supply a complex regular expression in the pathFilters parameter of the sync diff endpoint. When evaluated, this causes catastrophic backtracking, blocking the single-threaded Node.js event loop and rendering the entire server unresponsive.
CVE-2026-56681 is a high-severity authentication bypass vulnerability in 9Router, an AI router and token-saving proxy. The vulnerability arises from an improper trust boundary where the application relies on the client-controlled HTTP header X-9r-Real-Ip to determine whether an incoming request originates from a local (loopback) environment. In deployments where requests can reach the Next.js backend directly—bypassing the sanitizing custom-server.js wrapper—a remote, unauthenticated attacker can spoof their origin by supplying an X-9r-Real-Ip: 127.0.0.1 header.
A rate limiting bypass vulnerability in 9Router versions before 0.5.6 allows unauthenticated remote attackers to circumvent the login progressive lockout mechanism. By manipulating the client-supplied X-9r-Real-Ip HTTP header, an attacker can rotate the tracking IP address, enabling unthrottled brute-force password guessing against the administrative interface.
CVE-2026-58272 is a timing side-channel vulnerability in the authentication endpoint of Sync-in Server before version 2.4.1. Unauthenticated remote attackers can distinguish between valid and invalid usernames due to asymmetric execution paths. When processing invalid usernames, the database query returns early, skipping the computationally expensive bcrypt verification path that is normally triggered for valid accounts.