policy: one checker, and exceptions that actually suppress - #5
Open
thedancingdeveloper wants to merge 2 commits into
Open
policy: one checker, and exceptions that actually suppress#5thedancingdeveloper wants to merge 2 commits into
thedancingdeveloper wants to merge 2 commits into
Conversation
Two problems, one cause. The gate carried an inlined copy of the checker because a private policy repository cannot be checked out by a public caller, and the exceptions file was consumed by nothing but an expiry loop -- so `runner-exceptions.json` could record an exception but never grant one. The documented workaround was to drop the required status check on the repository entirely, which trades a narrow, expiring, reviewable exception for no gate at all. Making this repository public removes the constraint. scripts/runner_policy.py is now the only copy of the rule; the reusable gate checks this repository out alongside the caller and runs it. Exceptions are keyed by repo and workflow file and may be narrowed to named jobs, which matters immediately: FarmEggs' ci.yml holds two compliant jobs and two that cannot move, and a file-wide exception there would also hide a future regression in the compliant pair. An expired entry still fails the gate and suppresses nothing. Three entries recorded, all with a reason and an expiry: cadastre/ci.yaml -- builds fork PRs on a public repo FarmEggs/ci.yml mobile -- needs a JDK and the Android SDK FarmEggs/ci.yml web -- needs google-chrome and npm Verified against all 50 organization repositories: with these entries applied, the only failures are the jobs that TheDancingDeveloper-org/FarmEggs#2 and TheDancingDeveloper-org/cadastre#11 move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cadastre enforces a stricter rule than this one, in its own test suite, and that rule is the better-reasoned of the two. tests/test_release_workflow.py pins SELF_HOSTED_WORKFLOWS to the two tag-gated release workflows and asserts every other job is not self-hosted; test_self_hosted_workflows_are_all_tag_gated then asserts everything on that allowlist is reachable only from refs/tags/v*. So the line there is drawn at tag-gated, not at not-fork-reachable. TheDancingDeveloper-org/cadastre#11 tried to move publish.yml on the grounds that push-to-main needs write access and therefore has no fork exposure. That is true and beside the point: the invariant deliberately keeps the self-hosted surface as small as possible. Its own test matrix caught the change, and the PR is closed. Recording it as an exception is the honest outcome -- cadastre's hosted usage is a deliberate, tested security posture, not drift, and the expiry keeps it under review. Verified: all 51 organization repositories now pass, with FarmEggs#2 applied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The problem
runner-exceptions.jsonwas consumed by nothing but an expiry loop.scripts/audit-workflows.shvalidated that entries had not rotted, but never matched one against a violation — so the file could record an exception and never grant one. The README's own workaround says as much:That trades a narrow, expiring, reviewable exception for dropping the gate on that repository entirely. Separately, the gate carried an inlined duplicate of the checker, with a comment asking the next person to keep two copies in sync by hand.
Both had the same cause: a private policy repository cannot be checked out by a public caller.
The change
Making this repository public removes that constraint (owner-approved; disclosure audit below).
scripts/runner_policy.pyis now the only copy of the rule. The reusable gate checks this repository out beside the caller and runs it.scripts/audit-workflows.shis a thin wrapper kept for the existing call site.repo+workflow, optionally narrowed to namedjobs.ci.ymlholds two compliant jobs and two that cannot move; a file-wide exception there would also hide a future regression in the compliant pair.Exceptions recorded
cadastreci.yamlcadastrepublish.ymlFarmEggsci.ymlmobileFarmEggsci.ymlwebgoogle-chrome+npmOn cadastre: it enforces a stricter rule than this one, in its own test suite, and that rule is better reasoned.
tests/test_release_workflow.pypinsSELF_HOSTED_WORKFLOWSto the two tag-gated release workflows and asserts everything else is not self-hosted; a second test asserts every allowlisted workflow is reachable only fromrefs/tags/v*. cadastre#11 tried to movepublish.ymlbecause push-to-mainneeds write access and so has no fork exposure — true, and beside the point: the invariant deliberately keeps the self-hosted surface minimal. Its test matrix caught the change and the PR is closed. Recording it as an exception is the honest outcome; cadastre's hosted usage is a deliberate, tested security posture, not drift.Verification
13 checker tests in
tests/, wired intoaudit.yml. They cover the block-sequenceruns-onform, the# runs-on: ubuntu-latestwarning comments several repos carry, expiry boundaries, and the sibling-job case.Run against all 51 organization repositories at their current default branches, with FarmEggs#2 applied: 0 failures, 6 lines excused.
End-to-end: the gate now passes in a public repository — rustnzb#92,
runner-policy / runner-policygreen onnode-b-gha-public-rustin 11s. It last failed 2026-08-16 for exactly the visibility reason this PR removes.Disclosure audit before going public
Scanned every blob in all 5 commits: no credentials, tokens, keys or internal hostnames/IPs. 9 unique paths ever, no deleted files.
CODEOWNERSreferences the@TheDancingDeveloper-org/ownersteam, not individual usernames — so the "reviewer usernames" concern indocs/CI-RUNNER-GATES.mddoes not actually apply. What is disclosed is the runner label names, which is the intended cost.🤖 Generated with Claude Code