Skip to content

security: remove API key from browser bundle + full audit hardening (v0.2.0)#2

Merged
projectblackboxllc merged 1 commit into
mainfrom
feature/security-hardening-v0.2
May 25, 2026
Merged

security: remove API key from browser bundle + full audit hardening (v0.2.0)#2
projectblackboxllc merged 1 commit into
mainfrom
feature/security-hardening-v0.2

Conversation

@projectblackboxllc

Copy link
Copy Markdown
Owner

Summary

This PR addresses all findings from the post-launch security review, with the most significant being an architectural fix to ensure the API key never reaches the browser.

  • P1-1 (critical): API key removed from browser bundle via Next.js server-side proxy
  • P1-2 (high): SSRF guard extended to agent endpoint field (was only on webhookUrl)
  • P1-3 (high): Stack traces stripped from HTTP error responses
  • P2-1 (medium): CORS defaults to block-all in production when origins not configured
  • P2-2 (medium): POST /heartbeats returns 404 for unknown agent IDs
  • P2-3 (medium): SQL table-name allowlist in db.ts
  • P3-2 (low): Security headers added to Next.js dashboard (X-Frame-Options, CSP, etc.)
  • P3-4 (low): CLI docker-compose template no longer writes NEXT_PUBLIC_OPERATORBOARD_API_KEY

Key architectural change (P1-1)

Before: NEXT_PUBLIC_OPERATORBOARD_API_KEY was baked into the Next.js client bundle at build time, visible in DevTools, network requests, and bundle source to any user.

After: A catch-all proxy route (apps/web/src/app/api/[...proxy]/route.ts) runs server-side. The browser calls /api/*, the proxy reads OPERATORBOARD_API_KEY from the Node.js environment and injects it into upstream requests to Fastify. The key never reaches the client.

Migration: replace NEXT_PUBLIC_OPERATORBOARD_API_KEY with OPERATORBOARD_API_KEY (no prefix) on the web service. Replace NEXT_PUBLIC_API_URL with OPERATORBOARD_API_URL. See apps/web/.env.example for the updated reference.

Files changed

File Change
apps/web/src/app/api/[...proxy]/route.ts New — server-side proxy route
apps/web/src/app/page.tsx Remove API_KEY constant; fetch via /api/* proxy
apps/web/next.config.ts Add security headers
apps/web/Dockerfile Remove build ARGs for key/URL
apps/web/.env.example Updated env var names
apps/api/src/index.ts SSRF on endpoint, CORS fix, heartbeat 404, error handler
apps/api/src/db.ts Table-name allowlist
docker-compose.yml Runtime env vars for web service
packages/cli/src/cli.ts Updated compose template
CHANGELOG.md v0.2.0 release notes
SECURITY.md Proxy architecture docs + updated checklist

Test plan

  • docker compose up — dashboard loads, all API calls succeed through proxy
  • Open DevTools → Network: confirm no x-operatorboard-key header in browser requests
  • Open DevTools → Sources: confirm OPERATORBOARD_API_KEY value not in bundle
  • Register an agent with a private IP endpoint (http://192.168.1.1/task) → expect 400
  • POST /heartbeats with unknown agentId → expect 404
  • Confirm stack traces absent from 500 responses
  • pnpm -r typecheck passes

🤖 Generated with Claude Code

….2.0)

P1-1 (critical): Remove API key from browser bundle
- Add Next.js server-side proxy route (apps/web/src/app/api/[...proxy]/route.ts)
- Browser calls /api/* — proxy injects OPERATORBOARD_API_KEY server-side
- Remove NEXT_PUBLIC_OPERATORBOARD_API_KEY from page.tsx, Dockerfile, docker-compose
- Update CLI docker-compose template to use runtime env vars (not build args)
- Update .env.example with correct server-side var names

P1-2: Extend SSRF guard to agent endpoint field
- Apply isSafeWebhookUrl() to endpoint at POST /agents registration
- Add defense-in-depth check in executeTaskRun before dispatching
- Add defense-in-depth check in POST /agents/:id/test before health fetch

P1-3: Strip stack traces from HTTP error responses
- setErrorHandler returns generic message; stack logged server-side only

P2-1: CORS blocks all origins in production if OPERATORBOARD_CORS_ORIGINS unset
- Was: fall back to wildcard allow-all
- Now: return false (block all) with a startup warning log

P2-2: POST /heartbeats returns 404 for unknown agent IDs

P2-3: SQL table-name allowlist (assertAllowedTable in db.ts)

P3-2: Security headers in next.config.ts
- X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy,
  Strict-Transport-Security, Content-Security-Policy

Update CHANGELOG.md and SECURITY.md with full findings documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@projectblackboxllc projectblackboxllc merged commit 80f03db into main May 25, 2026
1 check failed
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