From 7a6c77eddd321352441f757e66a8f0e680d21802 Mon Sep 17 00:00:00 2001 From: George Elphick Date: Sat, 22 Aug 2026 14:03:00 +0100 Subject: [PATCH] Close the pinning loopholes (caersid Codex-review asks) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Auto-merge admission gate: github_actions-ecosystem Dependabot PRs never auto-approve and never enter the AI upgrade queue — SHA-pin bumps of the org workflows now always get a human, which is what makes consumer pinning meaningful. Ecosystem string verified against fetch-metadata source (parsed from the dependabot/github_actions/* branch name). 2. TALIEISIN_TRIGGER_APP_ID/_PRIVATE_KEY declared (required:false) in auto-merge.yml workflow_call.secrets so callers can pass them by name instead of secrets:inherit; inherit callers unchanged. 3. Internal validated-push@main self-references (dependabot-upgrade x2, copilot-response x1) pinned to fea67b6 — the outer pin was mutable underneath while these resolved @main at run time. This repo's own dependabot bumps them (and after item 1, those bumps get reviewed). Claude-Session: https://claude.ai/code/session_012EuXdzh4NJzL5mxmsqRDzR --- .github/workflows/auto-merge.yml | 32 +++++++++++++++++++++--- .github/workflows/copilot-response.yml | 2 +- .github/workflows/dependabot-upgrade.yml | 4 +-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 9ba7bd0..a1bb5de 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -1,6 +1,14 @@ name: Dependabot auto-merge on: workflow_call: + # Declared so callers can pass these by NAME instead of `secrets: inherit`. + # Optional: the workflow degrades gracefully without them (fallback comment + # path), and inherit-style callers keep working unchanged. + secrets: + TALIEISIN_TRIGGER_APP_ID: + required: false + TALIEISIN_TRIGGER_APP_PRIVATE_KEY: + required: false permissions: contents: write @@ -46,14 +54,30 @@ jobs: fi done < <(printf '%s\n' "$REVIEWERS" | tr ',' '\n' | tr -d ' ') done + # github-actions ecosystem bumps NEVER auto-merge, regardless of semver: + # consumers pin the org reusable workflows by commit SHA precisely so a + # new workflow version cannot reach them unreviewed — letting Dependabot's + # SHA-advance PRs self-approve would silently undo that. They also skip + # the AI upgrade queue: CI-infrastructure changes get a human. - name: Auto-approve and enable auto-merge - if: steps.meta.outputs.update-type != 'version-update:semver-major' + if: >- + steps.meta.outputs.update-type != 'version-update:semver-major' && + steps.meta.outputs.package-ecosystem != 'github_actions' run: | gh pr review --approve "$PR_URL" gh pr merge --auto --squash --delete-branch "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Note for workflow/action bumps (human review required) + if: >- + steps.meta.outputs.package-ecosystem == 'github_actions' && + (github.event.action == 'opened' || github.event.action == 'reopened') + run: | + gh pr comment "$PR_URL" --body "GitHub Actions dependency bump — auto-merge deliberately skipped: changes to CI workflows/actions require human review (this is what makes SHA-pinning the org workflows meaningful)." + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Majors: queue for the AI upgrade pipeline (documented in the org's # internal automation runbook). The label must be applied with an App token, not # GITHUB_TOKEN — label events created with GITHUB_TOKEN do not trigger @@ -64,7 +88,7 @@ jobs: # the PR through the dispatcher forever. - name: Mint trigger-app token id: trigger-token - if: steps.meta.outputs.update-type == 'version-update:semver-major' && (github.event.action == 'opened' || github.event.action == 'reopened') + if: steps.meta.outputs.update-type == 'version-update:semver-major' && steps.meta.outputs.package-ecosystem != 'github_actions' && (github.event.action == 'opened' || github.event.action == 'reopened') continue-on-error: true uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2 with: @@ -72,7 +96,7 @@ jobs: private-key: ${{ secrets.TALIEISIN_TRIGGER_APP_PRIVATE_KEY }} repositories: ${{ github.event.repository.name }} - name: Queue for AI upgrade (major) - if: steps.meta.outputs.update-type == 'version-update:semver-major' && steps.trigger-token.outcome == 'success' + if: steps.meta.outputs.update-type == 'version-update:semver-major' && steps.meta.outputs.package-ecosystem != 'github_actions' && steps.trigger-token.outcome == 'success' run: | # Belt and braces: never re-queue a PR already in the AI lifecycle. existing=$(gh pr view "$PR_URL" --json labels --jq '[.labels[].name | select(startswith("ai-"))] | length') @@ -95,7 +119,7 @@ jobs: REPO: ${{ github.repository }} GH_TOKEN: ${{ steps.trigger-token.outputs.token }} - name: Note for major version bumps (fallback, trigger app unavailable) - if: steps.meta.outputs.update-type == 'version-update:semver-major' && (github.event.action == 'opened' || github.event.action == 'reopened') && steps.trigger-token.outcome != 'success' + if: steps.meta.outputs.update-type == 'version-update:semver-major' && steps.meta.outputs.package-ecosystem != 'github_actions' && (github.event.action == 'opened' || github.event.action == 'reopened') && steps.trigger-token.outcome != 'success' run: | gh pr comment "$PR_URL" --body "Major version bump — left for human review (auto-merge skipped; AI upgrade trigger app not configured)." env: diff --git a/.github/workflows/copilot-response.yml b/.github/workflows/copilot-response.yml index b4d856a..3267f69 100644 --- a/.github/workflows/copilot-response.yml +++ b/.github/workflows/copilot-response.yml @@ -312,7 +312,7 @@ jobs: - name: Validated push (response) if: steps.haspatch.outputs.present == 'true' id: vpush - uses: Talieisin/.github/.github/actions/validated-push@main + uses: Talieisin/.github/.github/actions/validated-push@fea67b6fa1fb8be048f392582ce36c45c2f2b1ff # 2026-08-22, bumped by dependabot with: token: ${{ steps.app-token.outputs.token }} app-slug: ${{ steps.app-token.outputs.app-slug }} diff --git a/.github/workflows/dependabot-upgrade.yml b/.github/workflows/dependabot-upgrade.yml index 1e41046..3e85392 100644 --- a/.github/workflows/dependabot-upgrade.yml +++ b/.github/workflows/dependabot-upgrade.yml @@ -371,7 +371,7 @@ jobs: - name: Validated push (round 1) if: needs.gate.outputs.verdict == 'proceed' && steps.pre.outputs.verdict == 'push' id: vpush - uses: Talieisin/.github/.github/actions/validated-push@main + uses: Talieisin/.github/.github/actions/validated-push@fea67b6fa1fb8be048f392582ce36c45c2f2b1ff # 2026-08-22, bumped by dependabot with: token: ${{ steps.app-token.outputs.token }} app-slug: ${{ steps.app-token.outputs.app-slug }} @@ -937,7 +937,7 @@ jobs: - name: Validated push (round 2) if: steps.pre.outputs.verdict == 'push' id: vpush - uses: Talieisin/.github/.github/actions/validated-push@main + uses: Talieisin/.github/.github/actions/validated-push@fea67b6fa1fb8be048f392582ce36c45c2f2b1ff # 2026-08-22, bumped by dependabot with: token: ${{ steps.app-token.outputs.token }} app-slug: ${{ steps.app-token.outputs.app-slug }}