Skip to content

Commit 6892174

Browse files
authored
Verify MSI SBOM attestations (#87)
**Why** The registry verifier is being extended to verify SPDX SBOM bundles from release asset attestations. The non-Axiomatic Windows release path already asks GoReleaser for installer SBOMs, but the Windows signing step did not require them and the release validator skipped MSI SBOM verification. **What this changes** - Requires each Windows zip and MSI artifact to have an SPDX SBOM before signing SBOM attestation bundles. - Fails the Windows release job if no Windows SBOM bundles are produced. - Verifies MSI detached signatures, provenance attestations, and SBOM attestations in the release artifact validator. This can land after registry API PR #153; that registry PR intentionally keeps MSI SBOMs optional during the workflow transition. Validation: - bash -n scripts/validate-release-artifacts.sh - ruby -e 'require "yaml"; YAML.load_file(".github/workflows/release.yaml")'\n- go test ./cmd/generate-windows-manifest ./cmd/record-release ./cmd/generate-manifest ./cmd/merge-manifests\n- git diff --check
1 parent 27167b3 commit 6892174

2 files changed

Lines changed: 48 additions & 54 deletions

File tree

.github/workflows/release.yaml

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -634,33 +634,35 @@ jobs:
634634
635635
SIGNED_COUNT=0
636636
637-
# Find all SBOM files generated by GoReleaser (syft)
638-
# All files are in dist root (MSI files flattened by previous step)
639-
for sbom in "${CALLER_DIST}"/*.sbom.json; do
640-
[ -f "$sbom" ] || continue
641-
642-
SBOM_BASENAME=$(basename "$sbom")
643-
ARCHIVE_NAME="${SBOM_BASENAME%.sbom.json}"
644-
ARCHIVE="${CALLER_DIST}/${ARCHIVE_NAME}"
637+
# Require every Windows release artifact to have an SPDX SBOM.
638+
# MSI files are flattened to dist root by the previous step.
639+
for artifact in "${CALLER_DIST}"/*.zip "${CALLER_DIST}"/*.msi; do
640+
[ -f "$artifact" ] || continue
641+
[[ "$artifact" == *checksums* ]] && continue
645642
646-
if [ ! -f "$ARCHIVE" ]; then
647-
echo "::error::Could not find archive for SBOM: $sbom (expected: $ARCHIVE)"
643+
SBOM="${artifact}.sbom.json"
644+
if [ ! -f "$SBOM" ]; then
645+
echo "::error::Missing SBOM for artifact: $(basename "$artifact") (expected: $SBOM)"
648646
exit 1
649647
fi
650648
651-
echo "Signing SBOM for: $(basename "$ARCHIVE")"
649+
echo "Signing SBOM for: $(basename "$artifact")"
652650
cosign attest-blob \
653651
--yes \
654-
--predicate "$sbom" \
652+
--predicate "$SBOM" \
655653
--type https://spdx.dev/Document \
656-
--bundle "${ARCHIVE}.sbom.sigstore.json" \
657-
"$ARCHIVE" > /dev/null
658-
echo "✅ Created $(basename "$ARCHIVE").sbom.sigstore.json"
654+
--bundle "${artifact}.sbom.sigstore.json" \
655+
"$artifact" > /dev/null
656+
echo "✅ Created $(basename "$artifact").sbom.sigstore.json"
659657
((SIGNED_COUNT++)) || true
660658
done
661659
662660
echo "Generated SBOM bundles: ${SIGNED_COUNT}"
663-
ls "${CALLER_DIST}"/*.sbom.sigstore.json 2>/dev/null || echo "ℹ️ No SBOM bundles generated (GoReleaser may not have generated SBOMs)"
661+
if [ "$SIGNED_COUNT" -eq 0 ]; then
662+
echo "::error::No Windows SBOM bundles were generated - this indicates a build problem"
663+
exit 1
664+
fi
665+
ls "${CALLER_DIST}"/*.sbom.sigstore.json
664666
665667
- name: Configure AWS credentials via OIDC
666668
uses: aws-actions/configure-aws-credentials@v5

scripts/validate-release-artifacts.sh

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -123,53 +123,45 @@ for platform in $(echo "$MANIFEST" | jq -r '.assets | keys[]'); do
123123
continue
124124
fi
125125

126-
# Check binary signature (.sig + .cert files) - skip for MSI (derived artifact)
127-
if [[ "$platform" == *-msi ]]; then
128-
info "Skipping .sig/.cert check for $platform (derived artifact)"
129-
else
130-
SIG_FILE="${HREF}.sig"
131-
CERT_FILE="${HREF}.cert"
132-
if curl -sfL "$SIG_FILE" -o "$TEMP_DIR/${FILENAME}.sig" 2>/dev/null && \
133-
curl -sfL "$CERT_FILE" -o "$TEMP_DIR/${FILENAME}.cert" 2>/dev/null; then
134-
if cosign verify-blob \
135-
--signature "$TEMP_DIR/${FILENAME}.sig" \
136-
--certificate "$TEMP_DIR/${FILENAME}.cert" \
137-
--certificate-oidc-issuer "$CERT_OIDC_ISSUER" \
138-
--certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \
139-
"$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then
140-
pass "Binary signature verified: $platform"
141-
else
142-
fail "Binary signature verification failed: $platform"
143-
fi
126+
# Check binary signature (.sig + .cert files)
127+
SIG_FILE="${HREF}.sig"
128+
CERT_FILE="${HREF}.cert"
129+
if curl -sfL "$SIG_FILE" -o "$TEMP_DIR/${FILENAME}.sig" 2>/dev/null && \
130+
curl -sfL "$CERT_FILE" -o "$TEMP_DIR/${FILENAME}.cert" 2>/dev/null; then
131+
if cosign verify-blob \
132+
--signature "$TEMP_DIR/${FILENAME}.sig" \
133+
--certificate "$TEMP_DIR/${FILENAME}.cert" \
134+
--certificate-oidc-issuer "$CERT_OIDC_ISSUER" \
135+
--certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \
136+
"$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then
137+
pass "Binary signature verified: $platform"
144138
else
145-
fail "Binary signature files missing: $platform (.sig or .cert)"
139+
fail "Binary signature verification failed: $platform"
146140
fi
141+
else
142+
fail "Binary signature files missing: $platform (.sig or .cert)"
147143
fi
148144

149-
# Check provenance attestation (skip for MSI - it's derived from the same binary as the zip)
150-
if [[ "$platform" == *-msi ]]; then
151-
info "Skipping provenance check for $platform (derived from zip)"
145+
# Check provenance attestation
146+
PROV_BUNDLE="${HREF}.provenance.sigstore.json"
147+
if ! curl -sfL "$PROV_BUNDLE" -o "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" 2>/dev/null; then
148+
fail "Provenance bundle missing: $PROV_BUNDLE"
152149
else
153-
PROV_BUNDLE="${HREF}.provenance.sigstore.json"
154-
if ! curl -sfL "$PROV_BUNDLE" -o "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" 2>/dev/null; then
155-
fail "Provenance bundle missing: $PROV_BUNDLE"
150+
# Verify provenance
151+
if cosign verify-blob-attestation \
152+
--bundle "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" \
153+
--type https://slsa.dev/provenance/v1 \
154+
--certificate-oidc-issuer "$CERT_OIDC_ISSUER" \
155+
--certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \
156+
"$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then
157+
pass "Provenance verified: $platform"
156158
else
157-
# Verify provenance
158-
if cosign verify-blob-attestation \
159-
--bundle "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" \
160-
--type https://slsa.dev/provenance/v1 \
161-
--certificate-oidc-issuer "$CERT_OIDC_ISSUER" \
162-
--certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \
163-
"$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then
164-
pass "Provenance verified: $platform"
165-
else
166-
fail "Provenance verification failed: $platform"
167-
fi
159+
fail "Provenance verification failed: $platform"
168160
fi
169161
fi
170162

171-
# Check SBOM attestation (skip for checksums and MSI - only binary archives have SBOMs)
172-
if [[ "$platform" == "checksums" || "$platform" == *-msi ]]; then
163+
# Check SBOM attestation
164+
if [[ "$platform" == "checksums" ]]; then
173165
info "Skipping SBOM check for $platform (not applicable)"
174166
else
175167
SBOM_BUNDLE="${HREF}.sbom.sigstore.json"

0 commit comments

Comments
 (0)