Skip to content

Fix GitHub App cross-org member enumeration using per-installation tokens#4774

Open
dustin-decker wants to merge 2 commits into
mainfrom
fix/github-app-cross-org-member-enum
Open

Fix GitHub App cross-org member enumeration using per-installation tokens#4774
dustin-decker wants to merge 2 commits into
mainfrom
fix/github-app-cross-org-member-enum

Conversation

@dustin-decker

@dustin-decker dustin-decker commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • When a GitHub App is installed across multiple orgs, addMembersByApp discovers all installations via JWT but uses a single installation's token for ListMembers calls across all orgs
  • GitHub App installation tokens only bypass IP allowlists for their own org, so cross-org calls get 403s
  • Adds APIClientForInstallation to appConnector which creates per-installation API clients using ghinstallation.NewFromAppsTransport
  • Extracts addMembersByOrgWithClient from addMembersByOrg so the app auth path can provide an explicit per-installation client

Test plan

  • New unit tests for APIClientForInstallation (3 subtests verifying distinct clients, correct installation ID, different tokens)
  • New unit test for addMembersByOrgWithClient
  • Existing TestAddMembersByApp and TestEnumerateWithApp updated and passing
  • go build passes
  • Manual verification with a multi-org GitHub App installation

Note

Medium Risk
Changes GitHub App authentication and org member enumeration; mis-scoped tokens could still miss members, but behavior is narrowed with tests and per-org error isolation.

Overview
Fixes GitHub App member enumeration when the app is installed on multiple orgs by using an installation-scoped API client for each org instead of one token for every ListMembers call (which caused 403s when IP allowlists apply per installation).

appConnector now keeps the shared AppsTransport and adds APIClientForInstallation, built with ghinstallation.NewFromAppsTransport; the default API client from NewAppConnector still targets the configured installation ID. addMembersByApp walks app installations, builds a client per installation ID, and calls addMembersByOrgWithClient; failures for a single org are logged and skipped rather than aborting the whole run. addMembersByOrg delegates to the same helper with the connector’s default client.

New unit tests cover per-installation clients and the refactored member listing; existing app enumeration tests were updated for installation IDs in mocks.

Reviewed by Cursor Bugbot for commit a3fe806. Bugbot is set up for automated code reviews on this repo. Configure here.

@dustin-decker dustin-decker force-pushed the fix/github-app-cross-org-member-enum branch from 07427a9 to 977a541 Compare February 28, 2026 00:31
@dustin-decker dustin-decker marked this pull request as ready for review February 28, 2026 00:46
@dustin-decker dustin-decker requested a review from a team February 28, 2026 00:46
@dustin-decker dustin-decker requested a review from a team as a code owner February 28, 2026 00:46

@rosecodym rosecodym left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this review will be a bit enmeshed with my (upcoming) review of #4775.

Comment thread pkg/sources/github/connector_app.go
@dustin-decker dustin-decker requested a review from a team June 11, 2026 00:29

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5ad29e6. Configure here.

Comment thread pkg/sources/github/github.go Outdated
…kens

When a GitHub App is installed across multiple orgs, addMembersByApp
discovers all installations via JWT but uses a single installation's
token to call ListMembers for every org. GitHub App installation tokens
only bypass IP allowlists for their own org, causing 403 errors for
cross-org calls.

Changes:
- Add appsTransport and apiEndpoint fields to appConnector
- Add APIClientForInstallation method that creates a per-installation
  API client using ghinstallation.NewFromAppsTransport
- Refactor addMembersByApp to create per-installation clients for each
  discovered org instead of reusing s.connector.APIClient()
- Extract addMembersByOrgWithClient so callers can provide an explicit
  client (addMembersByOrg delegates to it for backward compat)
- Update existing tests to pass appConnector and include installation
  IDs in mock responses
- Add new tests for APIClientForInstallation and addMembersByOrgWithClient
Reuse APIClientForInstallation when constructing the configured GitHub App client so the default path shares the same per-installation client creation logic as cross-org enumeration.
@dustin-decker dustin-decker force-pushed the fix/github-app-cross-org-member-enum branch from a6449cc to a3fe806 Compare June 11, 2026 00:56
@dustin-decker

Copy link
Copy Markdown
Contributor Author

Rebased on latest main, CI is green (15 passing, 0 failing), and all active review threads are resolved. Ready for review/merge once the required approval is refreshed.

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.

3 participants