mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
* cso: ignore the compiled helpers' leading #! line in preparationSha256, on both sides of the release contract S1 (#3312) rewrote only line 1 of lib/cso/verifier.ts and lib/cso/preparation-container.ts. That shebang is an unused string in the compiled helper binaries, but it moved preparationSha256, so qualify run 39535948607 refused the run-5 statements staged at133cb36with STALE_RUNTIME_QUALIFICATION. Garry chose this narrow rule (option 2). The release contract re-measures a recorded preparationSha256 that differs from the checkout's at the staged commit (git history), accepts a record that matches it under the current or the legacy rule, and compares and records the current-rule value. Both release workflows fetch full history. * test(cso): prove the release-contract fingerprint rule without git history Free-suite checkouts are depth 1, so the tests no longer read133cb36or6b2b5f3. They rebuild the staged commit in a scratch repository from this tree with both helpers' S1 shebang reverted. Its legacy fingerprint equals the real run-5 record (b069f7ef...), so it is 133cb36's image source byte for byte, and the contract accepts that record under the current rule. * v1.91.176.0 stamp
163 lines
8.4 KiB
YAML
163 lines
8.4 KiB
YAML
name: CSO Runtime Qualification
|
|
|
|
# A private evaluator sends only gate results, public runtime identities, and an
|
|
# optional opaque evaluation reference. Its held-out assertions remain outside
|
|
# this repository. GitHub authenticates the dispatch actor; the protected
|
|
# environment supplies the human review gate. The unprotected summary job runs
|
|
# first so the reviewer sees what the dispatch claims before approving.
|
|
on:
|
|
repository_dispatch:
|
|
types: [cso-runtime-qualified]
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
attestations: read
|
|
|
|
jobs:
|
|
dispatch-summary:
|
|
if: github.ref == 'refs/heads/main' && github.event_name == 'repository_dispatch'
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Require the bounded payload and summarize it for the approver
|
|
run: |
|
|
set -euo pipefail
|
|
jq -e '
|
|
.action == "cso-runtime-qualified" and
|
|
((.client_payload | keys) as $keys | $keys == ["statements"] or (
|
|
$keys == ["evaluationRef", "statements"] and
|
|
(.client_payload.evaluationRef | type == "string" and test("^sha256:[a-f0-9]{64}$"))
|
|
)) and
|
|
(.client_payload.statements | type == "array" and length == 10) and
|
|
(.client_payload.statements | all(
|
|
type == "object" and
|
|
(.runtimeId | type == "string" and test("^[a-z0-9][a-z0-9._-]{0,100}$")) and
|
|
(.image | type == "string" and test("^ghcr\\.io/garrytan/gstack/cso-staging/[a-z0-9-]+-(amd64|arm64)@sha256:[a-f0-9]{64}$")) and
|
|
(.sourceCommit | type == "string" and test("^[a-f0-9]{40}$"))
|
|
)) and
|
|
([.client_payload.statements[].sourceCommit] | unique | length == 1)
|
|
' "$GITHUB_EVENT_PATH"
|
|
jq -r '
|
|
"## CSO runtime qualification dispatch",
|
|
"",
|
|
"- evaluationRef: `\(.client_payload.evaluationRef // "none")`",
|
|
"- sourceCommit: `\(.client_payload.statements[0].sourceCommit)`",
|
|
"",
|
|
"| runtimeId | image |",
|
|
"| --- | --- |",
|
|
(.client_payload.statements[] | "| `\(.runtimeId)` | `\(.image)` |")
|
|
' "$GITHUB_EVENT_PATH" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
qualify:
|
|
needs: dispatch-summary
|
|
if: github.ref == 'refs/heads/main' && github.event_name == 'repository_dispatch'
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 40
|
|
environment: cso-runtime-release
|
|
permissions:
|
|
contents: read
|
|
packages: read
|
|
attestations: read
|
|
actions: read
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
|
|
with:
|
|
bun-version: 1.4.2
|
|
- name: Require the configured private-evaluator actor
|
|
env:
|
|
CSO_QUALIFICATION_ACTOR: ${{ vars.CSO_QUALIFICATION_ACTOR }}
|
|
run: |
|
|
set -euo pipefail
|
|
test -n "$CSO_QUALIFICATION_ACTOR"
|
|
test "$GITHUB_ACTOR" = "$CSO_QUALIFICATION_ACTOR"
|
|
- name: Recheck public visibility and anonymous pulls for every qualified digest
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir public-image-evidence
|
|
jq -c '.client_payload.statements[]' "$GITHUB_EVENT_PATH" | while IFS= read -r statement; do
|
|
runtime_id="$(printf '%s' "$statement" | jq -er '.runtimeId | select(test("^[a-z0-9][a-z0-9._-]{0,100}$"))')"
|
|
image="$(printf '%s' "$statement" | jq -er '.image | select(test("^ghcr\\.io/garrytan/gstack/cso-staging/[a-z0-9-]+-(amd64|arm64)@sha256:[a-f0-9]{64}$"))')"
|
|
platform="$(printf '%s' "$statement" | jq -er '.platform | select(. == "linux/amd64" or . == "linux/arm64")')"
|
|
bun run scripts/cso-public-ghcr.ts verify \
|
|
--image "$image" --platform "$platform" --repository "$GITHUB_REPOSITORY" \
|
|
--output "public-image-evidence/$runtime_id.json" --remove-after
|
|
done
|
|
- name: Verify each staged image attestation and normalize qualification statements
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
signer="https://github.com/$GITHUB_REPOSITORY/.github/workflows/cso-runtime-images.yml@refs/heads/main"
|
|
workflow="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
|
|
qualified_at="$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
|
|
mkdir qualification-evidence
|
|
jq -c '.client_payload.statements[]' "$GITHUB_EVENT_PATH" | while IFS= read -r statement; do
|
|
runtime_id="$(printf '%s' "$statement" | jq -er '.runtimeId | select(test("^[a-z0-9][a-z0-9._-]{0,100}$"))')"
|
|
image="$(printf '%s' "$statement" | jq -er '.image | select(test("^ghcr\\.io/garrytan/gstack/cso-staging/[a-z0-9-]+-(amd64|arm64)@sha256:[a-f0-9]{64}$"))')"
|
|
source_commit="$(printf '%s' "$statement" | jq -er '.sourceCommit | select(test("^[a-f0-9]{40}$"))')"
|
|
output="qualification-evidence/$runtime_id"
|
|
mkdir "$output"
|
|
gh attestation verify "oci://$image" --repo "$GITHUB_REPOSITORY" \
|
|
--cert-identity "$signer" --source-ref refs/heads/main --source-digest "$source_commit" \
|
|
--deny-self-hosted-runners --predicate-type https://slsa.dev/provenance/v1 \
|
|
--format json > "$output/provenance-verification.json"
|
|
gh attestation verify "oci://$image" --repo "$GITHUB_REPOSITORY" \
|
|
--cert-identity "$signer" --source-ref refs/heads/main --source-digest "$source_commit" \
|
|
--deny-self-hosted-runners --predicate-type https://spdx.dev/Document/v2.3 \
|
|
--format json > "$output/sbom-verification.json"
|
|
provenance_digest="sha256:$(sha256sum "$output/provenance-verification.json" | cut -d ' ' -f 1)"
|
|
sbom_digest="sha256:$(sha256sum "$output/sbom-verification.json" | cut -d ' ' -f 1)"
|
|
printf '%s' "$statement" | jq \
|
|
--arg workflow "$workflow" --arg qualifiedAt "$qualified_at" \
|
|
--arg provenanceDigest "$provenance_digest" --arg sbomDigest "$sbom_digest" \
|
|
'{
|
|
schemaVersion:.schemaVersion,helperAbi:.helperAbi,state:.state,
|
|
buildRevision:.buildRevision,runtimeId:.runtimeId,stack:.stack,
|
|
platform:.platform,image:.image,versions:.versions,sourceCommit:.sourceCommit,
|
|
checks:.checks,workflow:$workflow,qualifiedAt:$qualifiedAt,
|
|
provenanceDigest:$provenanceDigest,sbomDigest:$sbomDigest
|
|
}' \
|
|
> "$output/qualified-runtime.json"
|
|
done
|
|
- name: Require one successful protected-main staging run with every native gate passed
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir staging-run-evidence
|
|
run_id="$(bun run scripts/cso-staging-run.ts run-id --repository "$GITHUB_REPOSITORY" \
|
|
qualification-evidence/*/provenance-verification.json)"
|
|
gh api "repos/$GITHUB_REPOSITORY/actions/runs/$run_id" > staging-run-evidence/run.json
|
|
gh api --paginate --slurp "repos/$GITHUB_REPOSITORY/actions/runs/$run_id/jobs?filter=latest&per_page=100" \
|
|
> staging-run-evidence/jobs.json
|
|
bun run scripts/cso-staging-run.ts verify --repository "$GITHUB_REPOSITORY" --run-id "$run_id" \
|
|
--event "$GITHUB_EVENT_PATH" --run staging-run-evidence/run.json --jobs staging-run-evidence/jobs.json
|
|
gh run download "$run_id" --repo "$GITHUB_REPOSITORY" \
|
|
--name cso-requalification-triggers --dir requalification-download
|
|
mv requalification-download/requalification.json qualification-evidence/requalification.json
|
|
rmdir requalification-download
|
|
- name: Enforce the complete release contract before retaining evidence
|
|
run: |
|
|
set -euo pipefail
|
|
bun run scripts/cso-runtime-promotion.ts \
|
|
--evidence-root qualification-evidence \
|
|
--output runtime-catalog.proof.json
|
|
rm runtime-catalog.proof.json
|
|
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
|
|
with:
|
|
name: cso-qualified-runtime-statements
|
|
path: |
|
|
qualification-evidence
|
|
public-image-evidence
|
|
staging-run-evidence
|
|
if-no-files-found: error
|
|
retention-days: 30
|