fix(policy): require a second reviewer for workflow changes - #4
Closed
benjiwagner wants to merge 1 commit into
Closed
fix(policy): require a second reviewer for workflow changes#4benjiwagner wants to merge 1 commit into
benjiwagner wants to merge 1 commit into
Conversation
The `ci` check every repository relies on is defined by a workflow file that GitHub evaluates from the pull-request head, so a pull request can rewrite what its own required check executes and an approval-bypass holder can then merge it unreviewed. Fail `repository-policy` when a pull request touches .github/workflows/** without an approving review from a code owner other than the author, reading CODEOWNERS and reviews from the base ref only. Also fix the self-test harness, which could not fail: a bare [[ ... ]] assertion inside a function does not abort under set -e here, so every assertion was unenforced and the suite passed even with a validator broken outright. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
Closing: the workflow-path guard this PR added does not contain the threat and cannot be made to by widening paths. An adversarial review confirmed 16 bypasses — the |
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.
Summary
repository-policywhen a pull request modifies.github/workflows/**without an approving review from a code owner other than the author.--self-testharness, which could not fail.pull_request_reviewtrigger so a late approval re-evaluates the check instead of leaving a stale failure.Context
Linear CUS-109. The organization
integrityruleset has zero bypass actors and requiresci. Butciis defined by each repository's own workflow file, triggered onpull_request, so GitHub runs the definition taken from the pull-request head. A pull request that replaces itscijob body withrun: trueproduces a genuine passing check from the pinned GitHub Actions integration, and its author can then merge it using theRepositoryRole/adminapproval bypass. From that merge onward the required check verifies nothing.Pinning
integration_idbinds who reports a check, not what the check ran, so it does not close this. CODEOWNERS does not close it either: code-owner review is enforced by theapprovalruleset, which is exactly the rule the bypass waives.repository-policyis the only required check not exposed to head-side redefinition — its caller runs onpull_request_target, so GitHub reads the caller and itsuses: ...@<sha>pin from the base branch, and the job checks out this repository at the immutablejob.workflow_sha. A pull request cannot substitute its own policy revision for its own evaluation.The self-test could not fail. A bare
[[ ... ]]assertion inside a function does not abort underset -ein this script, so every assertion in the suite was unenforced. Verified against the unmodified2af0536revision: breakingvalidate_branchso it rejects every valid branch still producedRepository policy self-test passedand exit 0. Thecijob here runs onlybash -nplus that self-test, so this repository's own required check has been vacuous since the suite was written. Assertions now record failures throughexpect_error_count/expect_equalandself_testreturns nonzero, with the--self-testentry point callingself_test || exit 1explicitly.Validation
bash -n repository-policy/validate.shpasses;bash repository-policy/validate.sh --self-testpasses on bash 3.2 (macOS) and 5.x, hencetrrather than${var,,}.validate_branchbroken outright (the pre-existing blind spot).docs/.github/workflows/x.ymlnot treated as a workflow path.Impact and rollback
Job permissions are unchanged (
contents: read,pull-requests: read,statuses: write) and no pull-request code is checked out; the guard reads metadata through the API only.This has no effect until each consumer repository moves its
uses: ...@<sha>pin, which is deliberate follow-on work since the pin can only reference this commit once merged.Note on scope:
customermates/.githubhas a single code owner, so the guard is vacuous in this repository and does not gate this pull request. It becomes active where a second code owner exists — today that iscustomermates/knowledge-base— so the live proof belongs on the pin-bump pull request there, not here.Roll back by reverting the squash commit. Consumers keep running whichever policy commit they pin, so a revert only takes effect when their pins move.
Screenshots
Not applicable because this pull request changes a shell validator and a workflow trigger only.