Mar 12, 2026·5 min read·52 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.
CVE-2026-115907 is an uncontrolled resource consumption vulnerability in the Rust zip crate (versions 5.1.0 through 8.6.0, and 9.0.0 pre-releases) when compiled with the optional legacy-zip feature flag. The vulnerability resides in the legacy decompression decoders—Shrink, Reduce, and Implode—which directly pass an archive-declared 64-bit uncompressed size from a ZIP64 extended information extra field to Vec::reserve() before validating the compressed data stream. By supplying a malformed ZIP archive with a declared uncompressed size exceeding isize::MAX (such as u64::MAX), an unauthenticated attacker can trigger an unhandled capacity overflow panic in the Rust standard library allocator, terminating the calling thread or aborting the entire process.
Twisted versions up to and including 26.4.0 are vulnerable to an unauthenticated remote denial-of-service attack via the HTTP/2 Rapid Reset technique (a variant of CVE-2023-44487). The HTTP/2 connection handler in twisted.web processes pipelined HEADERS and RST_STREAM frames sequentially within the same TCP segment. Because the request dispatch and synchronous rendering occur upon receiving the StreamEnded event before the subsequent StreamReset event is evaluated, an attacker can force the server to expend CPU cycles rendering responses while immediately evicting stream state to bypass concurrent stream limits.
An inefficient algorithmic complexity vulnerability exists in the npm package decode-uri-component prior to version 0.5.1. When processing malformed URI strings containing invalid percent-escape sequences, the library falls back to a custom decoder that collects distinct percent-encoded runs into a dictionary and executes a full-string regular expression replacement for each entry. Supplying a payload with thousands of distinct percent-encoded sequences alongside a malformed escape triggers quadratic O(K * L) execution time, starving the single-threaded Node.js event loop and resulting in an unauthenticated Denial of Service.
Open Neural Network Exchange (ONNX) versions 1.4.0 prior to 1.22.0 contain a null pointer dereference (CWE-476) and a signed-to-unsigned integer conversion error (CWE-195) in the shape inference functions for the Scan operator. When processing an untrusted ONNX model, the functions ScanInferenceFunction, ScanInferenceFunction_opset8, and ScanInferenceFunction_opset9 fail to validate the presence and value of the required num_scan_inputs attribute. Omitting the attribute results in a direct null pointer dereference that crashes the host process via SIGSEGV, while supplying a negative value triggers an integer wrap-around during type narrowing that leads to an unhandled allocation failure. Notably, models containing a negative num_scan_inputs value bypass standard validation via onnx.checker.check_model().
CVE-2026-91403 is a critical cryptographic trust validation flaw (CWE-327) in the Java Secure Socket Extension (JSSE) component of Oracle Java SE, Oracle GraalVM, and OpenJDK builds. Triggered by the compromise of private signing keys for eight intermediate Certificate Authorities operated by eMudhra (emSign), unpatched Java runtime environments implicitly trust fraudulent TLS and code-signing certificates issued by these compromised intermediates. Because Java disables online certificate revocation checking (OCSP/CRL) by default, an unauthenticated network attacker in an Adversary-in-the-Middle (AiTM) position can intercept, decrypt, and modify encrypted TLS communications or bypass code signature verification without triggering security exceptions.
Open Neural Network Exchange (ONNX) prior to version 1.22.0 contains a memory exhaustion vulnerability (CWE-789) in its Python tensor unpacking helpers and C++ model checker. Specifically, the _unpack_2bit() and _unpack_4bit() functions in onnx.numpy_helper unconditionally resize unpacked NumPy arrays to match attacker-controlled TensorProto.dims metadata without verifying that the underlying raw_data or int32_data buffers contain a corresponding number of packed bits. Furthermore, onnx.checker.check_tensor() previously bypassed length verification for sub-byte data types. Processing an untrusted ONNX model with packed sub-byte tensors (UINT2, INT2, UINT4, INT4, or FLOAT4E2M1) allows an unauthenticated attacker to trigger disproportionate memory allocations from minimal payloads, resulting in an out-of-memory (OOM) crash and denial of service.