Repository navigation
fix: make vouched_ci approvals work from the web dyno and verify ids match logins - #149
Merged
Merged
Conversation
…ry run `IS_DRY_RUN` is derived from the `--do-it-for-real-this-time` argv flag, which only the permissions cron is started with. The webhook server runs as `node lib/index.js`, so `IS_DRY_RUN` is always true there and the `workflow_run.requested` handler added in #147 could never get past its "would approve run" log line, and the vouched_ci installation token was minted with `actions: read` and could not have approved anything anyway. Drop the guard and always request `actions: write` for that token; no other webhook handler gates on `IS_DRY_RUN`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wd5WZCENbhvKnJi2EMqrbi
…sions run
The webhook handler only vouches for a user whose numeric id and login both
match a `vouched_ci` entry, so an entry whose id no longer resolves to its
login (typo, rename, deleted account) silently never approves anything, or
worse was meant for a different account. Resolve each id through
`GET /user/{account_id}` while processing the organization in the
permissions run and fail the run, dry run included, when the login does not
match case-insensitively or the id does not exist, so the .permissions CI
check catches it before merge.
The comparison lives in `verifyVouchedUsers` with an injected lookup so it
is unit tested; the GitHub call stays in run.ts next to the other
GitHub-backed checks rather than in the offline `validateConfigFast`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wd5WZCENbhvKnJi2EMqrbi
MarshallOfSound
approved these changes
Sep 10, 2026
MarshallOfSound
enabled auto-merge (squash)
September 10, 2026 22:01
dsanders11
approved these changes
Sep 10, 2026
MarshallOfSound
approved these changes
Oct 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Samuel Attard · Slack thread
Before: As deployed after #147, the
vouched_ciwebhook handler never approves anything.IS_DRY_RUNis!process.argv.includes('--do-it-for-real-this-time'), and the web dyno is started asnode lib/index.js(that flag only exists for the permissions cron), so the handler always stops at the "would approve run" log line, and the token minted bygetVouchedCIOctokitonly asked foractions: readand could not have approved a run anyway. Separately, avouched_cientry'sidwas never checked against itslogin: a mistyped id, a rename or a deleted account would silently never match atriggering_actor(or, if the id belonged to someone else, would be pointing at the wrong account) with nothing in CI to catch it.After: The handler approves runs that pass the per-commit verification, and the
vouched_citoken always requestsactions: write. The permissions run (dry run included, i.e. the.permissions"Sheriff Dry Run" check) resolves everyvouched_ciid throughGET /user/{account_id}and fails with an error naming the entry when the id does not exist or its login does not match the configured login case-insensitively.How: Two commits. The first removes the
IS_DRY_RUNgate from theworkflow_run.requestedhandler insrc/index.ts(no other webhook handler gates on it) and hardcodesactions: 'write'ingetVouchedCIOctokitwith a comment explaining why the web server is not a dry-run context for this handler. The second addsverifyVouchedUsers(vouched, fetchUserById)tosrc/vouched-ci.ts, a small helper with an injected lookup so it is unit tested (match, case-difference match, login mismatch, 404, multiple bad entries, non-404 errors propagate), and calls it frommain()insrc/permissions/run.tsright after the org's octokit is created, next to the other GitHub-backed checks rather than in the offlinevalidateConfigFast. A non-empty result throws, whichmain().catchturns intoprocess.exit(1). README documents the check.Notes:
getAuthNarrowingstill narrows the web dyno's general token to read-only becauseIS_DRY_RUNis always true there. The pre-existingrepos.addCollaborator/repos.removeCollaboratorcalls insrc/index.tsrun through that read-narrowed token, which is a separate, pre-existing issue that this PR deliberately does not touch (fixing it by changinggetAuthNarrowingor thestartscript would widen the whole web dyno's token).GET /user/{account_id}needs no extra App permission.🤖 Generated with Claude Code
https://claude.ai/code/session_01Wd5WZCENbhvKnJi2EMqrbi
Generated by Claude Code