AntFleet

Disagreement · 6084d1ea-anthropic-0

Missing authentication/authorization on secrets API exposes GitHub secret management to any caller

mismatch
repo 6f7fc663·PR #3·reviewed 1 week ago

Primary finding

Missing authentication/authorization on secrets API exposes GitHub secret management to any caller

criticalsecurityhigh
  • dashboard/app/api/secrets/route.ts:59-119
The route exports GET/POST/DELETE handlers that list, set, and delete GitHub repository secrets via the `gh` CLI. There is no authentication, authorization, CSRF check, origin check, or session validation. If this Next.js dashboard is ever exposed beyond localhost (or reachable via a CSRF/DNS-rebinding/LAN attack from a browser), any caller can enumerate secret names, overwrite any secret matching `[A-Z][A-Z0-9_]+` (including builtins like ANTHROPIC_API_KEY or GH_GLOBAL) with attacker-controlled values, or delete them — silently pivoting CI to attacker credentials. Even on a local dev box this is reachable via CSRF because POST/DELETE bodies are JSON but no `Origin`/`Content-Type` enforcement exists (a form-encoded POST will be parsed by `request.json()` failing, but a fetch from a malicious page with `Content-Type: text/plain` can still send JSON in many configurations; more importantly any tool on the host can hit it).

Recommendation

Require authentication on all three handlers (session/token check, or restrict the route to bind only to loopback and verify Origin/Host headers to mitigate CSRF/DNS-rebinding). At minimum, gate behind an env-token header and reject if request `Origin` is not same-origin.

Counterpart finding

Secret values can leak via CLI arguments and error messages when setting secrets

highsecurityhigh
  • dashboard/app/api/secrets/route.ts:95-99
  • dashboard/app/api/secrets/route.ts:101-103
Passing the secret as a command-line argument exposes it in process arguments (observable via OS process listings) and may appear in thrown Error.message (which typically includes the failed command). The handler returns error.message directly to clients, potentially echoing the secret back on failure.

Recommendation

- Pass the secret via stdin rather than argv: execFileSync('gh', ['secret','set', name, '-b-'], { input: value, stdio: ['pipe','pipe','pipe'], cwd: process.cwd() }). - Never return raw error.message to clients. Return a generic message and log a sanitized error server-side (ensure logs do not contain secrets).

Why this didn't post

This finding didn't meet AntFleet's unanimous agreement threshold. Both frontier models review every PR independently; only findings they both flag with the same severity and category are posted to the PR. This one fell through.

read the methodology →