Skip to content

Patch/ws session owner binding#34

Open
muhammad-at-sec wants to merge 11 commits into
mainfrom
patch/ws-session-owner-binding
Open

Patch/ws session owner binding#34
muhammad-at-sec wants to merge 11 commits into
mainfrom
patch/ws-session-owner-binding

Conversation

@muhammad-at-sec
Copy link
Copy Markdown
Contributor

@muhammad-at-sec muhammad-at-sec commented May 26, 2026

Summary

Closes #30 Fixes WebSocket session takeover by binding each session_id subscriber to a server-derived owner: agent_profile_id when available, otherwise api_key_id.

Why

Previously, any authenticated SDK client could reuse another client’s session_id and steal verdict/HITL responses.

Notes

Same-owner reconnects still work, including agent-profile key rotation. Cross-owner reuse is rejected instead of replacing the active subscriber.

Test plan

  • [x]

Checklist

  • CLA signed (see CLA.md)
  • Tests pass locally
  • Docs updated where needed
  • British English; no em-dashes; no marketing fluff

A self-contained briefing intended to be dropped into a coding agent's
context (Claude, Cursor, Copilot Chat, etc.) so the agent can install,
configure, and troubleshoot Adrian end-to-end without further reading.

Covers: managed cloud vs self-hosted bring-up, the minimal asyncio
snippet, every adrian.init() argument worth knowing, the three
execution modes (Alert / Human Review / Block) mapped to the MAD
taxonomy (M0-M4), webhook + MCP integrations, dashboard navigation,
manual instrumentation, fork-safety and event-loop pitfalls, full env
var table, the eleven most common failure modes with fixes, the API
key lifecycle, a copy-pasteable smoke test, and a compact system-prompt
cheat sheet.
What changed: the WebSocket hub now records a server-derived route owner for each session_id and rejects attempts by a different authenticated owner to register the same session. The route owner is the agent_profile_id when present, with api_key_id as the fallback for unprofiled keys. Conflicting reuse closes the second connection with a policy-violation close code while preserving the original subscriber.

Why changed: the previous routing key was only the client-supplied session_id, so any valid SDK key could claim another client session_id and receive verdict/HITL responses meant for that original SDK connection.

Why not bind to raw API key only: agent-profile keys are rotated during normal dashboard flows. Binding only to api_key_id would fix takeover but could break same-agent reconnect/rotation continuity. Using agent_profile_id first preserves that path while still preventing cross-agent takeover.

Tests: added hub and live WebSocket regression coverage for different owners sharing a session_id, updated HITL hub subscriber tests, and ran go test ./... in the backend via golang:1.25-alpine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] WebSocket session takeover via client-controlled session_id

2 participants