From ca1da7f095d4c0286f60300e79f9be0263ae03e8 Mon Sep 17 00:00:00 2001 From: Sebastion Date: Wed, 1 Jul 2026 14:40:31 +0100 Subject: [PATCH] fix(observability): bind Bun server to 127.0.0.1 to prevent LAN exposure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v2.3 Observability server exposes several unauthenticated endpoints with 'Access-Control-Allow-Origin: *', including: GET /api/activities — returns kitty tab titles (which typically contain ssh hostnames, filenames, running commands, project paths, git branches, etc.) POST /api/haiku/summarize — proxies requests to the Anthropic API using the operator's ANTHROPIC_API_KEY from ${PAI_DIR}/.env GET /api/tasks, GET /api/tasks/:id[/output] Bun.serve() was called without an explicit hostname, which means Bun binds to 0.0.0.0 by default. On any machine with LAN, VPN, or Tailscale peers, anyone on the same network can hit these endpoints and either read the developer's terminal titles or burn their Anthropic quota / rate limit. Fix: pass hostname: '127.0.0.1' to Bun.serve so the process only listens on the loopback interface. This matches how other PAI Bun servers in the tree are configured (e.g., v5.0.0/.claude/PAI/PAI-Install/web/server.ts). The client always fetches from http://localhost:4000, so this is transparent to normal use — only off-host access is blocked. --- Releases/v2.3/.claude/Observability/apps/server/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Releases/v2.3/.claude/Observability/apps/server/src/index.ts b/Releases/v2.3/.claude/Observability/apps/server/src/index.ts index bf52021f94..32167b07a4 100755 --- a/Releases/v2.3/.claude/Observability/apps/server/src/index.ts +++ b/Releases/v2.3/.claude/Observability/apps/server/src/index.ts @@ -48,6 +48,11 @@ startTaskWatcher((task: BackgroundTask) => { // Create Bun server with HTTP and WebSocket support const server = Bun.serve({ port: 4000, + // Bind to loopback only. This server exposes /api/activities + // (kitty tab titles) and /api/haiku/summarize (proxied Anthropic API + // key) with `Access-Control-Allow-Origin: *` and no authentication — + // it must never be reachable from the network. + hostname: '127.0.0.1', async fetch(req: Request) { const url = new URL(req.url);