Skip to content

security(websocket): authenticate /api/intelligent_agent/stream before processing (#17000) - #17003

Closed
mrveiss wants to merge 2 commits into
mainfrom
issue-17000-agent-ws-auth
Closed

mrveiss wants to merge 2 commits into
mainfrom
issue-17000-agent-ws-auth

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Thinking Path

websocket_stream (autobot-backend/api/intelligent_agent.py) is the WebSocket
twin of the authenticated POST /api/intelligent_agent/process, but it only
called enforce_ws_origin — which by its own docstring passes any client that
omits an Origin header, i.e. every non-browser caller. Any client could reach
agent.process_natural_language_goal with no credential at all.

POST /process requires Depends(get_current_user) but never threads
current_user into agent.process_natural_language_goal(request.goal, context=request.context) beyond the auth gate itself — so the fix for the
WebSocket doesn't invent new plumbing to pass an identity into the agent either;
it matches /process's actual behavior: a verified caller is required before
the goal ever runs.

For the auth mechanism and rejection shape, the issue named
api/voice_stream.py's voice_stream_ws as the convention to follow exactly.
I checked api/ws_security.py's enforce_ws_authentication as the issue also
suggested, but it closes the socket without accepting first (code 1008) —
the opposite of the voice_stream_ws / api/websockets.py (#2818) convention
this repo already uses in the most call sites (api/websockets.py x3,
api/voice_stream.py, api/transcripts.py, api/live_events.py,
api/presence_ws.py): authenticate, then on rejection accept() then
close(code=4001, reason=...) so the client gets a real WS close frame instead
of a raw handshake rejection indistinguishable from a missing route (#12366,
#15745). enforce_ws_authentication didn't fit that convention, so I used
auth_middleware.authenticate_websocket directly, mirroring voice_stream_ws
line for line.

What Changed

  • autobot-backend/api/intelligent_agent.py websocket_stream: after the
    existing enforce_ws_origin check, authenticate with
    auth_middleware.authenticate_websocket before any receive_json() or
    get_agent() call. On a missing/invalid token: accept() then
    close(code=4001, reason="Authentication required"), then return — the
    refused handshake never reaches process_natural_language_goal.
  • autobot-backend/api/intelligent_agent_ws_auth_17000_test.py (new): 4 tests
    covering the 4 acceptance criteria.

Not changed: the agent doesn't receive the authenticated principal as a new
parameter, because /process doesn't pass current_user to
agent.process_natural_language_goal either — only using it as an auth gate.
Threading it through would be new plumbing beyond what /process does.

Verification

Local (design aid only — environment is below CI's declared dependency floors;
CI is the evidence):

$ python3 -m pytest api/intelligent_agent_ws_auth_17000_test.py -v
api/intelligent_agent_ws_auth_17000_test.py::TestIntelligentAgentStreamAuthentication::test_no_token_is_refused PASSED
api/intelligent_agent_ws_auth_17000_test.py::TestIntelligentAgentStreamAuthentication::test_invalid_token_is_refused PASSED
api/intelligent_agent_ws_auth_17000_test.py::TestIntelligentAgentStreamAuthentication::test_a_refused_handshake_never_calls_process_natural_language_goal PASSED
api/intelligent_agent_ws_auth_17000_test.py::TestIntelligentAgentStreamAuthentication::test_valid_token_is_accepted_and_reaches_the_agent PASSED
4 passed in 0.79s

Also checked:

  • python3 -m py_compile api/intelligent_agent.py — OK
  • python3 -m black --check / python3 -m ruff check on both changed files — clean
  • tools/lint/check_decorator_order.py on the modified file — no violations
  • function_length_checker.py --whole-file on the modified file — no violations
    (websocket_stream's counted body is unchanged in the 31–50 bucket; the
    docstring addition doesn't count toward the limit)
  • api/intelligent_agent.py is not in repo_tests/python_file_size_ratchet_baseline.py
    (only intelligence/intelligent_agent.py, a different file, is tracked), so
    no size-ratchet is at risk
  • docs/developer/THREAT_MODEL.md does not reference api/intelligent_agent.py
    or websocket_stream by line number, so no anchor needed updating
  • pre-push hook ran pytest on the new test file and passed before the push went out

Model Used

Claude Sonnet 5

Closes #17000

🤖 Generated with Claude Code

Single-issue rationale

This is a single, self-contained critical security fix (unauthenticated agent WebSocket, #17000) with no other open issue sharing its scope or files. Batching it with unrelated work would only delay a critical fix reaching main.

@mrveiss mrveiss added this to the v0.9.0 milestone Sep 18, 2026
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 5 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 56c850ee-b4b2-4aba-bfe0-4cc37b4e08d6

📥 Commits

Reviewing files that changed from the base of the PR and between 27acbd1 and af31193.

📒 Files selected for processing (2)
  • autobot-backend/api/intelligent_agent.py
  • autobot-backend/api/intelligent_agent_ws_auth_17000_test.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Notice: 29 open PRs — past the runaway threshold (25)

There is no PR queue limit, and this is not a request to defer this PR. Work proceeds one issue at a time without a cap on open PRs; review capacity is the constraint.

This notice only means the count is high enough to be worth a glance for a runaway — something opening PRs in a loop, or a merge pipeline that has stalled so nothing is draining.

Currently open:

If the queue is draining normally, ignore this. Otherwise:

  1. Check whether CI is dispatching at all — see the ci-dispatch-watchdog status on these PRs
  2. Merge the ones whose CI has finished and review has passed: gh pr merge <number> --squash --delete-branch
  3. Look for a loop opening near-identical PRs

Warn-only runaway detector — .github/workflows/pr-queue-gate.yml. It never blocks a merge.

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@mrveiss mrveiss removed the land-next Landing set: CI capacity goes to these PRs first (owner direction 2026-09-18, #15397) label Sep 19, 2026
@mrveiss

mrveiss commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

Carried by vehicle #17086, which includes this PR's approved head af31193be. Closed now as carried, per the owner's ruling (2026-09-19) that consolidated work shouldn't keep open duplicates or trigger extra CI. The branch is kept. The vehicle's own Closes lines close the linked issues when it lands. If #17086 is abandoned, this PR gets reopened.

@mrveiss mrveiss closed this Sep 19, 2026
@mrveiss
mrveiss deleted the issue-17000-agent-ws-auth branch September 19, 2026 07:32
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.

security(websocket): /api/intelligent_agent/stream runs agent goals with no authentication — an unauthenticated bypass of POST /process

1 participant