SECURITY: builder agent holds the Forgejo admin PAT (3 channels) -> prompt-injected agent can exfiltrate ALL production secrets #16

Closed
opened 2026-06-29 19:45:39 +00:00 by plat · 0 comments
Owner

Severity: high. The in-cluster builder agent runs Claude Code with --dangerously-skip-permissions and reads attacker-influenceable input (issue/PR text). It currently holds the Forgejo ADMIN PAT (PLAT_TOKEN, dispatcher.ts:54) through THREE channels (verified in agents/src/runner.ts):

  1. runner.ts:171 — spawnEnv() does {...process.env}, copying PLAT_TOKEN into every subprocess.
  2. runner.ts:435 — the git clone remote is https://plat:<PLAT_TOKEN>@host/o/r.git, persisted into .git/config (recoverable via git remote -v).
  3. runner.ts:292 — .mcp.json carries Authorization: Bearer ${PLAT_TOKEN}.

Impact: with the admin token a prompt-injected agent can overwrite/read secrets, rewrite branch protection, and — worst — author a pull_request workflow that emits ${{ toJSON(secrets) }} to EXFILTRATE EVERY PRODUCTION SECRET (confirmed: _app-template/.forgejo/workflows/preview.yml runs on: pull_request and already consumes secrets.*; Forgejo populates secrets for same-repo non-fork PR workflows).

Fix (Phase 0 of the secret-scoping design #15 — must ship first):

  1. Strip the admin PAT from the subprocess entirely; the dispatcher keeps it for its own work.
  2. Per-(owner,app) git credential, push-only — a per-repo deploy key in the remote URL, never the admin PAT.
  3. Per-app non-admin scoped MCP bearer; the MCP server authorizes every call against the run (o,a) instead of collapsing to plat admin.
  4. spawnEnv builds the subprocess env from an explicit allowlist (Claude+git essentials + the per-run dev token), not ...process.env.
  5. Branch protection on .forgejo/workflows/** (binds only once the agent is non-admin — admin bypasses protections, which is why #1 is the prerequisite).

One change (drop the agent admin; give it a scoped non-admin push credential) makes scope-in-Variables real, makes branch protection bind, and closes the exfil. Highest-value security fix on its own; blocks #15 Phase 1.

**Severity: high.** The in-cluster builder agent runs Claude Code with --dangerously-skip-permissions and reads attacker-influenceable input (issue/PR text). It currently holds the Forgejo ADMIN PAT (PLAT_TOKEN, dispatcher.ts:54) through THREE channels (verified in agents/src/runner.ts): 1. runner.ts:171 — spawnEnv() does {...process.env}, copying PLAT_TOKEN into every subprocess. 2. runner.ts:435 — the git clone remote is https://plat:<PLAT_TOKEN>@host/o/r.git, persisted into .git/config (recoverable via git remote -v). 3. runner.ts:292 — .mcp.json carries Authorization: Bearer ${PLAT_TOKEN}. **Impact:** with the admin token a prompt-injected agent can overwrite/read secrets, rewrite branch protection, and — worst — author a pull_request workflow that emits ${{ toJSON(secrets) }} to EXFILTRATE EVERY PRODUCTION SECRET (confirmed: _app-template/.forgejo/workflows/preview.yml runs on: pull_request and already consumes secrets.*; Forgejo populates secrets for same-repo non-fork PR workflows). **Fix (Phase 0 of the secret-scoping design #15 — must ship first):** 1. Strip the admin PAT from the subprocess entirely; the dispatcher keeps it for its own work. 2. Per-(owner,app) git credential, push-only — a per-repo deploy key in the remote URL, never the admin PAT. 3. Per-app non-admin scoped MCP bearer; the MCP server authorizes every call against the run (o,a) instead of collapsing to plat admin. 4. spawnEnv builds the subprocess env from an explicit allowlist (Claude+git essentials + the per-run dev token), not ...process.env. 5. Branch protection on .forgejo/workflows/** (binds only once the agent is non-admin — admin bypasses protections, which is why #1 is the prerequisite). One change (drop the agent admin; give it a scoped non-admin push credential) makes scope-in-Variables real, makes branch protection bind, and closes the exfil. Highest-value security fix on its own; blocks #15 Phase 1.
plat closed this issue 2026-06-30 17:50:49 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-platform/mitosis#16
No description provided.