Skip to content

ci: don't flag private org members as external contributors#238

Open
teeohhem wants to merge 1 commit into
mainfrom
fix/external-contrib-private-members
Open

ci: don't flag private org members as external contributors#238
teeohhem wants to merge 1 commit into
mainfrom
fix/external-contrib-private-members

Conversation

@teeohhem

Copy link
Copy Markdown
Contributor

Teammates with private (concealed) organization membership are no longer misflagged as external contributors. They previously got a Slack alert and an external label on every issue/PR they opened, because author_association only reports MEMBER for publicly visible members — a private member shows up as CONTRIBUTOR/NONE, which the workflow treated as external.

The internal check now keeps author_association as a free fast path, then falls back to resolving the author's actual repository permission via repos.getCollaboratorPermissionLevel. That resolves access granted through private teams, which author_association cannot see.

Two design points worth noting:

  • Threshold on granted access, not permission !== 'none'. This is a public repo, so GitHub grants every user implicit read. A naive != 'none' check would mark every genuine external contributor as internal. The check requires triage/push/maintain/admin instead.
  • No new secret or permission. The built-in GITHUB_TOKEN can call the endpoint under the existing issues:write / pull-requests:write scope.

Both behaviors were verified in a throwaway Actions run before writing this fix: a private member with write access resolved to INTERNAL with no 403, and a non-collaborator (octocat, implicit read) correctly resolved to EXTERNAL.


Compound Engineering
Claude Code

author_association only reports MEMBER for *public* org members, so a
teammate with private (concealed) membership was classified as external,
alerted to Slack, and labeled. Resolve actual repo access via the
collaborator-permission endpoint as a fallback. Threshold on granted
access (triage and up) rather than permission != 'none', since public
repos grant everyone implicit read. Uses the built-in GITHUB_TOKEN; no
new secret or permission required.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@teeohhem teeohhem requested a review from a team as a code owner June 29, 2026 20:50
@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 783709d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added the external Opened by an external contributor label Jun 29, 2026
@teeohhem teeohhem changed the title fix: don't flag private org members as external contributors ci: don't flag private org members as external contributors Jun 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

The change correctly fixes the stated bug: private org members (who report as CONTRIBUTOR/NONE via author_association) are now resolved through repos.getCollaboratorPermissionLevel and classified internal when they hold triage/push/maintain/admin. The triage+ threshold correctly avoids the permission !== 'none' trap that would have marked every fork author internal on a public repo, and a non-collaborator (404) falls through to external as intended. No injection or auth-bypass path is introduced — fork metadata flows only into a JSON.stringify-encoded Slack body and a parameterized API call, with no checkout or execution of fork code.

🟡 P2 — recommended

  • .github/workflows/external-contributor-alerts.yml:47 — A non-404 error from getCollaboratorPermissionLevel (e.g. 403 secondary rate-limit or 5xx) re-throws and fails the step before the alert is sent, so the alert is dropped with no automatic retry on opened events, contradicting the file's fail-toward-alerting posture elsewhere.
    • Fix: On non-404 errors, core.warning and leave internal = false so the alert still fires, rather than throw e.
    • reliability, correctness
🔵 P3 nitpicks (1)
  • .github/workflows/external-contributor-alerts.yml:42item.user.login is dereferenced without optional chaining while the adjacent lines use item.user?.login; harmless since the bot guard already short-circuits a null user, but inconsistent within the same block.
    • Fix: Use item.user?.login at line 42 to match the surrounding usage.

Reviewers (5): correctness, security, reliability, maintainability, project-standards.

Testing gaps: No coverage exercising the new slow path — a private member resolving to internal via triage+, a 404 non-collaborator mapping to external, or a public-repo implicit-read author (all permission booleans false) classified external. The repo has no test harness for workflow JS, so this is advisory rather than a blocker.

@teeohhem teeohhem added automerge and removed external Opened by an external contributor labels Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants