Skip to content

Scope the Engine's own git and gh credentials, so a wrong-repo write fails instead of being reported afterwards #679

Description

@serge-ivo

Follow-up to #676, which shipped the detection half in 7d246958. That change halts a run after a wrong-repo write and names the repository. It does not stop the write. This issue is the half that does.

The problem, measured

The Engine is a claude --dangerously-skip-permissions child process on the owner's machine, spawned at packages/browser-runner/src/coding/headless.ts:420 with env: mergeEnv(process.env, this.config.env) — the machine env inherited wholesale. It therefore reaches GitHub with the account's credentials:

  • git push is over SSH. Both remotes are git@github-personal:… (ProAgentStore/platform and proappstore-online/platform). Git ignores userinfo on an ssh URL, so no token of any kind can gate these pushes.
  • gh is authed from the keyring, account serge-ivo, scopes admin:org, delete_repo, gist, repo, workflow — every org the account can see.
  • Nothing injects a GitHub credential today.grep -rn "GH_TOKEN\|GITHUB_TOKEN" packages/browser-runner/src workers/api/src returns only redaction logic. grep -rni "credential\.helper\|GIT_CONFIG_COUNT\|ASKPASS\|insteadOf\|GIT_SSH_COMMAND" returns 0.
  • The Engine steers its own credential. Observed on two runs: export GH_CONFIG_DIR=~/.config/gh-personal; gh pr merge 111 --merge --repo proappstore-online/platform. Anything env-based is therefore a speed bump, not a boundary, unless the engine is also denied the ability to re-point it.

The tension that has to be designed around, not through

#676 item 2 wants writes scoped; item 3 wants reads broad, and gives a real example. A process holds exactly one gh token, so:

  • scoped GH_TOKENgh pr create -R other/repo fails at the credential (good) andgh pr view 138 --repo proappstore-online/platform fails too (breaks the protected pattern);
  • broad read-only GH_TOKEN → reads fine, but the engine cannot open a PR on its own registered repo.

Verified this is not hypothetical: the cross-repo read is the Engine running gh, not a platform tool. Instance a185b1db ("PAGS Coder"), session csess_42bdfe20, 2026-08-16T01:38:04Z, the Pilot's own instruction: Please run: `gh pr view 138 --repo proappstore-online/platform --json tit…` . The platform's github_read_pull was never called on 138 from that instance.

So the answer is a per-invocation credential chooser, not a single injected token.

Sketch

A runner-owned bin directory prepended to the Engine's PATH, holding a gh shim that classifies argv and routes the credential:

Plus, for git: GIT_CONFIG_* env injecting a credential helper, and url.<https>.insteadOf rules to force github SSH remotes through it.

Risks this carries, stated up front

  1. Forcing SSH → https means taking over the machine's git configuration. The owner uses a custom github-personal host alias; rewriting it affects their own reads, not just the agent's. This is the single biggest design risk and may be a reason to scope this issue to gh only and accept that git push stays ungated.
  2. gh's argv surface is large. Misclassifying a write as a read is a hole; default-denying unknown subcommands breaks reads. The classification needs to be a closed, reviewed vocabulary — the same shape as packages/browser-runner/src/coding/repo-write.ts.
  3. A shim on PATH is bypassable by absolute path (/opt/homebrew/bin/gh) or by the engine re-exporting its own env, as it already does. This raises the cost of a wrong-repo write; it does not make one impossible. Anything claiming otherwise in the UI would be worse than nothing.
  4. Engine-agnostic? Only Claude Code reports acts, but a PATH shim applies to every engine — so unlike A coding instance can push to and open PRs on any repo the user can reach, not just its registered one #676's gate, this one would work on Codex/Grok too. That is a point in its favour.

Acceptance

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1: blocks external usersMust be true before someone who is not the owner can run an agent (#68)backendBackend / Worker / API workcoderThe Coder wedge agent (#68) — Engine, Pilot, Co-pilot, Loop, OverseerenhancementNew feature or requestrunnerpackages/browser-runner + packages/cli — ships only via an npm publishsecuritySecurity hardening / audit finding

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions