diff --git a/cli/slsa-verifier/main.go b/cli/slsa-verifier/main.go index bf066f4..41dcfa8 100644 --- a/cli/slsa-verifier/main.go +++ b/cli/slsa-verifier/main.go @@ -16,10 +16,6 @@ func check(err error) { } } -func ExperimentalEnabled() bool { - return os.Getenv("SLSA_VERIFIER_EXPERIMENTAL") == "1" -} - func rootCmd() *cobra.Command { c := &cobra.Command{ Use: "slsa-verifier", diff --git a/cli/slsa-verifier/verify.go b/cli/slsa-verifier/verify.go index ac987b7..c9d6ce0 100644 --- a/cli/slsa-verifier/verify.go +++ b/cli/slsa-verifier/verify.go @@ -43,7 +43,6 @@ func verifyArtifactCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { v := verify.VerifyArtifactCommand{ ProvenancePath: o.ProvenancePath, - BundlePath: o.BundlePath, SourceURI: o.SourceURI, PrintProvenance: o.PrintProvenance, BuildWorkflowInputs: o.BuildWorkflowInputs.AsMap(), @@ -61,21 +60,6 @@ func verifyArtifactCmd() *cobra.Command { v.BuilderID = &o.BuilderID } - // In experimental mode, we allow either provenance or bundle path, but exactly - // one must be set. We already check to ensure that they are mutually exclusive. - if ExperimentalEnabled() { - if !(cmd.Flags().Changed("provenance-path") || - cmd.Flags().Changed("bundle-path")) { - fmt.Fprintf(os.Stderr, "%s\n%s", cmd.UsageString(), - "exactly one of --provenance-path or --bundle-path must be supplied") - os.Exit(1) - } - } else if !cmd.Flags().Changed("provenance-path") { - // --provenance-path must be set. - fmt.Fprintf(os.Stderr, "%s\n%s\n", cmd.UsageString(), "--provenance-path must be supplied") - os.Exit(1) - } - if _, err := v.Exec(cmd.Context(), args); err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", FAILURE, err) os.Exit(1) @@ -86,6 +70,8 @@ func verifyArtifactCmd() *cobra.Command { } o.AddFlags(cmd) + // --provenance-path must be supplied when verifying an artifact. + cmd.MarkFlagRequired("provenance-path") return cmd } diff --git a/cli/slsa-verifier/verify/options.go b/cli/slsa-verifier/verify/options.go index ea3016b..f860cb1 100644 --- a/cli/slsa-verifier/verify/options.go +++ b/cli/slsa-verifier/verify/options.go @@ -39,7 +39,6 @@ type VerifyOptions struct { BuilderID string /* Other */ ProvenancePath string - BundlePath string PrintProvenance bool } @@ -68,17 +67,11 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.ProvenancePath, "provenance-path", "", "path to a provenance file") - cmd.Flags().StringVar(&o.BundlePath, "bundle-path", "", - "path to a Sigstore provenance bundle file containing offline information.") - cmd.Flags().BoolVar(&o.PrintProvenance, "print-provenance", false, "[optional] print the verified provenance to stdout") cmd.MarkFlagRequired("source-uri") cmd.MarkFlagsMutuallyExclusive("source-versioned-tag", "source-tag") - - // Enforce exactly one of --provenance-path and --bundle-path. - cmd.MarkFlagsMutuallyExclusive("provenance-path", "bundle-path") } type workflowInputs struct { diff --git a/cli/slsa-verifier/verify/verify_artifact.go b/cli/slsa-verifier/verify/verify_artifact.go index 01090b7..0a3e854 100644 --- a/cli/slsa-verifier/verify/verify_artifact.go +++ b/cli/slsa-verifier/verify/verify_artifact.go @@ -30,7 +30,6 @@ import ( // Note: nil branch, tag, version-tag and builder-id means we ignore them during verification. type VerifyArtifactCommand struct { ProvenancePath string - BundlePath string BuilderID *string SourceURI string SourceBranch *string @@ -63,20 +62,10 @@ func (c *VerifyArtifactCommand) Exec(ctx context.Context, artifacts []string) (* ExpectedID: c.BuilderID, } - var provenance []byte - if c.ProvenancePath != "" { - provenance, err = os.ReadFile(c.ProvenancePath) - if err != nil { - fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) - return nil, err - } - } else { - bundle, err := os.ReadFile(c.BundlePath) - if err != nil { - fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) - return nil, err - } - provenanceOpts.ProvenanceBundle = bundle + provenance, err := os.ReadFile(c.ProvenancePath) + if err != nil { + fmt.Fprintf(os.Stderr, "Verifying artifact %s: FAILED: %v\n\n", artifact, err) + return nil, err } verifiedProvenance, outBuilderID, err := verifiers.VerifyArtifact(ctx, provenance, artifactHash, provenanceOpts, builderOpts) diff --git a/options/env.go b/options/env.go new file mode 100644 index 0000000..5b52787 --- /dev/null +++ b/options/env.go @@ -0,0 +1,7 @@ +package options + +import "os" + +func ExperimentalEnabled() bool { + return os.Getenv("SLSA_VERIFIER_EXPERIMENTAL") == "1" +} diff --git a/options/options.go b/options/options.go index 2b78f83..f5cd591 100644 --- a/options/options.go +++ b/options/options.go @@ -23,9 +23,6 @@ type ProvenanceOpts struct { // ExpectedWorkflowInputs is a map of key=value inputs. ExpectedWorkflowInputs map[string]string - - // Bundle containing information to verify the provenance offline. - ProvenanceBundle []byte } // BuildOpts are the options for checking the builder. diff --git a/verifiers/internal/gha/bundle.go b/verifiers/internal/gha/bundle.go index 11d64e8..7b52f89 100644 --- a/verifiers/internal/gha/bundle.go +++ b/verifiers/internal/gha/bundle.go @@ -24,6 +24,15 @@ var ( ErrorUnexpectedBundleContent = errors.New("expected DSSE bundle content") ) +// IsSigstoreBundle checks if the provenance is a Sigstore bundle. +func IsSigstoreBundle(bytes []byte) bool { + var bundle bundle_v1.Bundle + if err := protojson.Unmarshal(bytes, &bundle); err != nil { + return false + } + return true +} + // verifyRekorEntryFromBundle extracts and verifies the Rekor entry from the Sigstore // bundle verification material, validating the SignedEntryTimestamp. func verifyRekorEntryFromBundle(ctx context.Context, tlogEntry *v1.TransparencyLogEntry, diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index 6b43b6d..c5ce51b 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -4,6 +4,7 @@ import ( "context" "crypto/x509" "encoding/base64" + "errors" "fmt" "os" "strings" @@ -85,6 +86,11 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, provenanceOpts *options.ProvenanceOpts, builderOpts *options.BuilderOpts, ) ([]byte, *utils.TrustedBuilderID, error) { + isSigstoreBundle := IsSigstoreBundle(provenance) + if isSigstoreBundle && !options.ExperimentalEnabled() { + return nil, nil, errors.New("sigstore bundle support is only provided in SLSA_VERIFIER_EXPERIMENTAL mode") + } + // This includes a default retry count of 3. rClient, err := client.GetRekorClient(defaultRekorAddr) if err != nil { @@ -98,9 +104,8 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, var signedAtt *SignedAttestation /* Verify signature on the intoto attestation. */ - if provenanceOpts.ProvenanceBundle != nil { - signedAtt, err = VerifyProvenanceBundle(ctx, provenanceOpts.ProvenanceBundle, - trustedRoot) + if isSigstoreBundle { + signedAtt, err = VerifyProvenanceBundle(ctx, provenance, trustedRoot) } else { signedAtt, err = VerifyProvenanceSignature(ctx, trustedRoot, rClient, provenance, artifactHash)