Skip to content

v4.27.0 — SECURITY: no wildcard CORS, foreign origins and hosts refused without auth

Latest

Choose a tag to compare

@GuyMannDude GuyMannDude released this 19 Oct 07:21
· 1 commit to master since this release

Problem. GHSA-682p-grrx-wrwg (high, reported 2026-08-20 by kasparovabi).
The server shipped cors_origins: ["*"] by default, and the default auth
posture is none. A browser on the same machine is a loopback client, so any
web page the user visited could read /facts, /sessions, transcripts and
dreams, write facts, pause capture, and plant trajectories — the loopback peer
check never saw a difference. A DNS-rebinding page did the same with no
Origin header at all. Deployments with auth_token or scoped tokens were
not affected (the token is the gate, and a page cannot attach it).

Fix (contributed by kasparovabi, merged as-is after maintainer review).
server.cors_origins now defaults to [] in the dataclass, the YAML loader
and agentb.yaml.example; CORSMiddleware is only installed when origins are
listed. When no auth is configured and allow_unauthenticated is off, the
request-time loopback guard also refuses any request whose Origin is neither
loopback nor listed (a "*" left in an old YAML does not reopen it, and CORS
"simple" requests that skip preflight are covered), and any request whose
Host is not a loopback address, localhost, the configured bind host or this
machine's own hostname. Origin-less clients (CLI, curl hooks, the MCP bridges)
and the same-origin Pocket UI are unchanged.

Maintainer follow-up (code review of the fix). The Origin check now
applies whenever no token is configured, allow_unauthenticated: true (the
Docker opt-in) included — opting open trusts LAN peers, not every web page a
LAN user visits. The peer and Host checks stay loopback-mode only, so an
opted-open server reached by name from other machines keeps working; it
remains exposed to DNS-rebinding READS until it sets a token. An Origin
naming this machine's hostname is accepted like the matching Host (a Pocket
tab opened at http://<hostname>:50001/app/ could load but not post).
socket.getfqdn() dropped from startup: a blocking reverse lookup that can
stall for seconds on macOS or with broken DNS; gethostname() covers it.

Operator note. If you reach an unauthenticated loopback server through a
hosts-file alias or a Host-preserving reverse proxy, set auth_token or
bind an alias your machine owns. file:// pages and browser extensions now
need an entry in cors_origins. Tests: tests/test_cors_origin_guard.py.


Credit. Reported privately on 2026-08-20, with a tested fix branch on 2026-10-18, by @kasparovabi. The report sat unread in triage for 59 days because nothing on our side watched the private-advisory queue; that watch exists as of today. Thank you, and apologies for the wait. Advisory: GHSA-682p-grrx-wrwg