Skip to content

Block supervisor/hassio WebSocket command types in the HA proxy - #7123

Open
mdegat01 wants to merge 1 commit into
mainfrom
fix/ws-proxy-block-core-only-commands
Open

Block supervisor/hassio WebSocket command types in the HA proxy#7123
mdegat01 wants to merge 1 commit into
mainfrom
fix/ws-proxy-block-core-only-commands

Conversation

@mdegat01

Copy link
Copy Markdown
Member

Proposed change

The Supervisor's Core WebSocket proxy (/core/websocket) accepts connections from any app with homeassistant_api: true. It authenticates the app with its own token but then opens the upstream connection to Core as the Supervisor itself. _proxy_message was forwarding frames verbatim with no content inspection.

An app could exploit this by sending a supervisor/api command (or other supervisor/*/hassio/* type) through the proxy. Core's hassio integration handles those commands by calling back into the Supervisor with Core's own token. That token bypasses all role checks in security.py — it is treated as the most privileged caller regardless of the app's declared hassio_api or hassio_role.

Fix: in _proxy_message, TEXT frames on the app-to-Core direction whose type field starts with "supervisor/" or "hassio/" are now rejected with a Core-shaped result/unauthorized response instead of being forwarded. The connection stays open so the app can continue making normal HA calls (e.g. call_service, subscribe_events). The Core-to-app direction is unfiltered. Fail-open on unparseable frames — Core's own validation already rejects those.

Tests cover: the exact supervisor/api attack vector, all denied command-type namespaces, continued usability of the connection after a rejection, and pass-through of non-JSON frames.

Type of change

  • Bugfix (non-breaking change which fixes an issue)

Additional information

  • This PR fixes or closes issue:
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to cli pull request:
  • Link to client library pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Ruff (ruff format supervisor tests)
  • Tests have been added to verify that the new code works.

@mdegat01
mdegat01 requested review from agners and balloob and a lite review from Copilot August 12, 2026 04:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Supervisor’s /core/websocket proxy to prevent add-ons/apps (with only homeassistant_api: true) from using the proxy as a confused deputy to invoke privileged supervisor/* or hassio/* WebSocket commands against Home Assistant Core, which would then call back into the Supervisor with Core’s fully privileged token.

Changes:

  • Add a denylist for WebSocket command type prefixes (supervisor/, hassio/) and a small helper to detect them from TEXT frames.
  • Reject denied app→Core WebSocket commands with a Core-shaped result/unauthorized response while keeping the connection open.
  • Add targeted tests covering the supervisor/api exploit path, all denied namespaces, continued connection usability, and pass-through of malformed/non-JSON frames.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
supervisor/api/proxy.py Adds command-type filtering in the WebSocket proxy to block supervisor/* and hassio/* messages from apps while preserving normal proxy behavior.
tests/api/test_proxy.py Adds regression and behavior tests validating the block, ensuring the upstream Core server doesn’t receive denied commands, and confirming other traffic still proxies correctly.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

An app with only homeassistant_api: true could send supervisor/api (or
other supervisor/* / hassio/* command type) frames through the Supervisor's
Core WebSocket proxy at /core/websocket. Core executes those via the hassio
integration's websocket_supervisor_api handler, which calls back into the
Supervisor using Core's own token. That token bypasses all role checks in
security.py, giving the app unrestricted Supervisor API access regardless of
its declared hassio_role or hassio_api flag.

Fix: filter command types on the app-to-Core direction of _proxy_message.
Any TEXT frame whose type field starts with 'supervisor/' or 'hassio/' is
rejected with a Core-shaped result/unauthorized response instead of being
forwarded. The connection stays open. Fail-open on unparseable frames (Core's
own validation already rejects those). The Core-to-app direction is
unfiltered.
@mdegat01
mdegat01 force-pushed the fix/ws-proxy-block-core-only-commands branch from 44ba945 to 5a2c3f6 Compare August 12, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants