From bdd31c89db291d2037438e3aa137f30db9c54df9 Mon Sep 17 00:00:00 2001 From: The01Geek Date: Sat, 29 Aug 2026 16:12:57 -0600 Subject: [PATCH] ci: a fork can no longer take the judge-change exemption The exemption that lets a maintainer change the release verifier keyed on the branch NAME. A fork chooses its own branch names, so a pull request from a fork on a branch called policy-update/... skipped both the judge comparison and the artifact verification, and reported the required check green on a tree that had never been verified. With no approving review required, nothing automated stood between such a pull request and a merge. Every exemption is now gated on the pull request originating in this repository (head.repo.full_name == github.repository), which the head cannot forge. A fork always takes the strict path. Also refragments the private repository's name in the verifier's leak pattern. It was published verbatim in the one control built to keep it out of the release, and did not self-trip only because the character preceding it is the b of \b. Co-Authored-By: Claude Opus 5 --- .github/verify-release.py | 6 +++++- .github/workflows/distribution-verify.yml | 21 +++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/verify-release.py b/.github/verify-release.py index c1c5fda85..2205cf722 100755 --- a/.github/verify-release.py +++ b/.github/verify-release.py @@ -106,7 +106,11 @@ "private key block"), (re.compile(r"\bgh[pousr]_[A-Za-z0-9]{16,}"), "GitHub token"), (re.compile(r"\bsk-ant-[A-Za-z0-9-]{16,}"), "Anthropic API key"), - (re.compile(r"\bRadman-LLC/prflow-dev\b"), "private repository reference"), + # Assembled from fragments for the same reason the allowlist keys are: spelling + # the private repository as one literal publishes the very name this pattern + # exists to keep out of the release. + (re.compile(r"\b" + "Radman" + "-LLC/prflow" + "-dev" + r"\b"), + "private repository reference"), ) # Reviewed, benign matches. Each entry is (path, matched text, reason) and diff --git a/.github/workflows/distribution-verify.yml b/.github/workflows/distribution-verify.yml index cb46b2c7a..f50e65811 100644 --- a/.github/workflows/distribution-verify.yml +++ b/.github/workflows/distribution-verify.yml @@ -43,6 +43,10 @@ jobs: env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_REF: ${{ github.event.pull_request.head.ref }} + # A fork chooses its own branch name, so a branch-name test is NOT an + # identity test. Every exemption below is gated on the pull request + # coming from this repository; a fork always takes the strict path. + IS_SAME_REPO: ${{ github.event.pull_request.head.repo.full_name == github.repository }} run: | set -euo pipefail judge=".github/verify-release.py .github/workflows/distribution-verify.yml" @@ -56,8 +60,8 @@ jobs: if git cat-file -e "${BASE_SHA}:${f}" 2>/dev/null; then present=$((present+1)); else absent=$((absent+1)); fi done if [ "$present" -eq 0 ]; then - case "$HEAD_REF" in - policy-update/*) + case "${IS_SAME_REPO}:${HEAD_REF}" in + true:policy-update/*) echo "::notice::bootstrap: the judge is absent from the protected base and this is a policy-update branch introducing it. Comparison skipped for this pull request only." exit 0 ;; *) @@ -76,9 +80,9 @@ jobs: # it fails, including the ceremony documented for changing it. The control # here is that such a branch is judged by the PREVIOUS base verifier and # reviewed by a human - not that the diff is forbidden. - case "$HEAD_REF" in - policy-update/*) - echo "::notice::policy-update branch: a judge change is this branch's purpose. It is evaluated by the base verifier and requires human review." + case "${IS_SAME_REPO}:${HEAD_REF}" in + true:policy-update/*) + echo "::notice::policy-update branch in this repository: a judge change is this branch's purpose. It is evaluated by the base verifier and requires human review." exit 0 ;; esac @@ -95,6 +99,7 @@ jobs: env: HEAD_REF: ${{ github.event.pull_request.head.ref || github.ref_name }} GITHUB_EVENT_NAME: ${{ github.event_name }} + IS_SAME_REPO: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} run: | set -euo pipefail # A tree carrying no .release/ metadata is not a release candidate. That @@ -117,9 +122,9 @@ jobs: # belongs to what is actually being published - a release branch, or main # itself - and not to an ordinary pull request, which would otherwise be # unmergeable for changing so much as a typo. - case "${GITHUB_EVENT_NAME}:${HEAD_REF}" in - pull_request:release/*|push:*) : ;; - pull_request:*) + case "${GITHUB_EVENT_NAME}:${IS_SAME_REPO}:${HEAD_REF}" in + pull_request:*:release/*|push:*:*) : ;; + pull_request:true:*) echo "::notice::not a release branch: the digest manifest describes the published release, so an ordinary pull request is not verified against it." exit 0 ;; esac