Bind container qualification to caller commit

Change-source: pulse-maintainer
This commit is contained in:
rcourtman
2026-08-26 03:36:46 +01:00
parent a0aa5d4e55
commit ee22a969d3
4 changed files with 61 additions and 8 deletions
@@ -862,4 +862,3 @@ jobs:
with:
version: ${{ inputs.version }}
container_artifact: ${{ needs.build.outputs.container_artifact_name }}
source_sha: ${{ github.sha }}
-1
View File
@@ -257,7 +257,6 @@ jobs:
with:
version: ${{ needs.prepare.outputs.version }}
container_artifact: ${{ needs.build_release_candidate.outputs.container_artifact_name }}
source_sha: ${{ github.sha }}
# Build the embed bundle independently so backend and smoke lanes can start
# without waiting for the full frontend quality suite.
@@ -11,10 +11,6 @@ on:
description: 'Exact-candidate container payload artifact from this run'
required: true
type: string
source_sha:
description: 'Exact source commit bound to the candidate payload'
required: true
type: string
permissions:
contents: read
@@ -31,7 +27,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.source_sha }}
# A reusable workflow inherits github.sha from its caller. Do not
# accept a caller-controlled checkout ref: the caller event commit is
# the immutable source identity for both code and candidate payload.
ref: ${{ github.sha }}
persist-credentials: false
- name: Verify exact caller source
env:
EXPECTED_SOURCE_SHA: ${{ github.sha }}
run: test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"
- name: Download exact-candidate container payload
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -45,7 +50,7 @@ jobs:
--release-dir "$RUNNER_TEMP/release-container-payload/payload" \
--manifest "$RUNNER_TEMP/release-container-payload/release-container-payload.json" \
--version "${{ inputs.version }}" \
--source-sha "${{ inputs.source_sha }}"
--source-sha "${{ github.sha }}"
- name: Assemble exact-candidate runtime and agent images
run: |
@@ -1513,6 +1513,56 @@ func TestReleaseWorkflowsUseSecretSafeAttestedImageBuilds(t *testing.T) {
}
}
func TestReleaseContainerQualificationBindsCheckoutToCallerCommit(t *testing.T) {
qualifierBytes, err := os.ReadFile(repoFile(".github", "workflows", "qualify-release-containers.yml"))
if err != nil {
t.Fatalf("read qualify-release-containers.yml: %v", err)
}
candidateBytes, err := os.ReadFile(repoFile(".github", "workflows", "build-release-candidate.yml"))
if err != nil {
t.Fatalf("read build-release-candidate.yml: %v", err)
}
releaseBytes, err := os.ReadFile(repoFile(".github", "workflows", "create-release.yml"))
if err != nil {
t.Fatalf("read create-release.yml: %v", err)
}
qualifier := string(qualifierBytes)
qualifyJob := workflowJobBlock(t, qualifier, "qualify")
for _, required := range []string{
`ref: ${{ github.sha }}`,
`persist-credentials: false`,
`EXPECTED_SOURCE_SHA: ${{ github.sha }}`,
`test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"`,
`--source-sha "${{ github.sha }}"`,
} {
if !strings.Contains(qualifyJob, required) {
t.Fatalf("release container qualification does not fail closed on the exact caller commit: %s", required)
}
}
for _, forbidden := range []string{
"inputs.source_sha",
"source_sha:",
} {
if strings.Contains(qualifier, forbidden) {
t.Fatalf("release container qualification must not accept an arbitrary source ref: %s", forbidden)
}
}
callerJobs := map[string]string{
"build-release-candidate.yml": workflowJobBlock(t, string(candidateBytes), "qualify-release-containers"),
"create-release.yml": workflowJobBlock(t, string(releaseBytes), "qualify_release_containers"),
}
for caller, job := range callerJobs {
if strings.Contains(job, "source_sha:") {
t.Fatalf("%s must not forward a caller-selectable source ref to container qualification", caller)
}
if !strings.Contains(job, "uses: ./.github/workflows/qualify-release-containers.yml") {
t.Fatalf("%s no longer calls the trusted container qualifier", caller)
}
}
}
func TestDeploymentDefaultsPinVersionedImagesAndHelmDocsChecksum(t *testing.T) {
versionBytes, err := os.ReadFile(repoFile("VERSION"))
if err != nil {