diff --git a/.github/affirmation-trusted/README.adoc b/.github/affirmation-trusted/README.adoc index d00d56a..26505b6 100644 --- a/.github/affirmation-trusted/README.adoc +++ b/.github/affirmation-trusted/README.adoc @@ -9,27 +9,69 @@ commit-signing public key: * fetched 2026-10-28 from `gh api users/web-flow/gpg_keys` (the entry with `key_id` `B5690EEEBB952194`) +`github-web-flow-previous.gpg` is the expired key it replaced +(`5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23`), kept so historical commits can +still be classified. + == Why it exists -The Affirmation Document Gate (`hyperpolymath/cicd-suite` -`actions/affirmation-check`, run from `main-estate-audit.yml`) classifies the -signature on the commit that last changed `AFFIRMATION.adoc`. The copy of the -web-flow key vendored inside that action is GitHub's *previous* key +The Affirmation Document Gate (`actions/affirmation-check`) attempts to classify +the signature on the commit that last changed `AFFIRMATION.adoc`, using the +available Git history. The copy of the web-flow key vendored inside that action +is GitHub's *previous* key (`5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23`, expired 2024-01-16) — the action's own fingerprint allowlist names the new key, but the new key is not in its -keyring. Every GitHub-signed commit (web merges, squash landings, and the -test-merge commit a shallow PR checkout consists of) therefore classified as -`E` ("cannot be checked"), which the gate treats as "bad, revoked, or failed -verification" and fails on. That was the `estate-audit` failure on PR #102. - -The workflow now runs `.github/affirmation-trusted/check.sh`, vendored from -`cicd-suite@bf7dd95cf49733019343ff485c2f45c6b261839c` with a local repair: -`E` accompanied by GnuPG's `NO_PUBKEY` is a notice; bad, revoked, and other failed +keyring. A GitHub-signed commit whose key the keyring lacks (web merges, squash +landings, and the test-merge commit a shallow PR checkout consists of) +classifies as `E` ("cannot be checked"), which the action treats as "bad, +revoked, or failed verification" and fails on. That was the `estate-audit` +failure on PR #102. + +`check.sh` in this directory is that action's script vendored from +`cicd-suite@bf7dd95cf49733019343ff485c2f45c6b261839c` with a local repair: `E` +accompanied by GnuPG's `NO_PUBKEY` is a notice; bad, revoked, and other failed verifications still fail. The local script imports both the current and historical vendored GitHub keys and rejects them unless their primary -fingerprints match the two pinned above. This is a contained workaround, not -the durable trust boundary: gate code in a pull-request checkout is necessarily -reviewed code, whereas the root fix belongs in `cicd-suite`, whose pinned -external action can then be restored. If GitHub rotates the web-flow key -again, re-fetch it the same way and update the fingerprint above in the same -commit. +fingerprints match the two pinned above. + +== Current status (2026-11-09) + +`main-estate-audit.yml` is now a thin caller of the suite's shared reusable +workflow, so the gate that runs on this repository is *upstream's* +`actions/affirmation-check` at the suite's own pin — not the local script. The +local script is no longer wired into CI. + +That gate checks out at depth 1, so the commit whose signature it classifies is +the *checkout boundary*. On a pull request that is the test-merge commit; on a +push it is the branch tip. Neither is necessarily the commit that last changed +`AFFIRMATION.adoc`. The gate emits a notice naming the boundary commit. Even +when that commit's signature verifies, CI may not have checked the signature +on the commit that last changed `AFFIRMATION.adoc`. A passing gate can also +report that the boundary commit's signature could not be verified. +Measured on this repository 2026-11-09: the gate passes in both shapes +(`estate-audit / governance` on PR run 42605863386; a depth-1 push-shape run +over the GitHub-signed landing commit `c0eb4f1` exited 0 with "verified with a +trusted key: GitHub "), and it classifies the boundary +commit in both. + +The local script is kept because: + +* it is the reference implementation of the fix the upstream action still + needs. Re-checked 2026-11-09: the suite's vendored `github-web-flow.gpg` is + byte-identical to `github-web-flow-previous.gpg` here — its only key is the + 2017 primary `5DE3E050…`, not the 2024 key `968479A1…` that signed this + repository's recent landings. Upstream's verification of those landings + therefore depends on resolving a key that is not in the file it ships (it + succeeded on the runner, which has network), which is precisely the fragile + path the local repair hardens: it ships both keys and turns a `NO_PUBKEY` + into a notice instead of a failure; +* `tests/workflows/review_regressions_test.sh` exercises it, so the repair + cannot rot silently; +* if the shared gate ever starts failing on a key it cannot classify, this + script is the local oracle to diagnose it — run + `bash .github/affirmation-trusted/check.sh` from the repository root. + +This was always a contained workaround, not the durable trust boundary: gate +code in a pull-request checkout is necessarily reviewed code, and the root fix +belongs upstream. If GitHub rotates the web-flow key again, re-fetch it the +same way and update the fingerprint above in the same commit. diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 15bad72..01dea48 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -25,9 +25,7 @@ workflows: - 'step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a' '.github/workflows/label-triage.yml': [] '.github/workflows/labels.yml': [] - '.github/workflows/main-estate-audit.yml': - - 'actions/checkout@v7.0.1' - - 'hyperpolymath/cicd-suite@bf7dd95cf49733019343ff485c2f45c6b261839c' + '.github/workflows/main-estate-audit.yml': [] '.github/workflows/mirror.yml': [] '.github/workflows/openssf-compliance.yml': - 'actions/checkout@v7.0.1' diff --git a/.github/workflows/main-estate-audit.yml b/.github/workflows/main-estate-audit.yml index 885a13d..60f6df4 100644 --- a/.github/workflows/main-estate-audit.yml +++ b/.github/workflows/main-estate-audit.yml @@ -1,5 +1,55 @@ -# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 +# This workflow is managed by gh actions-lock. +# +# Central Estate CI/CD Audit — a THIN CALLER of the shared estate audit. +# +# This file used to be an expanded copy of the audit: one job of 26 steps, each +# a direct `uses:` of a hyperpolymath/cicd-suite composite. A copy freezes the +# repository at whatever suite commit its actions.lock happened to hold, so +# upstream fixes never arrive, and it had drifted four ways: +# +# * It still ran `prat-check` and `semantic-audit-check`, which upstream +# removed as stubs that passed unconditionally (README, known defect 5: +# a green tick that credited assurance nothing checked). +# * It still ran `contractile-validation-check` and `proof-runner-check`, +# removed upstream for pass-on-absence (known defect 7): each printed +# "passed" when the tool it exists to run was absent — the normal case on +# a hosted runner — so they could only ever certify scans that never ran. +# * One job stopped at its first failing gate, so a single red hid the other +# 25 and every failure read "estate-audit: failing after 8s" with no gate +# named. The shared workflow reports five component checks (governance, +# hygiene, manifests, languages, advisory), each gate its own step, and +# keeps running the remaining gates after one fails. +# * Every step named `hyperpolymath/cicd-suite`, the suite's PRE-RENAME +# coordinate. Measured on this branch (runs 42603702962 / 42603704212, +# 2026-11-09): a reusable-workflow call under the old name starts with ZERO +# jobs and reports only "failure", while the same pin under the canonical +# name starts all five component checks. Resolving the old coordinate via +# the API still succeeds because REST follows the transfer redirect — the +# redirect is exactly what made the failure invisible. This repository's +# own workflow-linter re-resolution step flags it 24 times. +# +# So the audit is called, not copied: +# +# * Pin is a full branch-reachable commit SHA on the suite's default branch +# (2026-11-09 main). Never `@main` or a tag: the account enforces +# sha_pinning_required, and a branch ref would change the gates in every +# consuming repository at once. Never a blob SHA. +# * Caller job id stays `estate-audit`, and the workflow name is unchanged, +# so the required context keeps the prefix it had. The component checks +# report as `estate-audit / governance`, `estate-audit / hygiene`, +# `estate-audit / manifests`, `estate-audit / languages`, +# `estate-audit / advisory`. +# * DE-ONBOARDED from actions.lock: this file's entry lists no step-level +# action refs (the reusable call is a job-level `uses:`, which +# gh actions-lock does not track). An onboarded cross-repo reusable call +# under sha_pinning_required is the zero-job startup failure measured +# above. Regenerate the lock with `gh actions-lock` — do not hand-edit it. +# +# The Affirmation Document Gate now comes from the shared workflow. This +# repository's local repair (.github/affirmation-trusted/) remains the +# reference implementation for the upstream defect it documents; see that +# directory's README. name: Central Estate CI/CD Audit @@ -9,132 +59,10 @@ on: pull_request: branches: [ "main" ] -# Least privilege: every gate below is a read-only check over the tree. +# Least privilege: the called workflow only reads this repository's tree. permissions: contents: read jobs: estate-audit: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - # fetch-depth: 0 is load-bearing for the Affirmation Document Gate: it - # classifies the signature on the commit that last changed - # AFFIRMATION.adoc. In the default depth-1 checkout that "commit" is - # the shallow boundary (on a PR, GitHub's test-merge commit), so the - # gate graded a checkout artefact, not the affirming commit. The local - # check treats unavailable public keys as notices and still rejects bad - # signatures; the current GitHub key is in .github/affirmation-trusted/. - - uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Required Files Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/required-files-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Code Hygiene Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/code-hygiene-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Manifest Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/manifest-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Idris2 ABI Purity Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/idris2-abi-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Zig UnifiedApiAdapter API Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/zig-unified-api-adapter-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Contractile Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/contractile-validation-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Recipes Set Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/recipes-set-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Affirmation Document Gate - if: ${{ !cancelled() }} - run: bash .github/affirmation-trusted/check.sh - - - name: Academic Referencing Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/referencing-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Semantic Audit Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/semantic-audit-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: SPDX License Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/spdx-license-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Proof Runner Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/proof-runner-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: PRAT Testing Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/prat-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - # No "Panic Attack & Pons" step here (removed 2026-10-28): the - # cicd-suite custom-tools-check composite prints "validation passed" - # when neither tool is on PATH — which is always — so it certified - # scans that never ran. panic-attack and pons-asinorum evidence is - # produced exactly once per trigger by the standards Hypatia lane - # (fork-governance.yml on PRs, hypatia-scan.yml on push/schedule) - # as verdict-grid rows with status envelopes; a presence proxy in - # this audit would only be a second, weaker claim to reconcile. - - - name: WWW & Well-Known Compliance Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/www-compliance-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: BoJ Cartridge Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/boj-cartridge-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Formatting Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/formatting-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Accreditations & Badges Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/badges-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Metrics Extraction Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/metrics-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Linguist & Banned Languages Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/linguist-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Test & Benchmarks Dashboard Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/tests-benches-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Hosting & Site Status Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/hosting-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Git-Sea Analytics Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/gitsea-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Trust & Humans Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/trust-humans-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Are We UnAPI Gate (Secret Scanning) - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/secrets-check@bf7dd95cf49733019343ff485c2f45c6b261839c - - - name: Reasonably Good Token Validation Gate - if: ${{ !cancelled() }} - uses: hyperpolymath/cicd-suite/actions/vaulted-tokens-check@bf7dd95cf49733019343ff485c2f45c6b261839c + uses: hyperpolymath/proven-cicd-and-workflows/.github/workflows/main-estate-audit.yml@3120495390635096552935d904652789253938e8 # suite main, 2026-11-09