You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit baa1ea6
Browse filesBrowse the repository at this point in the historyBrowse files
fix(security): reject non-local origins and bind loopback by default
Srcbook has no authentication and can execute arbitrary code, so the only
thing separating the user's own tab from any other page they have open is
the request origin. Nothing was checking it.
Verified before this change, against a running instance: any web page could
read every provider API key from GET /api/settings, read arbitrary files
from disk via POST /api/file, and — because the websocket accepted any
origin — open a socket and drive cell:update + cell:exec to run code as the
user. The server also bound 0.0.0.0, so all of it was reachable from the
whole network.
Changes:
- New server/security.mts owns the origin allowlist. Local origins on any
port are allowed, plus anything in SRCBOOK_ALLOWED_ORIGINS. Requests with
no Origin header are allowed, since browsers always set it on cross-origin
requests and non-browser clients (the CLI, curl) legitimately omit it.
- HTTP: the 41 per-route cors() calls become one cors + verifyOrigin pair on
the router. Disallowed origins get a 403 rather than just a missing CORS
header — CORS alone stops an attacker reading a response, but the request
still executes, which is all you need for POST /api/settings.
- WebSocket: origin checked via verifyClient, so rejection happens at the
handshake. Checking in the connection handler is too late; the client
observes an open socket that is only torn down afterwards.
- Bind 127.0.0.1 unless HOST says otherwise, and warn when it doesn't. This
makes docker-compose's HOST variable real — it was previously read by
nothing. Compose now sets HOST=0.0.0.0 because Docker forwards published
ports to the container IP; host-side exposure stays controlled by
HOST_BIND, which already defaulted to loopback.
- Guard process.send in the CLI server. It only exists under IPC, so running
the server directly crashed it right after printing "running at ..." —
which is how this was found while testing the bind change.
12 tests for the allowlist, covering the substring cases that make this kind
of check go wrong (localhost.evil.com, example.com.evil.com).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0 commit comments