From f524c0c15708a3112339a99db0b107b669f8e720 Mon Sep 17 00:00:00 2001 From: Will Wang Date: Fri, 14 Aug 2026 15:33:56 -0700 Subject: [PATCH 1/3] ci: run actions-pinned-check via reusable workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inline gate declared `runs-on: opus-runner-standard`, a self-hosted runner that never became available — the job on PR #9 starved in the queue and was killed after 24h ("exceeded the maximum execution time while awaiting a runner"), failing the required `check`. Replace the hand-rolled jobs section with a one-line call to the shared reusable gate at opus-pro/iac. This also fixes correctness: the inline grep had no exemption for first-party `opus-pro/*@main` refs, so it would have wrongly rejected this very change; the reusable gate parses YAML and exempts opus-pro/*, local ./ and docker@sha256. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/actions-pinned-check.yml | 36 ++++++---------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/.github/workflows/actions-pinned-check.yml b/.github/workflows/actions-pinned-check.yml index 70c3aa5..b8e52d7 100644 --- a/.github/workflows/actions-pinned-check.yml +++ b/.github/workflows/actions-pinned-check.yml @@ -1,9 +1,12 @@ --- -# SEC-254: every `uses:` must reference an immutable 40-char commit SHA. -# Mutable tags/branches do not pin — a compromised maintainer token can repoint -# them at a malicious commit (CVE-2025-30066 hit 23k+ repos via tj-actions). -# Deliberately implemented with shell only: the gate itself must not add a -# third-party action dependency. +# SEC-254: every third-party `uses:` must reference an immutable 40-char commit +# SHA. Mutable tags/branches do not pin — a compromised maintainer token can +# repoint them at a malicious commit (CVE-2025-30066 hit 23k+ repos via +# tj-actions). +# +# Delegates to the shared reusable gate in opus-pro/iac so the detection logic +# (a YAML parser that exempts first-party opus-pro/* @main refs, local ./ refs +# and docker@sha256 digests) lives in one place instead of a hand-rolled grep. name: actions-pinned-check on: pull_request: @@ -17,25 +20,4 @@ permissions: jobs: check: - runs-on: opus-runner-standard - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - name: Fail on unpinned action refs - run: | - set -euo pipefail - bad=$(grep -rnE '^[[:space:]]*(-[[:space:]]+)?uses:' .github \ - --include='*.yml' --include='*.yaml' \ - | sed 's/#.*//' \ - | grep 'uses:' \ - | grep -vE "uses:[[:space:]]*[\"']?\./" \ - | grep -vE 'docker://[^[:space:]]+@sha256:' \ - | grep -vE "@[0-9a-f]{40}[\"']?[[:space:]]*$" \ - || true) - if [ -n "$bad" ]; then - echo "Unpinned action references (must be 40-char commit SHAs):" - echo "$bad" - exit 1 - fi - echo "All action references are SHA-pinned." + uses: opus-pro/iac/.github/workflows/actions-pinned-check.yml@main From 3eebff6fb696ddd7e1462970b43344b3177299ea Mon Sep 17 00:00:00 2001 From: Will Wang Date: Fri, 14 Aug 2026 15:43:05 -0700 Subject: [PATCH 2/3] ci: consume shared gate via composite action on opus-runner-standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow_call caller form couldn't resolve/reach the private opus-pro/iac reusable workflow from this repo. Use the iac composite action instead, on a job that keeps runs-on: opus-runner-standard — only that runner's allow-listed IP can fetch the private iac action. Same shared gate logic (check.py), same `check` job name for the required status check. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/actions-pinned-check.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/actions-pinned-check.yml b/.github/workflows/actions-pinned-check.yml index b8e52d7..3e1af7c 100644 --- a/.github/workflows/actions-pinned-check.yml +++ b/.github/workflows/actions-pinned-check.yml @@ -4,9 +4,11 @@ # repoint them at a malicious commit (CVE-2025-30066 hit 23k+ repos via # tj-actions). # -# Delegates to the shared reusable gate in opus-pro/iac so the detection logic -# (a YAML parser that exempts first-party opus-pro/* @main refs, local ./ refs -# and docker@sha256 digests) lives in one place instead of a hand-rolled grep. +# Uses the shared iac gate via its composite action rather than the reusable- +# workflow caller. The job MUST stay on opus-runner-standard: only that runner's +# allow-listed IP can fetch the private opus-pro/iac action. A `workflow_call` +# caller can't set runs-on, and its control-plane resolution can't reach iac +# from this repo — hence the composite-action form here. name: actions-pinned-check on: pull_request: @@ -20,4 +22,9 @@ permissions: jobs: check: - uses: opus-pro/iac/.github/workflows/actions-pinned-check.yml@main + runs-on: opus-runner-standard + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: opus-pro/iac/.github/actions/actions-pinned-check@main From b59f1c41b50c382cd1306c9137c3c0d608a69828 Mon Sep 17 00:00:00 2001 From: Will Wang Date: Mon, 17 Aug 2026 10:06:06 -0700 Subject: [PATCH 3/3] ci: read pr title config from checkout, not the api The org IP allow list rejects contents-API calls from GitHub-hosted runners, so pr-title-checker failed to fetch pull_request_title.json and the gate failed on every PR. local_configuration_path reads the same file from the checkout; with an empty LABEL.name it was the only API call the action made. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/check-pr-basic.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-pr-basic.yml b/.github/workflows/check-pr-basic.yml index 0ef97af..c5fd78d 100644 --- a/.github/workflows/check-pr-basic.yml +++ b/.github/workflows/check-pr-basic.yml @@ -22,4 +22,8 @@ jobs: - uses: thehanimo/pr-title-checker@1d8cd483a2b73118406a187f54dca8a9415f1375 # v1.4.2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - configuration_path: pull_request_title.json \ No newline at end of file + # Read the config off the checkout, not the contents API: the org's IP + # allow list rejects API calls from GitHub-hosted runners ("your IP + # address is not permitted"), which failed this gate on every PR. + # LABEL.name is empty, so this is the action's only API call. + local_configuration_path: pull_request_title.json \ No newline at end of file