12 Commits

Author SHA1 Message Date
Sam Morrow 95b347e945 fix(lockdown): drop fixed-age expiry, keep per-identity cache isolation
The cache's idle/sliding TTL is cache2go's documented behaviour and was
deliberate in both the original hand-rolled cache and the cache2go
migration: a hot repo keeps serving from cache and only idle entries are
reclaimed. Replacing it with a fixed max age traded that away for a
periodic refetch on every hot repo, which is a freshness change rather
than the isolation fix this issue is about.

Remove createdAt, the injected clock, entryExpired, the createdAt
preservation on entry updates, and the tests that only existed to prove
bounded non-sliding expiry. Restore the original sliding semantics.

Keep the per-caller isolation, which is the actual defect: entries were
keyed on owner/repo alone in a process-wide table, so a trust decision
computed under one caller's credentials could be served to another
caller whose own credentials were never checked. Entry keys now carry a
SHA-256 digest of the request identity, inside a single bounded table.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 16:30:34 +02:00
Sam Morrow acd2afc766 refactor(lockdown): trim comments to non-obvious invariants
The cache changes carried explanatory comments that restated the code or
narrated what each step did. Drop them and keep only what the code cannot
express: that cache2go never reclaims a named table, that its own expiry
slides on every read, that createdAt survives entry updates, and that
RepoAccessOpts is shared across requests. Exported options keep a short
doc comment.

Comment-only; no behavior change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 16:30:34 +02:00
Sam Morrow 56c9088c60 fix(lockdown): scope repo-access cache per identity via entry keys
Isolating identities by deriving a cache2go table name per token grew a
process-wide registry that is never reclaimed: cache2go creates each named
table on first use and never evicts it, so every distinct bearer token —
including invalid ones, since the table was built before GitHub validated
the token — permanently added a table.

Keep a single cache table and scope entries instead. WithIdentity stores a
SHA-256 digest of the identity and prefixes each entry key with it, so
different identities still cannot observe each other's trust decisions,
while per-identity state is reclaimed by the table's ordinary TTL cleanup.
WithCacheName stays for tenant/test isolation, with docs warning against
deriving names from request data.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 16:30:34 +02:00
Sam Morrow a429a8759f fix(lockdown): bound repo-access cache expiry and isolate it per identity
The repo-access cache used by lockdown mode relied on cache2go's sliding
expiry: every read extends an entry's life, so a frequently-accessed
entry could keep a stale trust decision (e.g. revoked push access)
alive indefinitely instead of refreshing after its TTL.

Separately, cache2go.Cache(name) returns a process-wide singleton table
keyed by name. In HTTP mode, RequestDeps.GetRepoAccessCache built a new
RepoAccessCache per request but always reused the same default-named
table, so trust decisions computed under one caller's credentials could
be served to a different caller for the same owner/repo, without ever
validating the second caller's own access.

Fixes:
- Track each cache entry's original creation time and bound its maximum
  age from that fixed point, not from last access, so entries are
  refreshed after a fixed TTL regardless of read frequency.
- Add lockdown.CacheNameForIdentity, which derives a stable, hashed
  cache-table name from a request identity (e.g. auth token). Two calls
  for the same identity return the same name (reusing a warm cache
  across a session's repeated requests); different identities always
  get different names (no shared cache state).
- RequestDeps.GetRepoAccessCache now scopes each request's cache to the
  requesting token's identity via CacheNameForIdentity, closing the
  cross-identity leak in HTTP/multi-tenant deployments. Stdio mode is
  unaffected: it constructs a single RepoAccessCache for the whole
  process lifetime, as before.

Tests added:
- TestRepoAccessCacheBoundedExpiryIgnoresRepeatedAccess and
  TestRepoAccessCacheNewUserDoesNotResetEntryAge exercise bounded expiry
  deterministically via an injectable clock (no sleeps).
- TestCacheNameForIdentity and
  TestRepoAccessCacheIdentityScopedNamesPreventCrossIdentityLeakage
  cover the naming helper and cross-identity isolation at the lockdown
  package level.
- TestGetRepoAccessCacheIsolatesTrustDecisionsPerIdentity in
  pkg/github mirrors the HTTP server's exact construction pattern
  end-to-end and fails without the dependencies.go fix.

Fixes #3107

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-19 16:30:34 +02:00
Tommaso Moro 778f5bb6a3 chore(deps): bump go-github v87 → v89 and resolve breaking changes (#2840)
* chore(deps): bump go-github v87 -> v89 and resolve breaking changes

Bumps google/go-github from v87 to v89 across the module and fixes the
resulting breaking changes. No tool or behavior changes.

- Rewrite all import paths go-github/v87 -> go-github/v89.
- gists.go: Gists.Create now takes CreateGistRequest by value and
  Gists.Edit is renamed to Gists.Update taking UpdateGistRequest.
- repositories_test.go: adapt to RepositoryRelease fields that became
  value types in v89 (ID, TagName, Draft).
- Regenerate third-party license files for the new module path.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Don't clear gist description on update when omitted

update_gist always sent Description as a pointer to the OptionalParam
zero value (""), so omitting description would overwrite an existing gist
description with an empty string. Only set UpdateGistRequest.Description
when the caller actually provided the argument; an explicit empty string
still clears it. Adds a test asserting the description key is absent from
the PATCH body when omitted and present when set.

This addresses a pre-existing behavior surfaced while migrating to the
v89 gist request types.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-08 15:08:59 +01:00
kerobbi b5397f6e33 lockdown mode: remove RepoAccessCache singleton and isolate viewer state per instance
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
GoReleaser Release / release (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
MCP Server Diff / mcp-diff-http (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
2026-05-29 12:24:09 +02:00
Iulia Bejan 1861a351f8 Upgrade go-github from v82 to v87 (#2452)
Breaking changes addressed:
- raw.NewClient: Use WithHTTPClient/WithEnterpriseURLs options, pass ctx to
  NewRequest, return (*Client, error)
- internal/ghmcp/server.go: Use functional options for REST client creation,
  replace UserAgent field mutation with UserAgentTransport wrapper, add
  restUATransp field to githubClients struct
- pkg/github/dependencies.go: Use functional options for REST client creation,
  handle raw.NewClient error return
- pkg/github/actions.go: Handle new WorkflowDispatchRunDetails return value
  from CreateWorkflowDispatchEventByID/ByFileName
- pkg/github/issues.go: Replace IssueListOptions with ListOptions for
  SubIssue.ListByIssue
- pkg/github/notifications.go: MarkThreadDone now takes string instead of
  int64; remove ParseInt and strconv import
- pkg/github/projects.go: Remove pointer indirection from
  ListProjectsPaginationOptions and ListProjectsOptions fields
- pkg/github/issues_granular.go: Pass ctx to NewRequest, remove ctx from Do
- Test files: Add mustNewGHClient helper, replace all NewClient calls,
  fix stubClientFnFromHTTP signature, fix lockdown_test.go BaseURL handling,
  fix raw_test.go, remove invalid threadID test case

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-18 14:36:23 +02:00
Roberto Nacu 4bded57e02 Fix lockdown mode permission check (#2361)
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
GoReleaser Release / release (push) Has been cancelled
MCP Server Diff / mcp-diff (push) Has been cancelled
Publish to MCP Registry / publish (push) Has been cancelled
* use REST API for permission checks

* update tests

* skip API call for bots and add github-action[bot] to trusted logins

* improve tests

* add nil guard to IsSafeContent

* add comment clarifying maintain mapping

---------

Co-authored-by: Sam Morrow <info@sam-morrow.com>
2026-04-23 12:21:29 +01:00
Oleksandr Redko 505d5dc33a refactor: modernize code with modernize and intrange 2026-02-12 12:58:49 +01:00
JoannaaKL 7cfb354728 Dont filter content from Copilot (#1464)
* Dont filter content from trusted bots

* Final changes

* Use only debug level

* Add logs and comments
2025-11-26 11:24:23 +01:00
JoannaaKL 28b868dad4 Add in memory cache for lockdown mode (#1416)
* Apply lockdown mode to issues and pull requests

* Add cache

* Unlock in defer

* Add muesli/cache2go

* [WIP] Replace custom cache in lockdown.go with cache2go struct (#1425)

* Initial plan

* Replace custom cache with cache2go library

- Added github.com/muesli/cache2go dependency
- Replaced custom map-based cache with cache2go.CacheTable
- Removed manual timer management (scheduleExpiry, ensureEntry methods)
- Removed timer field from repoAccessCacheEntry struct
- Updated GetRepoAccessInfo to use cache2go's Value() and Add() methods
- Updated SetTTL to flush and re-add entries with new TTL
- Used unique cache names per instance to avoid test interference
- All existing tests pass with the new implementation

Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

* Final verification complete

Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

* Use muesli for cache

* Make RepoAccessCache a singleton (#1426)

* Initial plan

* Implement RepoAccessCache as a singleton pattern

Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

* Complete singleton implementation and verification

Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

* Remove cacheIDCounter as requested

Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com>

* Update mutexes

* .

* Reuse cache

* .

* .

* Fix logic after vibe coding

* Update docs

* .

* Refactoring to make the code pretty

* Hide lockdown logic behind shouldFilter function

* .

* Tests

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2025-11-21 10:34:51 +01:00
JoannaaKL 2e5418093f Add lockdown mode to filter issue (#1371)
* Add lockdown mode to filter issue

* Update flag name

* Update pkg/lockdown/lockdown.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Merge two graphql queries into one

* Don't use Issue.Repository

* Add function signature

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-10 17:01:21 +01:00