fix(pr-review): API-fallback collaborator check + authorized_users input (#253)#254
Conversation
…put (#253) The "Check author association" step blocked PRs when the PR author's org membership was private. GitHub does not disclose private org membership in pull_request* event payloads — org admins appear as CONTRIBUTOR rather than MEMBER, causing the association guard to reject their own PRs. Reproducer: glitchwerks/mom-bot#20, run 25602562131, log line AUTHOR_ASSOCIATION: CONTRIBUTOR for cbeaulieu-gt (repo admin). Three-step authorization logic (implemented in pr-review/action.yml): 1. authorized_users CSV input — if the PR author is in the explicit list, authorize immediately (highest precedence, no API call needed). 2. author_association field — OWNER / MEMBER / COLLABORATOR still fast-path authorize (remains correct for public org membership and direct collabs). 3. API-fallback collaborator check — gh api repos/$REPO/collaborators/$ACTOR exits 0 (HTTP 204) when the user is a repo collaborator regardless of org membership visibility. Any non-zero exit (404, 403, rate-limit) is treated as "not a collaborator" to avoid silent authorization on API errors. Also adds authorized_users as a workflow_call input in claude-pr-review.yml, threaded through to the composite action for consumer use. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… review)
Code review surfaced a footgun: human-written CSV lists like
"alice, bob, charlie" would silently fail to match "alice" because the
tokens carried leading/trailing spaces. Strip both the actor and the
allowlist of all whitespace before comma-boundary matching.
The previous comma-wrapped boundary pattern (",alice,bob,") already
prevents substring collisions ("bob" vs "bobby"); the only missing piece
was whitespace tolerance. Adding tr -d '[:space:]' to both sides covers
spaces, tabs, and stray newlines without touching the boundary logic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Claude finished @cbeaulieu-gt's task in 4m 21s —— View job PR Review CompleteReviewed PR #254: fix(pr-review): API-fallback collaborator check + authorized_users input SummaryThis PR successfully addresses the private org membership authorization bug (#253) with a well-structured three-step authorization flow. The implementation is secure, handles edge cases correctly, and maintains backwards compatibility. The code is thoroughly documented with clear inline comments explaining the rationale for each step. Key strengths:
Code security analysis:
Backwards compatibility verified:
Findings🟢 MediumAPI rate limiting implications not documented in user-facing guidance The API-fallback collaborator check ( While this is acknowledged as an accepted trade-off in the PR description, it is not documented in the action's user-facing description or usage guidance. Consider adding a note to the
Location: NitStep name no longer reflects full scope The step name "Check author association" (
Consider renaming to "Check authorization" or "Authorize PR author" for accuracy. Location: NitComment wording imprecise about whitespace handling The comment on lines 187-190 states "an unstripped " bob " would silently fail to match "bob"" but the implementation uses While the behavior is correct and safe (GitHub usernames cannot contain spaces), the comment could be more precise: # Whitespace is stripped from both the CSV list and the actor name before
# comparison, so "alice, bob, charlie" becomes "alice,bob,charlie" — this
# tolerates copy-paste from human-written lists and ensures " bob " matches "bob".Location: Edge Cases VerifiedThe CSV parsing correctly handles:
The API fallback correctly handles:
Test Plan StatusFrom PR description:
The incomplete test items are standard for pre-merge state and should be completed post-release per the repository's validation workflow. VerdictVerdict: APPROVE |
|
v2.6.0 release withdrawn. This PR was shipped as part of v2.6.0, but the release was rolled back: The API-fallback collaborator check added here cannot enumerate collaborators with The CSV Code on 🤖 Generated by Claude Code on behalf of @cbeaulieu-gt |
Closes #253
Summary
Fixes a pre-existing bug in
pr-review/action.ymlwhere self-PRs by maintainers with private org membership were blocked as "external" because GitHub does not disclose private membership inpull_request*event payloads —author_associationreturnsCONTRIBUTOReven for actual MEMBER/admin users. Concrete reproducer:glitchwerks/mom-bot#20(run25602562131, log lineAUTHOR_ASSOCIATION: CONTRIBUTORdespitecbeaulieu-gtbeing repo admin).Three-step authorization
The auth gate now evaluates in this order, authorizing on the first hit:
authorized_usersCSV match — explicit allowlist input (highest precedence). Whitespace-tolerant, case-insensitive, comma-boundary matching prevents substring collisions (bobvsbobby).author_associationfast-path — OWNER / MEMBER / COLLABORATOR. Unchanged behavior for public-membership cases.gh api repos/$REPO/collaborators/$ACTOR. HTTP 204 → authorize; non-zero exit (404, 403, 429, network error) → block. Any non-zero exit is intentionally treated as "not a collaborator" — no silent authorization on transient errors.Changes
pr-review/action.yml— addsauthorized_userscomposite-action input; rewrites the "Check author association" step with the three-step logic above; documents the failure mode..github/workflows/claude-pr-review.yml— addsauthorized_userstoworkflow_call.inputs; threads through to the composite step.Backwards compatibility
External consumers who do not set
authorized_usersretain identical behavior for any case where step 2 (author_association) would have authorized. The new step 3 (API fallback) only fires when step 2 would have blocked — strictly additive.Test plan
actionlintpasses (verified locally on both commits).glitchwerks/claude-configsskills/github-actions/SKILL.md§ 13 applies — this PR's own dogfood will execute the OLD@v2code, not the branch code, becausepr-review/action.ymlis itself a self-referenced composite action).glitchwerks/mom-bot#20re-run authorizescbeaulieu-gtvia step 3 (API fallback) and the review actually runs.authorized_usersallowlist verified by setting it to a known username on a temporary PR; verifies CSV parsing tolerates"a, b, c"whitespace.Notes
pr-review/action.ymlto delegate to the existingcheck-auth/action. That was option (3) in fix(pr-review): author_association check is blind to private org membership — self-PRs get blocked as external #253; the user picked options (1)+(2). A future cleanup issue can fold the auth logic intocheck-auth/if duplication becomes a maintenance burden.🤖 Generated by Claude Code on behalf of @cbeaulieu-gt