diff --git a/pkg/cmd/attestation/inspect/inspect.go b/pkg/cmd/attestation/inspect/inspect.go index 31afb7ce6..c139a5af2 100644 --- a/pkg/cmd/attestation/inspect/inspect.go +++ b/pkg/cmd/attestation/inspect/inspect.go @@ -141,9 +141,9 @@ func runInspect(opts *Options) error { return fmt.Errorf("failed to build policy: %v", err) } - res, err := opts.SigstoreVerifier.Verify(attestations, policy) - if err != nil { - return fmt.Errorf("at least one attestation failed to verify against Sigstore: %v", err) + res := opts.SigstoreVerifier.Verify(attestations, policy) + if res.Error != nil { + return fmt.Errorf("at least one attestation failed to verify against Sigstore: %v", res.Error) } opts.Logger.VerbosePrint(opts.Logger.ColorScheme.Green( @@ -152,7 +152,7 @@ func runInspect(opts *Options) error { // If the user provides the --format=json flag, print the results in JSON format if opts.exporter != nil { - details, err := getAttestationDetails(opts.Tenant, res) + details, err := getAttestationDetails(opts.Tenant, res.VerifyResults) if err != nil { return fmt.Errorf("failed to get attestation detail: %v", err) } @@ -165,7 +165,7 @@ func runInspect(opts *Options) error { } // otherwise, print results in a table - details, err := getDetailsAsSlice(opts.Tenant, res) + details, err := getDetailsAsSlice(opts.Tenant, res.VerifyResults) if err != nil { return fmt.Errorf("failed to parse attestation details: %v", err) } diff --git a/pkg/cmd/attestation/verification/mock_verifier.go b/pkg/cmd/attestation/verification/mock_verifier.go index 41332dc62..e22142ed5 100644 --- a/pkg/cmd/attestation/verification/mock_verifier.go +++ b/pkg/cmd/attestation/verification/mock_verifier.go @@ -16,7 +16,7 @@ type MockSigstoreVerifier struct { t *testing.T } -func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { +func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) *SigstoreResults { statement := &in_toto.Statement{} statement.PredicateType = SLSAPredicateV1 @@ -41,7 +41,9 @@ func (v *MockSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve results := []*AttestationProcessingResult{&result} - return results, nil + return &SigstoreResults{ + VerifyResults: results, + } } func NewMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { @@ -50,6 +52,8 @@ func NewMockSigstoreVerifier(t *testing.T) *MockSigstoreVerifier { type FailSigstoreVerifier struct{} -func (v *FailSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { - return nil, fmt.Errorf("failed to verify attestations") +func (v *FailSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) *SigstoreResults { + return &SigstoreResults{ + Error: fmt.Errorf("failed to verify attestations"), + } } diff --git a/pkg/cmd/attestation/verification/sigstore.go b/pkg/cmd/attestation/verification/sigstore.go index 825f9da1c..34904988f 100644 --- a/pkg/cmd/attestation/verification/sigstore.go +++ b/pkg/cmd/attestation/verification/sigstore.go @@ -28,6 +28,11 @@ type AttestationProcessingResult struct { VerificationResult *verify.VerificationResult `json:"verificationResult"` } +type SigstoreResults struct { + VerifyResults []*AttestationProcessingResult + Error error +} + type SigstoreConfig struct { TrustedRoot string Logger *io.Handler @@ -37,7 +42,7 @@ type SigstoreConfig struct { } type SigstoreVerifier interface { - Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) + Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) *SigstoreResults } type LiveSigstoreVerifier struct { @@ -198,9 +203,9 @@ func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verif }, nil } -func (v *LiveSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) ([]*AttestationProcessingResult, error) { +func (v *LiveSigstoreVerifier) Verify(attestations []*api.Attestation, policy verify.PolicyBuilder) *SigstoreResults { if len(attestations) == 0 { - return nil, ErrNoAttestationsVerified + return &SigstoreResults{Error: ErrNoAttestationsVerified} } results := make([]*AttestationProcessingResult, 0) @@ -219,10 +224,10 @@ func (v *LiveSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve } if len(results) == 0 { - return nil, lastError + return &SigstoreResults{Error: lastError} } - return results, nil + return &SigstoreResults{VerifyResults: results} } func newCustomVerifier(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerifier, error) { diff --git a/pkg/cmd/attestation/verification/sigstore_integration_test.go b/pkg/cmd/attestation/verification/sigstore_integration_test.go index e14b472b0..84dd1695a 100644 --- a/pkg/cmd/attestation/verification/sigstore_integration_test.go +++ b/pkg/cmd/attestation/verification/sigstore_integration_test.go @@ -52,15 +52,15 @@ func TestLiveSigstoreVerifier(t *testing.T) { Logger: io.NewTestHandler(), }) - results, err := verifier.Verify(tc.attestations, publicGoodPolicy(t)) + res := verifier.Verify(tc.attestations, publicGoodPolicy(t)) if tc.expectErr { - require.Error(t, err, "test case: %s", tc.name) - require.ErrorContains(t, err, tc.errContains, "test case: %s", tc.name) - require.Nil(t, results, "test case: %s", tc.name) + require.Error(t, res.Error, "test case: %s", tc.name) + require.ErrorContains(t, res.Error, tc.errContains, "test case: %s", tc.name) + require.Nil(t, res.VerifyResults, "test case: %s", tc.name) } else { - require.Equal(t, len(tc.attestations), len(results), "test case: %s", tc.name) - require.NoError(t, err, "test case: %s", tc.name) + require.Equal(t, len(tc.attestations), len(res.VerifyResults), "test case: %s", tc.name) + require.NoError(t, res.Error, "test case: %s", tc.name) } } @@ -74,10 +74,10 @@ func TestLiveSigstoreVerifier(t *testing.T) { attestations = append(attestations, invalidBundle[0]) require.Len(t, attestations, 3) - results, err := verifier.Verify(attestations, publicGoodPolicy(t)) + res := verifier.Verify(attestations, publicGoodPolicy(t)) - require.Len(t, results, 2) - require.NoError(t, err) + require.Len(t, res.VerifyResults, 2) + require.NoError(t, res.Error) }) t.Run("fail with 0/2 verified attestations", func(t *testing.T) { @@ -90,9 +90,9 @@ func TestLiveSigstoreVerifier(t *testing.T) { attestations = append(attestations, invalidBundle[0]) require.Len(t, attestations, 2) - results, err := verifier.Verify(attestations, publicGoodPolicy(t)) - require.Nil(t, results) - require.Error(t, err) + res := verifier.Verify(attestations, publicGoodPolicy(t)) + require.Nil(t, res.VerifyResults) + require.Error(t, res.Error) }) t.Run("with GitHub Sigstore artifact", func(t *testing.T) { @@ -108,9 +108,9 @@ func TestLiveSigstoreVerifier(t *testing.T) { Logger: io.NewTestHandler(), }) - results, err := verifier.Verify(attestations, githubPolicy) - require.Len(t, results, 1) - require.NoError(t, err) + res := verifier.Verify(attestations, githubPolicy) + require.Len(t, res.VerifyResults, 1) + require.NoError(t, res.Error) }) t.Run("with custom trusted root", func(t *testing.T) { @@ -121,9 +121,9 @@ func TestLiveSigstoreVerifier(t *testing.T) { TrustedRoot: test.NormalizeRelativePath("../test/data/trusted_root.json"), }) - results, err := verifier.Verify(attestations, publicGoodPolicy(t)) - require.Len(t, results, 2) - require.NoError(t, err) + res := verifier.Verify(attestations, publicGoodPolicy(t)) + require.Len(t, res.VerifyResults, 2) + require.NoError(t, res.Error) }) } diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 75b9bce1b..206001f9b 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -264,14 +264,14 @@ func runVerify(opts *Options) error { opts.Logger.VerbosePrintf("Verifying attestations with predicate type: %s\n", opts.PredicateType) - sgResults, err := opts.SigstoreVerifier.Verify(attestations, policy) - if err != nil { + sigstoreRes := opts.SigstoreVerifier.Verify(attestations, policy) + if sigstoreRes.Error != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed")) - return err + return sigstoreRes.Error } // Verify extensions - if err := verification.VerifyCertExtensions(sgResults, opts.Tenant, opts.Owner, opts.Repo, opts.OIDCIssuer); err != nil { + if err := verification.VerifyCertExtensions(sigstoreRes.VerifyResults, opts.Tenant, opts.Owner, opts.Repo, opts.OIDCIssuer); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed")) return err } @@ -281,7 +281,7 @@ func runVerify(opts *Options) error { // If an exporter is provided with the --json flag, write the results to the terminal in JSON format if opts.exporter != nil { // print the results to the terminal as an array of JSON objects - if err = opts.exporter.Write(opts.Logger.IO, sgResults); err != nil { + if err = opts.exporter.Write(opts.Logger.IO, sigstoreRes.VerifyResults); err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to write JSON output")) return err } @@ -291,7 +291,7 @@ func runVerify(opts *Options) error { opts.Logger.Printf("%s was attested by:\n", artifact.DigestWithAlg()) // Otherwise print the results to the terminal in a table - tableContent, err := buildTableVerifyContent(opts.Tenant, sgResults) + tableContent, err := buildTableVerifyContent(opts.Tenant, sigstoreRes.VerifyResults) if err != nil { opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse results")) return err