From 649ce11d6a9592f0536335d3c76004fda1f22df5 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 2 Nov 2026 13:48:53 -0500 Subject: [PATCH] Advance the cosign-installer pin, and gate the rule sigstore/cosign-installer sat on the commit of v3.9.2 while v4.1.2 is current -- a major behind, in the workflow that signs releases. A stale pin is worse than a float, because it keeps a known-older release alive in the one job whose output people verify. This was found 2026-11-02 by the estate sweep and deliberately left alone at the time: the pin is in release.yml, and probaci#39 was open against that exact file. One writer per set of files. #39 is merged, so it lands now. The new step checks the shape of every reference offline, inside the existing test job, and fails below a floor of 5 workflow files. Proven red before this change and green after. --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d685dd6..b5ecf96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,40 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + + # Every action reference must match the org pinning rule, offline and exactly: a third-party + # action on the commit SHA of a named release, a first-party one (actions/, docker/, github/) + # floating on its current major. Whether that release is the newest is Dependabot's job and + # laranail/package-tools' verify-action-pins.py; nothing checked the *shape* until this step, + # which is how the references here drifted off it with every check staying green. + - name: Action pins match the rule + run: | + python3 - <<'PINS' + import os, re, sys + d = ".github/workflows" + files = sorted(f for f in os.listdir(d) if f.endswith((".yml", ".yaml"))) + floor = 5 + if len(files) < floor: + sys.exit(f"{len(files)} workflow files in {d}; floor is {floor}. " + "A search that stops matching must fail, not report a clean repository.") + uses = re.compile(r"uses:\s*([\w.-]+/[\w./-]+?)@([\w./-]+?)([ \t]*#[ \t]*(\S+))?\s*$", re.M) + bad, seen = [], 0 + for name in files: + for ref, at, _, note in uses.findall(open(os.path.join(d, name)).read()): + if ref.startswith("./") or "/.github/workflows/" in ref: + continue # a local action or a reusable-workflow call + seen += 1 + if ref.startswith(("actions/", "docker/", "github/")): + if not re.fullmatch(r"v\d+", at) or note: + bad.append(f"{name}: {ref}@{at} -- first-party, so float on a bare major tag") + elif not re.fullmatch(r"[0-9a-f]{40}", at): + bad.append(f"{name}: {ref}@{at} -- third-party, so pin to a commit SHA") + elif not note or re.fullmatch(r"v?\d+", note): + bad.append(f"{name}: {ref}@{at[:12]} # {note} -- name the release, not a mutable major") + print("\n".join(f" {b}" for b in bad)) + print(f"{seen} action references in {len(files)} workflow files; {len(bad)} off the rule") + sys.exit(1 if bad else 0) + PINS - uses: actions/setup-go@v7 with: go-version: "1.25" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 394d1fc..80f2c78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: - uses: actions/setup-go@v7 with: go-version: "1.25" - - uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2 + - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 # syft powers GoReleaser's SBOM generation. - uses: anchore/sbom-action/download-syft@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3 # QEMU + buildx build the multi-arch dockers_v2 images; login pushes to ghcr.