Mar 12, 2026·5 min read·8 visits
ha-mcp versions before 7.0.0 are vulnerable to Stored and Reflected XSS in the beta ha-mcp-oauth mode. Attackers can steal Home Assistant tokens by tricking operators into clicking crafted authorization links.
The Home Assistant MCP Server (ha-mcp) prior to version 7.0.0 contains a Cross-Site Scripting (XSS) vulnerability within its beta OAuth consent form. The application dynamically constructs HTML using Python f-strings without proper input sanitization, allowing attackers to execute arbitrary JavaScript in the context of the server operator's browser session.
ha-mcp functions as a Model Context Protocol server that facilitates integrations between artificial intelligence models and Home Assistant instances. The software includes a beta feature designated as ha-mcp-oauth which exposes an OAuth authorization flow for client registration and consent.\n\nThe core vulnerability resides within the manual HTML response construction process used by the OAuth consent form. The application relies on Python f-strings to inject dynamic content into the HTTP response body without employing context-aware output encoding. This architectural decision creates a direct pathway for code injection.\n\nThe resulting flaw is classified as CWE-79, Improper Neutralization of Input During Web Page Generation. An attacker exploits this weakness to embed arbitrary JavaScript payloads within the HTML response served to the Home Assistant operator.\n\nThe attack surface spans multiple parameters within the /authorize endpoint and the Dynamic Client Registration /register endpoint. This broad exposure enables both Stored and Reflected Cross-Site Scripting vectors, depending on the specific parameter targeted by the adversary.
The root cause resides in the consent_form.py module within the beta OAuth implementation. The application receives user-controlled input from incoming HTTP requests and interpolates these values directly into a predefined HTML template string.\n\nThe implementation explicitly omits required sanitization or escaping mechanisms. Standard Python security practices dictate the use of functions such as html.escape() when constructing dynamic web pages, yet these safeguards are absent from the HTML generation pipeline.\n\nThe vulnerability manifests across multiple HTML contexts. Variables such as client_name are rendered directly within HTML text nodes, while parameters including state and redirect_uri are placed within HTML attribute values.\n\nAttackers bypass the intended HTML structure by supplying specific payload sequences. Providing a double quote character (") terminates the attribute value string prematurely, which allows the subsequent injection of arbitrary DOM event handlers like onmouseover.
Examining the vulnerable implementation reveals the direct use of f-strings for HTML rendering. The server extracts variables from the incoming OAuth request and immediately uses them to build the response body.\n\nThe vulnerable code pattern interpolates unvalidated variables directly into the DOM structure.\n\npython\n# Vulnerable implementation in consent_form.py\nhtml_content = f"""\n<html>\n <body>\n <h1>Authorize {client_name}</h1>\n <p>Do you want to grant access to {client_id}?</p>\n <form action="/approve" method="POST">\n <input type="hidden" name="state" value="{state}">\n <input type="hidden" name="redirect_uri" value="{redirect_uri}">\n </form>\n </body>\n</html>\n"""\n\n\nThe patch introduced in ha-mcp version 7.0.0 resolves this issue by upgrading the underlying fastmcp dependency to version 3.1.0. The updated dependency enforces strict output encoding for all user-controlled variables before they enter the templating engine.\n\npython\n# Patched implementation utilizing html.escape\nimport html\n\n# Variables are strictly sanitized before interpolation\nsafe_client_name = html.escape(client_name, quote=True)\nsafe_client_id = html.escape(client_id, quote=True)\nsafe_state = html.escape(state, quote=True)\nsafe_redirect_uri = html.escape(redirect_uri, quote=True)\n\nhtml_content = f"""\n<html>\n <body>\n <h1>Authorize {safe_client_name}</h1>\n <p>Do you want to grant access to {safe_client_id}?</p>\n <form action="/approve" method="POST">\n <input type="hidden" name="state" value="{safe_state}">\n <input type="hidden" name="redirect_uri" value="{safe_redirect_uri}">\n </form>\n </body>\n</html>\n"""\n\n\nThe patch completely neutralizes the injection vector by transforming HTML control characters into their corresponding safe HTML entities. The use of quote=True ensures that single and double quotes are escaped, adequately protecting the attribute contexts.
Exploitation requires the target ha-mcp instance to run the beta ha-mcp-oauth configuration. The attacker operates entirely without initial authentication to the Home Assistant instance, relying instead on network access to the exposed endpoints.\n\nThe Stored XSS vector leverages the Open Dynamic Client Registration (RFC 7591) feature. The attacker submits a malicious registration payload to the /register endpoint, populating the client_name field with a script tag that the server stores for future rendering.\n\nThe Reflected XSS vector targets the /authorize endpoint directly via URL parameters. The attacker crafts an authorization URL containing breakout sequences in parameters such as state or error_description, embedding the payload directly within the request URI.\n\nThe attack sequence relies on the operator interacting with the crafted URL.\n\nmermaid\ngraph LR\n A["Attacker"] -->|"Registers payload via /register"| B["ha-mcp Server"]\n A -->|"Sends crafted OAuth link"| C["Home Assistant Operator"]\n C -->|"Clicks link to /authorize"| B\n B -->|"Returns unsanitized HTML"| C\n C -->|"Executes JavaScript payload"| A\n\n\nUpon clicking the link, the server returns the poisoned HTML document. The browser executes the embedded JavaScript payload within the security context of the operator's active session.
The execution of arbitrary JavaScript within the operator's browser session yields substantial security consequences. The attacker gains the ability to interact with the Home Assistant environment under the operator's exact privilege level.\n\nThe primary objective of the exploit entails token exfiltration. The injected payload accesses the browser's storage mechanisms, including cookies and local storage, to locate and extract the Home Assistant Long-Lived Access Token.\n\nPossession of the access token grants the attacker persistent, authenticated access to the Home Assistant API. This access facilitates unauthorized control over connected physical devices, the retrieval of sensitive environment configuration data, and the potential manipulation of security systems integrated with the platform.\n\nThe CVSS v3.1 base score of 6.8 reflects the requirement for user interaction and the specific beta configuration prerequisite. Despite these contextual mitigating factors, the impact on confidentiality and integrity is high due to the potential for complete system compromise via token theft.
The primary remediation strategy requires upgrading ha-mcp to version 7.0.0 or a subsequent release. This version incorporates the updated fastmcp dependency, which enforces secure HTML generation and effectively eliminates the vulnerability.\n\nAdministrators who cannot immediately apply the patch must disable the beta ha-mcp-oauth mode. Modifying the server configuration to remove this feature completely eliminates the vulnerable authorization flow from the exposed attack surface.\n\nImplementing a restrictive Content Security Policy (CSP) provides an essential layer of defense-in-depth. A strictly configured CSP prevents the execution of inline scripts and blocks unauthorized outbound network requests, thereby neutralizing the primary mechanisms used for data exfiltration.\n\nNetwork access controls should restrict interaction with the ha-mcp server management interfaces. Limiting exposure to trusted internal IP ranges or requiring VPN access minimizes the likelihood of external exploitation attempts against the /register and /authorize endpoints.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ha-mcp Home Assistant AI | < 7.0.0 | 7.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Base Score | 6.8 |
| EPSS Score | 0.00033 |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.