mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
CORSMiddleware doesn't cover WebSocket handshakes at all (Starlette's CORS support only wraps HTTP), so under SEMANTICA_ALLOW_ANONYMOUS=true -- the mode docker-compose.dev.yml ships -- is_valid_api_key's anonymous bypass accepted a /ws/graph-updates connection from any origin. Loopback binding isn't a boundary against a browser: any page the operator has open can still reach ws://localhost:8000/ws/graph-updates directly, and ConnectionManager.broadcast sends every graph_mutation to every connected socket with no per-connection scoping. Combined with /api/import accepting multipart/form-data (a CORS-safelisted content type that skips preflight), a hostile page could write to the graph over REST and read the result back over the unauthenticated WebSocket -- demonstrated end-to-end in the report with a real client. Not affected: any deployment with SEMANTICA_API_KEY configured -- the handshake already rejects without a valid key in that mode. This is an anonymous-mode-only, development-configuration exposure. Fix: check the handshake's Origin header against app.state.explorer_settings['allowed_origins'], the same list CORSMiddleware already enforces for HTTP, before the key check. A missing Origin (native/CLI clients, which never set the header -- only browsers do) is still allowed through, since the browser is the only threat this closes. 4 new tests in test_explorer_auth.py: hostile Origin rejected under anonymous mode; hostile Origin rejected even with a correct key (Origin is checked before the key, so a leaked key alone can't hijack the socket); an allowlisted Origin still connects under anonymous mode; a missing Origin still connects under anonymous mode (native clients keep working). Full explorer suite: 226 passed. Co-authored-by: Sameer Kadam <sskadam6305@gmail.com>