diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7e932b9..ff3f272 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -634,33 +634,35 @@ jobs: SIGNED_COUNT=0 - # Find all SBOM files generated by GoReleaser (syft) - # All files are in dist root (MSI files flattened by previous step) - for sbom in "${CALLER_DIST}"/*.sbom.json; do - [ -f "$sbom" ] || continue - - SBOM_BASENAME=$(basename "$sbom") - ARCHIVE_NAME="${SBOM_BASENAME%.sbom.json}" - ARCHIVE="${CALLER_DIST}/${ARCHIVE_NAME}" + # Require every Windows release artifact to have an SPDX SBOM. + # MSI files are flattened to dist root by the previous step. + for artifact in "${CALLER_DIST}"/*.zip "${CALLER_DIST}"/*.msi; do + [ -f "$artifact" ] || continue + [[ "$artifact" == *checksums* ]] && continue - if [ ! -f "$ARCHIVE" ]; then - echo "::error::Could not find archive for SBOM: $sbom (expected: $ARCHIVE)" + SBOM="${artifact}.sbom.json" + if [ ! -f "$SBOM" ]; then + echo "::error::Missing SBOM for artifact: $(basename "$artifact") (expected: $SBOM)" exit 1 fi - echo "Signing SBOM for: $(basename "$ARCHIVE")" + echo "Signing SBOM for: $(basename "$artifact")" cosign attest-blob \ --yes \ - --predicate "$sbom" \ + --predicate "$SBOM" \ --type https://spdx.dev/Document \ - --bundle "${ARCHIVE}.sbom.sigstore.json" \ - "$ARCHIVE" > /dev/null - echo "✅ Created $(basename "$ARCHIVE").sbom.sigstore.json" + --bundle "${artifact}.sbom.sigstore.json" \ + "$artifact" > /dev/null + echo "✅ Created $(basename "$artifact").sbom.sigstore.json" ((SIGNED_COUNT++)) || true done echo "Generated SBOM bundles: ${SIGNED_COUNT}" - ls "${CALLER_DIST}"/*.sbom.sigstore.json 2>/dev/null || echo "â„šī¸ No SBOM bundles generated (GoReleaser may not have generated SBOMs)" + if [ "$SIGNED_COUNT" -eq 0 ]; then + echo "::error::No Windows SBOM bundles were generated - this indicates a build problem" + exit 1 + fi + ls "${CALLER_DIST}"/*.sbom.sigstore.json - name: Configure AWS credentials via OIDC uses: aws-actions/configure-aws-credentials@v5 diff --git a/scripts/validate-release-artifacts.sh b/scripts/validate-release-artifacts.sh index 76e8404..4e9e701 100755 --- a/scripts/validate-release-artifacts.sh +++ b/scripts/validate-release-artifacts.sh @@ -123,53 +123,45 @@ for platform in $(echo "$MANIFEST" | jq -r '.assets | keys[]'); do continue fi - # Check binary signature (.sig + .cert files) - skip for MSI (derived artifact) - if [[ "$platform" == *-msi ]]; then - info "Skipping .sig/.cert check for $platform (derived artifact)" - else - SIG_FILE="${HREF}.sig" - CERT_FILE="${HREF}.cert" - if curl -sfL "$SIG_FILE" -o "$TEMP_DIR/${FILENAME}.sig" 2>/dev/null && \ - curl -sfL "$CERT_FILE" -o "$TEMP_DIR/${FILENAME}.cert" 2>/dev/null; then - if cosign verify-blob \ - --signature "$TEMP_DIR/${FILENAME}.sig" \ - --certificate "$TEMP_DIR/${FILENAME}.cert" \ - --certificate-oidc-issuer "$CERT_OIDC_ISSUER" \ - --certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \ - "$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then - pass "Binary signature verified: $platform" - else - fail "Binary signature verification failed: $platform" - fi + # Check binary signature (.sig + .cert files) + SIG_FILE="${HREF}.sig" + CERT_FILE="${HREF}.cert" + if curl -sfL "$SIG_FILE" -o "$TEMP_DIR/${FILENAME}.sig" 2>/dev/null && \ + curl -sfL "$CERT_FILE" -o "$TEMP_DIR/${FILENAME}.cert" 2>/dev/null; then + if cosign verify-blob \ + --signature "$TEMP_DIR/${FILENAME}.sig" \ + --certificate "$TEMP_DIR/${FILENAME}.cert" \ + --certificate-oidc-issuer "$CERT_OIDC_ISSUER" \ + --certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \ + "$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then + pass "Binary signature verified: $platform" else - fail "Binary signature files missing: $platform (.sig or .cert)" + fail "Binary signature verification failed: $platform" fi + else + fail "Binary signature files missing: $platform (.sig or .cert)" fi - # Check provenance attestation (skip for MSI - it's derived from the same binary as the zip) - if [[ "$platform" == *-msi ]]; then - info "Skipping provenance check for $platform (derived from zip)" + # Check provenance attestation + PROV_BUNDLE="${HREF}.provenance.sigstore.json" + if ! curl -sfL "$PROV_BUNDLE" -o "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" 2>/dev/null; then + fail "Provenance bundle missing: $PROV_BUNDLE" else - PROV_BUNDLE="${HREF}.provenance.sigstore.json" - if ! curl -sfL "$PROV_BUNDLE" -o "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" 2>/dev/null; then - fail "Provenance bundle missing: $PROV_BUNDLE" + # Verify provenance + if cosign verify-blob-attestation \ + --bundle "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" \ + --type https://slsa.dev/provenance/v1 \ + --certificate-oidc-issuer "$CERT_OIDC_ISSUER" \ + --certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \ + "$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then + pass "Provenance verified: $platform" else - # Verify provenance - if cosign verify-blob-attestation \ - --bundle "$TEMP_DIR/${FILENAME}.provenance.sigstore.json" \ - --type https://slsa.dev/provenance/v1 \ - --certificate-oidc-issuer "$CERT_OIDC_ISSUER" \ - --certificate-identity-regexp "$CERT_IDENTITY_REGEXP" \ - "$TEMP_DIR/$FILENAME" > /dev/null 2>&1; then - pass "Provenance verified: $platform" - else - fail "Provenance verification failed: $platform" - fi + fail "Provenance verification failed: $platform" fi fi - # Check SBOM attestation (skip for checksums and MSI - only binary archives have SBOMs) - if [[ "$platform" == "checksums" || "$platform" == *-msi ]]; then + # Check SBOM attestation + if [[ "$platform" == "checksums" ]]; then info "Skipping SBOM check for $platform (not applicable)" else SBOM_BUNDLE="${HREF}.sbom.sigstore.json"