Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes lockdown-mode permission checks by switching from a GraphQL collaborators-based approach (which can silently return empty results under limited tokens) to the REST Repositories.GetPermissionLevel endpoint, and expands the trusted bot list to avoid unnecessary lookups for known-safe actors.
Changes:
- Replace GraphQL collaborators permission checks with REST
GetPermissionLevel, keeping GraphQL only for repo metadata (viewer.login,isPrivate). - Thread a REST client into the lockdown
RepoAccessCache(singleton + new constructor for test isolation) and optimize cache-hit flows to skip GraphQL. - Update lockdown-related tests and add
github-actions[bot]to the trusted bot list.
Show a summary per file
| File | Description |
|---|---|
| pkg/lockdown/lockdown.go | Switches permission lookup to REST and adjusts cache construction / trusted-bot behavior. |
| pkg/lockdown/lockdown_test.go | Updates cache tests to mock REST permission responses. |
| pkg/github/server_test.go | Refactors test helpers to build a RepoAccessCache with an optional REST client + REST permission mock. |
| pkg/github/pullrequests_test.go | Updates PR lockdown tests to use REST permission mocking. |
| pkg/github/issues_test.go | Updates issue lockdown tests to use REST permission mocking and revised GraphQL metadata mock. |
| pkg/github/dependencies.go | Wires REST client into lockdown.GetInstance during request dependency construction. |
| internal/ghmcp/server.go | Wires REST client into lockdown.GetInstance for stdio server initialization. |
Copilot's findings
- Files reviewed: 7/7 changed files
- Comments generated: 4
| } | ||
|
|
||
| permission := permLevel.GetPermission() | ||
| return permission == "admin" || permission == "write", nil |
There was a problem hiding this comment.
The REST GetPermissionLevel endpoint returns "maintain" for users with the Maintain role (orgs with granular permissions), and those users have push access. The old GraphQL code checked for MAINTAIN — this line drops it, which would incorrectly deny access to maintainers.
| return permission == "admin" || permission == "write", nil | |
| return permission == "admin" || permission == "maintain" || permission == "write", nil |
Otherwise this PR looks good to me — clean change, correct approach, nice optimizations. Approved with this one fix.
There was a problem hiding this comment.
This came up in one of Copilot's comments as well, I should've updated the PR description instead of replying in a now resolved thread. The go-github and the REST API docs say Permission only returns admin/write/read/none and that maintain is mapped to write, so maintainers are already covered by the check. I've added a comment to make this explicit now.
SamMorrowDrums
left a comment
There was a problem hiding this comment.
Excellent, thanks @kerobbi
Summary
Replaces the GraphQL
repository.collaboratorspermission check in lockdown mode with the REST GetPermissionLevel endpoint, and addgithub-actions[bot]to the trusted bots list.Lockdown filtering behaviour is preserved; same trust model (push access = trusted), same fail-closed error handling.
Why
Lockdown mode filters issue and PR content on public repos based on whether the author has push access to the repository. It does this using the GraphQL
repository.collaboratorsfield, but this field requires the calling token to have admin/write access on the repo to enumerate collaborators.GITHUB_TOKENand GitHub App installation tokens typically don't have this, so the query silently returns empty results (no error), and lockdown ends up treating every author as untrusted, blocking all content.The REST
GetPermissionLevelendpoint works with any authenticated token and returns the exact permission level.What changed
GetPermissionLevelfor permission checks (kept GraphQL for repo metadata only:viewer.login,isPrivate)restClientas a positional parameter onGetInstanceand wired it throughNewRepoAccessCacheconstructor for test isolationgithub-actions[bot]to trustedBotLoginsisTrustedBotcheck before API calls inIsSafeContentto skip unnecessary lookups for trusted botsMCP impact
Security / limits
GetPermissionLevelrequires onlymetadata: read(included by default on all token types as far as I know) and works with any authenticated token on public repos.Tool renaming
deprecated_tool_aliases.goLint & tests
./script/lint./script/testDocs