diff --git a/pkg/plugin/signature.go b/pkg/plugin/signature.go index b00d335..e809b5b 100644 --- a/pkg/plugin/signature.go +++ b/pkg/plugin/signature.go @@ -46,6 +46,13 @@ var trustedRootFetcher = func() (root.TrustedMaterial, error) { // inclusion and a trusted timestamp. Any failure — bad signature, wrong identity, // missing log entry, digest mismatch against manifestData — is an error. func verifyManifestSignature(sigBundleJSON, manifestData []byte) error { + // cosign's LEGACY bundle ({base64Signature,cert,rekorBundle}) is not the + // Sigstore protobuf bundle sigstore-go parses; detect it and give a clear + // error rather than a cryptic protobuf "unknown field" message. Releases must + // sign with `cosign sign-blob --new-bundle-format`. + if bytes.Contains(sigBundleJSON, []byte(`"base64Signature"`)) { + return fmt.Errorf("signature is in cosign's legacy bundle format; the release must be signed with --new-bundle-format") + } var b bundle.Bundle if err := b.UnmarshalJSON(sigBundleJSON); err != nil { return fmt.Errorf("parse signature bundle: %w", err) diff --git a/pkg/plugin/signature_test.go b/pkg/plugin/signature_test.go index 1b9f6e6..cb5091d 100644 --- a/pkg/plugin/signature_test.go +++ b/pkg/plugin/signature_test.go @@ -1,6 +1,7 @@ package plugin import ( + "strings" "testing" "github.com/sigstore/sigstore-go/pkg/root" @@ -68,6 +69,20 @@ func TestVerifySignedEntity(t *testing.T) { }) } +// TestVerifyManifestSignature_RejectsLegacyBundle ensures a cosign legacy bundle +// (the default `cosign sign-blob --bundle` output) is rejected with a clear, +// actionable error rather than a cryptic protobuf parse failure. +func TestVerifyManifestSignature_RejectsLegacyBundle(t *testing.T) { + legacy := []byte(`{"base64Signature":"MEUCIQ...","cert":"LS0tLS1CRUdJTi...","rekorBundle":{}}`) + err := verifyManifestSignature(legacy, []byte(`{"any":"manifest"}`)) + if err == nil { + t.Fatal("expected an error for a legacy cosign bundle, got nil") + } + if !strings.Contains(err.Error(), "legacy bundle format") { + t.Errorf("error %q does not explain the legacy-format problem", err) + } +} + // TestTrustedRootFetcherOverride confirms the fetcher hook is overridable, so // verifyManifestSignature never touches the network in tests. (The JSON-bundle // unmarshal is sigstore-go's own tested code; the verification policy it feeds