Skip to content

fix(agent): treat an emptied sandbox env file as GitHub logout#3611

Open
VojtechBartos wants to merge 3 commits into
mainfrom
vojtab/github-token-empty-file-logout
Open

fix(agent): treat an emptied sandbox env file as GitHub logout#3611
VojtechBartos wants to merge 3 commits into
mainfrom
vojtab/github-token-empty-file-logout

Conversation

@VojtechBartos

@VojtechBartos VojtechBartos commented Jul 21, 2026

Copy link
Copy Markdown
Member

On a GitHub actor transition (multiplayer follow-ups on the PostHog side), the backend logs the sandbox out by emptying the token vars in /tmp/agent-env. This agent-server was resurrecting the previous actor's token anyway.

flowchart TD
    R([resolveGithubToken]) --> F{ /tmp/agent-env present? }
    F -- "no (local / desktop)" --> P[use process.env]
    F -- yes --> T{ token var non-empty? }
    T -- yes --> U[use the file token]
    T -- "no (emptied = logout)" --> E["return '' (no token)"]
Loading

Changes

  • resolveGithubToken: a present-but-empty file is an explicit logout (return ""), not a fallback to process.env. Only an absent/unmanaged file defers to the process env. ("" ?? processEnv === "", so an emptied file no longer resurrects the frozen launch-time token.)
  • gh attribution/whoami (fetchPrAttribution, fetchGhLogin): run as the current actor via the live file token instead of inheriting process.env.

Companion PR (required together)

PostHog/posthog#71941 (backend): removes the frozen process-env token and adds the rebind/logout gate.

How did you test this code?

github-token.test.ts (11 pass), including the assertion that an emptied file does not resurrect the process-env token. Validated live in a running sandbox (old resolver returns the previous actor's token; new returns ""), and end-to-end on a dedicated GitHub org where both users have access to the same repo: no-access follow-up blocked, with-access follow-up rebinds.

Tested locally together with posthog/posthog changes.

Agent context

Model: Claude Opus 4.8.

resolveGithubToken prefers the live /tmp/agent-env file (which the backend
rewrites when the sandbox's actor changes mid-session) but fell back to the
process env whenever the file yielded no token. On an actor transition the
backend logs the sandbox out by emptying the token vars in that file, so the
fallback resurrected the previous actor's token — frozen in the agent-server's
launch-time process env — letting a follow-up actor push as the prior actor.

Distinguish a file that carries the token vars but empties them (an explicit
logout: return "") from a file that never had them (unmanaged: defer to the
process env). Only the latter falls back.
@trunk-io

trunk-io Bot commented Jul 21, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 775a0f1.

@posthog

posthog Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

👋 Visual changes detected for this PR.

Review and approve in PostHog Visual Review

If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix.

fetchPrAttribution and fetchGhLogin called gh with no env, inheriting the
process env — the actor's token frozen at launch. After a mid-session actor
transition that reports the wrong identity, and once the backend stops baking
the token into the process env these calls would run unauthenticated. Resolve
the live sandbox token (same file-first path as the signed-commit tools) and
pass it explicitly; fall back to the process env only when unmanaged.
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Security Review

The explicit logout state prevents normal reuse of a frozen process token, but two actor-boundary paths remain: a cached login can outlive a credential change, and any env-file read error falls back to the previous process credential.

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/agent/src/server/agent-server.ts:3832
**Cached Login Survives Actor Changes**

After the first successful lookup, `ghLoginPromise` keeps the old login across credential refreshes and session cleanup. When `/tmp/agent-env` is rebound, `fetchPrAttribution()` uses the new token while `fetchGhLogin()` returns the previous actor, so the new actor's PR can be rejected or an old actor's PR can be attached to the current run.

### Issue 2 of 2
packages/agent/src/utils/github-token.ts:21-24
**Unreadable Credential File Fails Open**

The catch treats every read error as an unmanaged sandbox. If `/tmp/agent-env` exists but is temporarily unreadable during an actor transition, `resolveGithubToken()` falls back to the frozen process token and GitHub operations can continue as the previous actor; only a genuinely absent file should enable that fallback.

Reviews (1): Last reviewed commit: "fix(agent): run gh attribution/whoami as..." | Re-trigger Greptile

Comment thread packages/agent/src/utils/github-token.ts
Comment thread packages/agent/src/server/agent-server.ts Outdated
Comment thread packages/agent/src/utils/github-token.ts Outdated
@veria-ai

veria-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Address review findings on the emptied-env-file logout:
- list_repos built its gh env with a truthiness check, so an empty (logged-out)
  token fell back to the raw process env and could enumerate the previous
  actor's private repos. Clear both token vars on a managed logout; only an
  undefined (unmanaged) token inherits the process env.
- fetchGhLogin memoized the login across actor transitions, so after a rebind
  attribution used the previous actor's login. Key the cache on the live token.
- readGithubTokenFromSandboxEnvFile treated every read error as an unmanaged
  sandbox; a present-but-unreadable file during a transition then resurrected
  the frozen process token. Only ENOENT falls back; other errors fail closed.
@VojtechBartos
VojtechBartos requested a review from a team July 21, 2026 14:07
@posthog

posthog Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Nothing worth raising this time, so here's a calming gif instead:

Someone relaxing in a sunny garden

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