CVE Patch Review

GHSA-Q855-8RH5-JFGQ: Ingress IP Gate Fixes Unauthenticated Root Settings Routes

GHSA-Q855-8RH5-JFGQ · Updated 2026-07-08 Root-cause

Summary

The patch adds an ingress-only guard to root-mounted settings and policy routes in ha-mcp, restricting bare-root access on TCP/9583 to the Home Assistant Supervisor transport peer (172.30.32.2). This directly addresses the missing authentication/CSRF exposure on the unauthenticated root mount while preserving secret-path access for direct or remote use. The fix is strong for the documented Home Assistant add-on deployment model, but it relies on a fixed network trust assumption rather than introducing explicit application-layer authentication on the root routes.

Analysis

Vulnerability

The advisory describes administrative settings and policy endpoints exposed on the add-on's bare root over TCP/9583 without authentication, allowing network-adjacent callers to modify tool configuration, feature flags, and approval policy state. The vulnerable routing mounted handlers both under a secret-prefixed path and directly at the root, including write-capable endpoints, with no access control on the root mount. The pre-patch code explicitly registered mcp.custom_route("/", methods=["GET"]) and then called _mount(""), making the same settings UI routes reachable without the MCP secret on the published port. This created both a missing-authentication issue and a CSRF-reachable attack surface because browser-originated requests to the bare root did not require a secret or transport-origin validation. See the advisory and patch discussion at GitHub Security Advisory, pull request #1508, and commit 9f5b085.

def _mount(prefix: str) -> None:
            mcp.custom_route(f"{prefix}{path}", methods=methods)(handlers[handler_key])
        mcp.custom_route("/", methods=["GET"])(handlers["root_page"])
        _mount("")

Patch

The patch introduces an _ingress_only wrapper and applies it to all root-mounted settings UI routes, including the root page itself. The guard checks request.client.host against a fixed Supervisor ingress IP constant, 172.30.32.2, and returns HTTP 403 for any non-Supervisor peer. Importantly, the patch does not trust X-Forwarded-For; it keys only on the transport peer, which is the correct anti-spoofing choice for this deployment model. The secret-prefixed routes remain unguarded by this wrapper because the secret path is treated as the authentication mechanism for direct or remote access. The implementation also improves typing by defining a route-handler callable type and broadening the wrapper return type to generic Response. Tests were added to verify rejection of non-Supervisor peers, acceptance of the Supervisor peer, rejection when client metadata is absent, non-bypass via forged X-Forwarded-For, and correct application of the wrapper only to root-mounted routes. These changes are visible in PR #1508 and commit 9f5b085.

SUPERVISOR_INGRESS_IP = "172.30.32.2"

def _ingress_only(handler: _SettingsRoute) -> _SettingsRoute:
    @functools.wraps(handler)
    async def _guarded(request: Request) -> Response:
        peer = request.client.host if request.client else None
        if peer != SUPERVISOR_INGRESS_IP:
            return JSONResponse({"error": "This endpoint is only reachable through Home Assistant ingress."}, status_code=403)
        return await handler(request)
    return _guarded

mcp.custom_route("/", methods=["GET"])(_ingress_only(handlers["root_page"]))
_mount("", guard=True)

Review

Pros

The patch addresses the actual root cause in the current architecture: sensitive root-mounted routes were reachable without the secret path, so the fix removes unauthenticated direct access to those routes by enforcing the Home Assistant ingress trust boundary. Applying the guard at route registration time is simple and hard to bypass accidentally for the covered endpoints. The decision to validate the transport peer rather than X-Forwarded-For is technically sound and explicitly tested. Test coverage is good for the security property being introduced: positive path, negative path, missing peer metadata, forged header resistance, and verification that secret-path twins remain unwrapped. The patch also preserves intended product behavior, since ingress traffic from the Supervisor should continue to work while direct callers are forced onto the secret-prefixed path. The inline comments document the Home Assistant networking assumptions in detail, which is useful because the security model depends on them.

Cons

The fix is tightly coupled to a specific deployment invariant: that legitimate ingress always arrives from 172.30.32.2. If Home Assistant networking changes, if alternative deployment topologies exist, or if an operator runs the add-on in an environment where the peer address differs, the guard may fail closed or become brittle. More importantly, this is still network-origin authorization rather than explicit application-layer authentication. The secret-prefixed routes remain protected only by path secrecy, which is better than bare-root exposure but weaker than session-bound auth plus CSRF defenses. The patch mitigates CSRF on the root mount by making browser-originated LAN requests fail the peer check, but it does not introduce generic CSRF tokens or origin validation for authenticated state-changing requests. There is also no evidence in the provided diff of a centralized authorization abstraction for future routes, so new root-mounted endpoints could regress if developers forget to apply the same pattern outside this registration path.

Verdict

Root-cause.

Within the documented Home Assistant add-on ingress model, the patch fixes the vulnerable condition by restoring an access-control boundary on the bare-root routes that previously had none. It is not merely input filtering or a narrow endpoint-specific workaround; it changes how the entire root mount is exposed and adds regression tests around that security contract. The remaining concern is architectural strength, not immediate correctness: the solution depends on trusted network topology and secret-path access for non-ingress use rather than first-class authentication and CSRF primitives. For this advisory, however, the patch is a credible and source-supported remediation. References: GHSA advisory, PR #1508, fix commit, CVE Reports summary.

Sources