Fix top 5 priority issues: WS auth bypass, read-only assistant, init-timeout UX, idle disconnects#238
Merged
Merged
Conversation
Starlette's http middleware never runs for WebSocket scopes, so the localhost guard did not protect any WS route and any web page could hijack the terminal PTY socket (CWE-306). Adds a pure-ASGI WebSocketOriginMiddleware that rejects handshakes from non-localhost (or, in remote mode, non-matching-Host) origins with close code 4403 before the route handler runs, covering all five WS endpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The assistant and expand chat sessions only forbade code modification via prompt text; with permission_mode=bypassPermissions, Write/Edit/Bash remained fully callable. Add disallowed_tools (which actually removes tools from the model) plus a permissions deny list in the generated settings, block coding- agent-only feature MCP tools, and strengthen the assistant system prompt to direct users to create features for the coding agents instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd enforce Python 3.11+ Fail fast with a clear message in server/main.py and start.py when running on Python < 3.11 (previously the unsupported interpreter only manifested as an opaque SDK 'Control request timeout: initialize'). Add a shared format_client_init_error() helper in chat_constants.py and use it in the expand, assistant, and spec chat sessions so handshake timeouts yield actionable guidance (auth, CLI install, antivirus, timeout env var) over the WebSocket instead of a bare 'Failed to initialize Claude'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gered reconnect The UI socket had a fire-and-forget ping with no dead-connection detection, timer-only reconnects that browsers throttle in idle/background tabs, and a cleanup path whose orphaned onclose could schedule zombie reconnects. The hook now tracks last inbound message time, force-closes stale sockets (90s) from a 20s heartbeat, reconnects immediately on visibilitychange/online, and detaches handlers on intentional close. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves the top 5 triaged issues by priority: #235 (P0), #233, #221, #215, #210.
#235 — Pre-auth RCE via terminal WebSocket (CWE-306 / CSWSH)
Starlette's HTTP-only localhost/CORS middleware never runs for websocket scopes, so all WS endpoints (including the PTY terminal) accepted cross-origin and remote connections unauthenticated. Added a pure-ASGI
WebSocketOriginMiddleware(server/utils/ws_security.py, registered unconditionally inserver/main.py) that rejects handshakes with close code 4403 before any route handler runs unless the Origin is a localhost variant (or, withAUTOFORGE_ALLOW_REMOTE=1, matches the Host header). Covers all five WS endpoints and any added later. Origin-less non-browser clients and HTTP traffic are unaffected, so the existing UI (prod :8888, Vite dev :5173 via proxy) keeps working. Includes new tests intest_ws_security.py.#233 — Assistant implementing code instead of delegating
The "read-only" assistant relied on prompt text while running with permissions that skipped all checks. Now enforced at the tool level:
disallowed_tools(Write/Edit/MultiEdit/NotebookEdit/Bash + coding-agent-only feature MCP tools) actually removes the tools from the model, plus apermissions.denylist in the generated settings as belt-and-suspenders, plus a strengthened system prompt directing users to create features for the coding agents. The same flaw existed in the expand-project chat and got the same hardening.#221 — "Control request timeout: initialize" opening expand/assistant chat
Two-part fix: fail fast with a clear error in
server/main.pyandstart.pywhen running Python < 3.11 (the reporter was on unsupported 3.10), and a sharedformat_client_init_error()helper (server/services/chat_constants.py) that translates the opaque SDK handshake timeout into actionable guidance (claude login, CLI reinstall, antivirus on Windows,CLAUDE_CODE_STREAM_CLOSE_TIMEOUT). All three chat sessions now surface that message over the WebSocket instead of a bare "Failed to initialize Claude".#215 — UI constantly disconnecting after idle
Hardened
ui/src/hooks/useWebSocket.ts: 20s application-level heartbeat with dead-connection detection (force-close + immediate exponential-backoff reconnect if no traffic for 90s instead of sitting stale),visibilitychange/onlinelisteners to reconnect immediately when a backgrounded tab wakes (bypassing browser timer throttling), and fixes for zombie reconnects/duplicate sockets by clearing pending timers and detaching handlers before intentional closes. Server already answers{"type":"ping"}with pong, so no backend change was needed.#210 — Crash on unknown
rate_limit_eventmessage typeVerified as already fixed on master (PR #211): every SDK message-stream consumer handles
MessageParseErrorby logging and resuming (agent.py:82-136; the three chat sessions viasafe_receive_response()inserver/services/chat_constants.py:99-124), andcheck_rate_limit_error()short-circuitsMessageParseErrorbefore the rate-limit regex. No changes needed — issue #210 can be closed once confirmed by the reporter.Verification
ruff check .— passpython test_security.py+test_security_integration.py— pass (169 tests)pytest test_client.py test_rate_limit_utils.py test_dependency_resolver.py— passui: npm run lintandnpm run build— passAn adversarial correctness/security review of the diff is finishing; any confirmed findings will be pushed as follow-up commits on this branch.
🤖 Generated with Claude Code