Skip to content

ci: gate on real write access, not author_association - #19

Merged
iamrraj merged 1 commit into
mainfrom
ci/permission-gate
Aug 3, 2026
Merged

ci: gate on real write access, not author_association#19
iamrraj merged 1 commit into
mainfrom
ci/permission-gate

Conversation

@iamrraj

@iamrraj iamrraj commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The bug

Three issues opened by the repository owner were declined:

event=issues author_association=CONTRIBUTOR on_pull_request=false β†’ allowed=false

The REST API says MEMBER. The webhook says CONTRIBUTOR. Both are correct β€” author_association reports MEMBER only when someone's organization membership is public. With private membership, an owner arrives looking like a stranger.

The fix that would have been wrong

Adding CONTRIBUTOR to the allowlist. It fixes the symptom and breaks the gate: CONTRIBUTOR is not a permission, it is a fact about the past β€” anyone with a single merged PR keeps it forever, including on a repo they were never trusted with.

The fix

Ask GitHub what the account may actually do:

GET /repos/{repo}/collaborators/{user}/permission  β†’  admin | maintain | write | read | none

Require write, maintain or admin. Verified against this repo: permission=admin role=admin.

author_association is kept only as a fallback for when that call cannot answer, so a token without the scope fails closed rather than open. The notice prints both values, so the next surprise of this kind is one log line rather than an afternoon of guessing.

Why the observable gate earned its keep immediately

The previous PR made the gate a job that prints its decision. It found this on the first run. As a one-line if:, this was invisible: three skipped runs, empty logs, and no way to tell a correct refusal from a broken expression.

Three issues from the repository owner were declined with
author_association=CONTRIBUTOR. The REST API says MEMBER; the webhook
says CONTRIBUTOR β€” because that field only reports MEMBER when an
organization membership is PUBLIC. With private membership an owner
arrives looking like a stranger.

Adding CONTRIBUTOR to the allowlist would have fixed the symptom and
broken the gate: it is not a permission, it is a fact about the past.
Anyone with one merged pull request keeps it forever.

The gate now asks GitHub what the account may actually do β€”
collaborators/:user/permission β€” and requires write, maintain or admin.
author_association is kept only for when that call cannot answer, so a
token without the scope fails closed rather than open. The notice prints
both, so the next surprise is one log line rather than an afternoon.
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

πŸ” ShipIT Forge reviewed this PR β€” πŸ’¬ commented (no blocking issues)

0 finding(s) (0 security). See the review above for inline details and suggested fixes.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ShipIT Forge review

βœ… No blocking issues found. I reviewed the changed files and ran:

  • πŸ›‘οΈ Security checks β€” SSRF, injection (SQL/command/template), broken auth/authz, hardcoded secrets, unsafe deserialization, path traversal, weak crypto.
  • πŸ”§ Code review β€” correctness, error handling, missing tests, clarity.

Nothing to flag. This is a comment, not an approval β€” ShipIT Forge never approves PRs; a human reviewer should approve and merge.

@iamrraj
iamrraj merged commit 164c3da into main Aug 3, 2026
3 checks passed
@iamrraj
iamrraj deleted the ci/permission-gate branch August 3, 2026 13:27
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.

1 participant