Describe the bug
In HTTP mode the lockdown repository access cache is shared across callers. Entries are keyed on owner/repo alone inside a process-wide cache2go table, so a trust decision computed under one caller's credentials — repository visibility, author push access — can be served to a different caller whose own credentials were never checked.
Stdio mode is not affected: it runs a single cache for one process-wide identity.
Affected version
Current main as of 2026-08-19.
Steps to reproduce the behavior
- Run the server in HTTP mode.
- Issue a lockdown-gated request for
owner/repo as identity A. - Issue the same request for the same
owner/repo as identity B. - Observe that B is served A's cached trust decision, without B's own credentials being used.
Expected vs actual behavior
Expected: a cached trust decision is scoped to the identity whose credentials produced it.
Actual: decisions are shared process-wide under a bare owner/repo key.
Note on cache freshness
The cache's sliding/idle expiry is not a defect. cache2go refreshes an entry's TTL on access by design, and this cache has always relied on that — the hand-rolled cache it replaced kept the same semantics. A hot repository stays cached; idle entries are reclaimed.
Whether repo-access decisions should additionally carry a fixed maximum age is a separate freshness question rather than an isolation one, and is deliberately not part of this fix. A fixed age makes every hot repository refetch on a timer, so it would want singleflight (or a shared store such as Redis) and possibly stale-while-revalidate to avoid a refetch stampede. Worth revisiting on its own terms.
Logs
N/A
Describe the bug
In HTTP mode the lockdown repository access cache is shared across callers. Entries are keyed on
owner/repoalone inside a process-widecache2gotable, so a trust decision computed under one caller's credentials — repository visibility, author push access — can be served to a different caller whose own credentials were never checked.Stdio mode is not affected: it runs a single cache for one process-wide identity.
Affected version
Current
mainas of 2026-08-19.Steps to reproduce the behavior
owner/repoas identity A.owner/repoas identity B.Expected vs actual behavior
Expected: a cached trust decision is scoped to the identity whose credentials produced it.
Actual: decisions are shared process-wide under a bare
owner/repokey.Note on cache freshness
The cache's sliding/idle expiry is not a defect.
cache2gorefreshes an entry's TTL on access by design, and this cache has always relied on that — the hand-rolled cache it replaced kept the same semantics. A hot repository stays cached; idle entries are reclaimed.Whether repo-access decisions should additionally carry a fixed maximum age is a separate freshness question rather than an isolation one, and is deliberately not part of this fix. A fixed age makes every hot repository refetch on a timer, so it would want singleflight (or a shared store such as Redis) and possibly stale-while-revalidate to avoid a refetch stampede. Worth revisiting on its own terms.
Logs
N/A