2222# until the prod WIF pool/SA exist (see aicr-testgrid Terraform apply).
2323#
2424# Pipeline:
25- # 1. Accept the verified, digest-pinned OCI bundle ref from evidence ingest
26- # 2. Validate the bundle ref format
25+ # 1. Accept and validate the digest-pinned OCI bundle ref
26+ # 2. Authenticate to GHCR and verify the signature, issuer, and UAT identity
2727# 3. Authenticate to GCP via WIF (publish SA, groups/ prefix write-only)
28- # 4. Log in to GHCR to pull the evidence OCI bundle
29- # 5. Build testgrid-publish from source
30- # 6. Pull the OCI bundle and write started/junit/finished to GCS
28+ # 4. Build testgrid-publish from source
29+ # 5. Pull the same immutable bundle and write started/junit/finished to GCS
3130#
3231# Auth:
3332# WIF pool: aicr-testgrid[-<env>]-github (aicr-testgrid Terraform)
5756 # Evidence ingest explicitly dispatches this workflow with the verified,
5857 # digest-pinned bundle ref. workflow_dispatch is an allowed GITHUB_TOKEN
5958 # recursion exception, unlike workflow_run chains from bot-dispatched UAT.
60- # Manual dispatch uses the same input for testing and backfills .
59+ # Manual dispatch supports NVIDIA UAT backfills through the same verification .
6160 workflow_dispatch :
6261 inputs :
6362 bundle_ref :
64- description : " OCI bundle ref (ghcr.io/<org> /...@sha256:...)"
63+ description : " NVIDIA UAT OCI bundle ref (ghcr.io/nvidia /...@sha256:...)"
6564 type : string
6665 required : true
67- source_class :
68- description : " Source class"
69- type : choice
70- default : " uat"
71- options :
72- - uat
73- - community
7466 environment :
7567 description : " Target environment"
7668 type : choice
@@ -103,7 +95,11 @@ jobs:
10395 id-token : write # required for WIF token exchange
10496
10597 env :
106- TG_SOURCE_CLASS : ${{ inputs.source_class || 'uat' }}
98+ # Production is stricter than staging ingest: only main/release UAT signers.
99+ FIRST_PARTY_ISSUER : ' https://token.actions.githubusercontent.com'
100+ FIRST_PARTY_IDENTITY : ' ^https://github\.com/NVIDIA/aicr/\.github/workflows/uat-(aws|gcp|azure|kind)\.yaml@refs/heads/(main|release/.+)$'
101+ TRUSTED_REGISTRIES : ' ghcr.io/nvidia'
102+ TG_SOURCE_CLASS : uat
107103
108104 steps :
109105 - name : Checkout
@@ -161,10 +157,8 @@ jobs:
161157 BUNDLE_REF : ${{ inputs.bundle_ref }}
162158 run : |
163159 set -euo pipefail
164- # Validate format: ghcr.io/<org>/...@sha256:<64 hex chars>
165- # Namespace is intentionally NOT restricted to nvidia/ — community
166- # bundles (source_class=community) are hosted under contributor
167- # namespaces, e.g. ghcr.io/yuanchen8911/aicr-evidence.
160+ # This production path accepts only NVIDIA UAT bundle repositories;
161+ # the provenance verification below independently pins the signer.
168162 #
169163 # Uses bash's [[ =~ ]] (whole-string match, POSIX ERE) rather than
170164 # `echo | grep -qE`, which is line-based: a value containing an
@@ -173,22 +167,38 @@ jobs:
173167 # reaches `echo >> GITHUB_OUTPUT` below and injects a second,
174168 # attacker-controlled output key. [[ =~ ]] anchors ^ and $ to the
175169 # start/end of the whole variable, so an embedded newline fails.
176- if [[ ! "${BUNDLE_REF}" =~ ^ghcr\.io/[A-Za-z0-9][A-Za-z0-9-]* /[A-Za-z0-9._/-]+@sha256:[0-9a-f]{64}$ ]]; then
177- echo "::error::BUNDLE_REF '${BUNDLE_REF}' does not match expected format ghcr.io/<org> /...@sha256:<64hex>"
170+ if [[ ! "${BUNDLE_REF}" =~ ^ghcr\.io/nvidia /[A-Za-z0-9._/-]+@sha256:[0-9a-f]{64}$ ]]; then
171+ echo "::error::BUNDLE_REF '${BUNDLE_REF}' does not match expected format ghcr.io/nvidia /...@sha256:<64hex>"
178172 exit 1
179173 fi
180174 echo "bundle_ref=${BUNDLE_REF}" >> "${GITHUB_OUTPUT}"
181175 echo "Resolved bundle: ${BUNDLE_REF}"
182176
183- # Authenticate to GCP via WIF using the testgrid publish SA.
184- # This SA has objectCreator on gs://aicr-testgrid[-<env>]/groups/ only.
185- #
186- # No `if: steps.bundle.outputs.skip != 'true'` guard here (or on the
187- # steps below): unlike the old workflow_run-triggered pointer lookup,
188- # which had a legitimate "no evidence produced" skip path, "Resolve
189- # bundle ref" above validates a required workflow_dispatch input and
190- # exits 1 on failure — a failed resolve already halts the job before
191- # these steps run, so there is no skip state left to guard against.
177+ # Authenticate to GHCR and verify provenance before exchanging GCP
178+ # credentials. The verifier rejects unsigned bundles and pins both the
179+ # Fulcio issuer and the exact NVIDIA UAT workflow identity family.
180+ - name : Login to GHCR
181+ uses : ./.github/actions/ghcr-login
182+
183+ - name : Build evidence verifier
184+ run : |
185+ set -euo pipefail
186+ GOFLAGS="-mod=vendor" go build \
187+ -o /tmp/evidence-project ./tools/evidence-project
188+
189+ - name : Verify evidence provenance
190+ env :
191+ BUNDLE_REF : ${{ steps.bundle.outputs.bundle_ref }}
192+ run : |
193+ set -euo pipefail
194+ /tmp/evidence-project \
195+ -in "${BUNDLE_REF}" \
196+ -out /tmp/testgrid-verified-evidence \
197+ --expected-issuer "${FIRST_PARTY_ISSUER}" \
198+ --expected-identity-regexp "${FIRST_PARTY_IDENTITY}" \
199+ --trusted-registry "${TRUSTED_REGISTRIES}"
200+
201+ # Exchange GCP credentials only after provenance verification succeeds.
192202 - name : Authenticate to GCP
193203 id : gcp-auth
194204 uses : google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
@@ -199,11 +209,6 @@ jobs:
199209 - name : Set up gcloud
200210 uses : google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
201211
202- # Authenticate to GHCR so testgrid-publish can pull the evidence OCI
203- # bundle. The packages: read permission above gates the token scope.
204- - name : Login to GHCR
205- uses : ./.github/actions/ghcr-login
206-
207212 # Always build from source — ensures the binary matches the current
208213 # commit and avoids integrity risks from downloading a release artifact
209214 # into a privileged WIF context. goreleaser packages testgrid-publish
0 commit comments