Skip to content

fix(server): safe-by-default HTTP boundary + tests (CRITICAL)#28

Merged
willgitdata merged 1 commit into
mainfrom
fix/critical-server-defaults
May 12, 2026
Merged

fix(server): safe-by-default HTTP boundary + tests (CRITICAL)#28
willgitdata merged 1 commit into
mainfrom
fix/critical-server-defaults

Conversation

@willgitdata
Copy link
Copy Markdown
Owner

Summary

Address the critical server-defaults finding from the code review. Before this patch, a fresh npx augur-server or docker compose up exposed destructive admin endpoints to whoever could reach the port. After: doing nothing yields a safe state, and only explicit choices open the door.

What changed

Library (buildServer)

  • cors defaults to false (was true = reflect any origin).
  • /admin/* and DELETE /traces return 503 until apiKey is configured. They never run unauthenticated, even on loopback.
  • When apiKey is set, every non-public endpoint requires x-api-key (admin included). Auth rejection uses return reply for clarity.

CLI (augur-server)

  • HOST defaults to 127.0.0.1 (was 0.0.0.0). Bind LAN-wide only by explicit opt-in.
  • New AUGUR_CORS env var: comma-separated allowlist or *.
  • Loud startup warnings for non-loopback-without-key and CORS-reflect-all.

Docker

  • docker-compose.yml publishes the host port to 127.0.0.1:3001 only.
  • HOST=0.0.0.0 inside the container (correct for a single-process container; the published-port restriction limits exposure).
  • Placeholder AUGUR_API_KEY commented in for easy enable.

Testspackages/server/src/server.test.ts, 22 new tests, the first tests this package has ever had:

  • Public routes reachable without auth (even when key is set).
  • All non-public routes require the key when configured.
  • /admin/* + DELETE /traces → 503 when key unset.
  • /admin/* + DELETE /traces → 401 when key set but missing/wrong header.
  • Query-string variants don't bypass the destructive check.
  • CORS default false; explicit origin allowed; other origins rejected.
  • Input validation (400) covered.

Test plan

  • pnpm build → clean
  • pnpm typecheck → clean
  • pnpm test192 core + 22 server, all passing
  • Manual: npx augur-server → server listens on 127.0.0.1:3001, /admin/clear returns 503.
  • Manual: AUGUR_API_KEY=k npx augur-server/admin/clear returns 401 without header, 200 with x-api-key: k.
  • Manual: HOST=0.0.0.0 npx augur-server (no key) → loud warning logged.

Notes

  • This is CRITICAL issue Post-rename cleanup from doc audit (4 fixes, 10 files, ±21) #1 from the code review.
  • Backwards-compatible for users who set AUGUR_API_KEY and an explicit HOST. Users who relied on the implicit HOST=0.0.0.0 need to set HOST=0.0.0.0 (now an explicit choice).
  • CORS default change is a minor breaking change. Documented in the README's new "Security defaults" section.

🤖 Generated with Claude Code

Previously a fresh `npx augur-server` or `docker compose up` shipped:
- HOST=0.0.0.0 (listens on every interface)
- AUGUR_API_KEY unset (no auth required)
- cors:true (Access-Control-Allow-Origin reflects any origin)
- /admin/clear, /admin/stats, DELETE /traces open to anyone who
  could reach the port

In combination, any operator who started the server before deciding
on an auth posture exposed destructive endpoints to their LAN. This
patch makes "I didn't configure anything" a safe state instead.

Library changes (`buildServer`):
  - cors default → `false` (was `true`). Opt in explicitly.
  - Destructive endpoints (/admin/*, DELETE /traces) return 503
    "admin endpoints disabled" until apiKey is configured. They
    never run unauthenticated, even from loopback.
  - When apiKey is set, every non-public endpoint requires the header
    (admin endpoints included). Auth rejection now uses `return reply`
    so the intent is unambiguous.

CLI changes (`augur-server`):
  - HOST default → 127.0.0.1 (was 0.0.0.0). Loopback-only out of
    the box. Set HOST=0.0.0.0 explicitly to expose on the LAN.
  - New AUGUR_CORS env var: comma-separated allowlist or `*` for any.
  - Loud warning at startup when binding non-loopback without an
    API key, and when CORS reflects every origin.

Docker:
  - docker-compose.yml binds the published port to 127.0.0.1:3001
    so a default `docker compose up` doesn't publish on the LAN.
  - HOST=0.0.0.0 inside the container (single process, no exposure
    surface there).
  - Commented AUGUR_API_KEY placeholder.

Tests (`packages/server/src/server.test.ts`, 22 new):
  - Public routes (/health, /openapi.json, /docs) reachable without
    auth, even when apiKey is set.
  - /search /index /traces require apiKey when configured;
    correct / wrong / missing headers all covered.
  - /admin/* and DELETE /traces return 503 when apiKey is unset.
  - /admin/* and DELETE /traces still require the header when set.
  - Query-string variants don't bypass the destructive check.
  - CORS: default false, explicit string origin allowed, other
    origins rejected.
  - Input validation: /search missing query → 400, /index non-array
    documents → 400.

The server now has its first test file (was zero coverage before).
prepublishOnly runs tests; tests run on every CI build.

Total: 192 core tests + 22 server tests, all passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@willgitdata willgitdata merged commit 81edf25 into main May 12, 2026
4 checks passed
@willgitdata willgitdata deleted the fix/critical-server-defaults branch May 12, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant