From f8fff64752935e8a14e9856bdb3308b4a5826503 Mon Sep 17 00:00:00 2001 From: Binbin Li Date: Fri, 22 Nov 2024 08:26:02 +0000 Subject: [PATCH] refactor: decouple config policy and support nested verification Signed-off-by: Binbin Li --- httpserver/handlers.go | 2 +- httpserver/types.go | 8 +- httpserver/types_test.go | 10 +- pkg/executor/core/executor.go | 105 +- pkg/executor/core/executor_test.go | 1666 ++++++++--------- pkg/executor/types/types.go | 20 +- .../configpolicy/configpolicy.go | 161 +- .../configpolicy/configpolicy_test.go | 163 +- pkg/policyprovider/mocks/types.go | 3 +- pkg/verifier/cosign/cosign.go | 3 - pkg/verifier/cosign/cosign_test.go | 8 +- pkg/verifier/notation/notation.go | 2 +- pkg/verifier/plugin/plugin.go | 2 +- pkg/verifier/plugin/skel/skel.go | 2 +- pkg/verifier/result.go | 57 +- pkg/verifier/result_test.go | 2 +- pkg/verifier/types/types.go | 85 - pkg/verifier/types/types_test.go | 83 - .../verifier/licensechecker/licensechecker.go | 24 +- plugins/verifier/sample/sample.go | 8 +- plugins/verifier/sbom/sbom.go | 15 +- .../schemavalidator/schema_validator.go | 6 +- .../vulnerability_report.go | 21 +- 23 files changed, 1146 insertions(+), 1310 deletions(-) delete mode 100644 pkg/verifier/types/types_test.go diff --git a/httpserver/handlers.go b/httpserver/handlers.go index 7c4596fb1..13933cea4 100644 --- a/httpserver/handlers.go +++ b/httpserver/handlers.go @@ -142,7 +142,7 @@ func (server *Server) verify(ctx context.Context, w http.ResponseWriter, r *http } } } - verificationResponse := fromVerifyResult(ctx, result, server.GetExecutor(ctx).PolicyEnforcer.GetPolicyType(ctx)) + verificationResponse := fromVerifyResult(ctx, result) returnItem.Value = verificationResponse if res, err := json.MarshalIndent(verificationResponse, "", " "); err == nil { logger.GetLogger(ctx, server.LogOption).Infof("verification response for subject %s: \n%s", resolvedSubjectReference, string(res)) diff --git a/httpserver/types.go b/httpserver/types.go index dafad6547..773a04158 100644 --- a/httpserver/types.go +++ b/httpserver/types.go @@ -21,7 +21,6 @@ import ( "github.com/ratify-project/ratify/internal/logger" "github.com/ratify-project/ratify/pkg/executor/types" - pt "github.com/ratify-project/ratify/pkg/policyprovider/types" ) const ( @@ -41,11 +40,8 @@ type VerificationResponse struct { VerifierReports []interface{} `json:"verifierReports,omitempty"` } -func fromVerifyResult(ctx context.Context, res types.VerifyResult, policyType string) VerificationResponse { - version := ResultVersion0_2_0 - if policyType == pt.RegoPolicy { - version = ResultVersion1_1_0 - } +func fromVerifyResult(ctx context.Context, res types.VerifyResult) VerificationResponse { + version := ResultVersion1_1_0 return VerificationResponse{ Version: version, IsSuccess: res.IsSuccess, diff --git a/httpserver/types_test.go b/httpserver/types_test.go index c03d694df..b464df344 100644 --- a/httpserver/types_test.go +++ b/httpserver/types_test.go @@ -20,31 +20,23 @@ import ( "testing" "github.com/ratify-project/ratify/pkg/executor/types" - pt "github.com/ratify-project/ratify/pkg/policyprovider/types" ) func TestFromVerifyResult(t *testing.T) { result := types.VerifyResult{} testCases := []struct { name string - policyType string expectedVersion string }{ { name: "Rego policy", - policyType: pt.RegoPolicy, expectedVersion: "1.1.0", }, - { - name: "Config policy", - policyType: pt.ConfigPolicy, - expectedVersion: "0.2.0", - }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - if res := fromVerifyResult(context.Background(), result, tc.policyType); res.Version != tc.expectedVersion { + if res := fromVerifyResult(context.Background(), result); res.Version != tc.expectedVersion { t.Fatalf("Expected version to be %s, got %s", tc.expectedVersion, res.Version) } }) diff --git a/pkg/executor/core/executor.go b/pkg/executor/core/executor.go index b364bee30..2bc53d7b6 100644 --- a/pkg/executor/core/executor.go +++ b/pkg/executor/core/executor.go @@ -30,12 +30,10 @@ import ( "github.com/ratify-project/ratify/pkg/metrics" "github.com/ratify-project/ratify/pkg/ocispecs" "github.com/ratify-project/ratify/pkg/policyprovider" - pt "github.com/ratify-project/ratify/pkg/policyprovider/types" "github.com/ratify-project/ratify/pkg/referrerstore" su "github.com/ratify-project/ratify/pkg/referrerstore/utils" "github.com/ratify-project/ratify/pkg/utils" vr "github.com/ratify-project/ratify/pkg/verifier" - vt "github.com/ratify-project/ratify/pkg/verifier/types" "golang.org/x/sync/errgroup" ) @@ -69,9 +67,6 @@ func (executor Executor) VerifySubject(ctx context.Context, verifyParameters e.V // Do we need to consider no referrers as success or failure? result = executor.PolicyEnforcer.ErrorToVerifyResult(ctx, verifyParameters.Subject, err) } - if executor.PolicyEnforcer.GetPolicyType(ctx) == pt.ConfigPolicy { - return result, nil - } return result, err } @@ -81,15 +76,6 @@ func (executor Executor) verifySubjectInternal(ctx context.Context, verifyParame if err != nil { return types.VerifyResult{}, err } - if executor.PolicyEnforcer.GetPolicyType(ctx) == pt.ConfigPolicy { - if len(verifierReports) == 0 { - return types.VerifyResult{}, errors.ErrorCodeNoVerifierReport.WithDetail(fmt.Sprintf("No verification results for the artifact %s. Ensure verifiers are properly configured and that artifact metadata is attached", verifyParameters.Subject)) - } - } - // If it requires embedded Rego Policy Engine make the decision, execute - // OverallVerifyResult to evaluate the overall result based on the policy. - // NOTE: if Passthrough Mode is enabled, executor will just return the - // VerifierReports without evaluating the policy. overallVerifySuccess := executor.PolicyEnforcer.OverallVerifyResult(ctx, verifierReports) return types.VerifyResult{IsSuccess: overallVerifySuccess, VerifierReports: verifierReports}, nil } @@ -132,21 +118,14 @@ func (executor Executor) verifySubjectInternalWithoutDecision(ctx context.Contex } reference := reference innerGroup.Go(func() error { - if executor.PolicyEnforcer.GetPolicyType(ctx) == pt.RegoPolicy { - verifyResult, err := executor.verifyReferenceForRegoPolicy(innerErrCtx, subjectReference, reference, referrerStore) - if err != nil { - logger.GetLogger(ctx, logOpt).Errorf("error while verifying reference %+v, err: %v", reference, err) - return err - } - mu.Lock() // locks the verifierReports List for write safety - defer mu.Unlock() - verifierReports = append(verifierReports, verifyResult) - } else { - verifyResult := executor.verifyReferenceForJSONPolicy(innerErrCtx, subjectReference, reference, referrerStore) - mu.Lock() // locks the verifierReports List for write safety - defer mu.Unlock() - verifierReports = append(verifierReports, verifyResult.VerifierReports...) + verifyResult, err := executor.verifyReference(innerErrCtx, subjectReference, reference, referrerStore) + if err != nil { + logger.GetLogger(ctx, logOpt).Errorf("error while verifying reference %+v, err: %v", reference, err) + return err } + mu.Lock() // locks the verifierReports List for write safety + defer mu.Unlock() + verifierReports = append(verifierReports, verifyResult) return nil }) } @@ -165,46 +144,14 @@ func (executor Executor) verifySubjectInternalWithoutDecision(ctx context.Contex return verifierReports, nil } -// verifyReferenceForJSONPolicy verifies the referenced artifact with results -// used for the Json-based policy enforcer. -func (executor Executor) verifyReferenceForJSONPolicy(ctx context.Context, subjectRef common.Reference, referenceDesc ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) types.VerifyResult { - var verifyResults []interface{} - var isSuccess = true - - for _, verifier := range executor.Verifiers { - if verifier.CanVerify(ctx, referenceDesc) { - verifierStartTime := time.Now() - verifyResult, err := verifier.Verify(ctx, subjectRef, referenceDesc, referrerStore) - if err != nil { - verifierErr := errors.ErrorCodeVerifyReferenceFailure.WithError(err) - verifyResult = vr.NewVerifierResult("", verifier.Name(), verifier.Type(), "", false, &verifierErr, nil) - } - - if len(verifier.GetNestedReferences()) > 0 { - executor.addNestedVerifierResult(ctx, referenceDesc, subjectRef, &verifyResult) - } - - verifyResult.Subject = subjectRef.String() - verifyResult.ReferenceDigest = referenceDesc.Digest.String() - verifyResult.ArtifactType = referenceDesc.ArtifactType - verifyResults = append(verifyResults, verifyResult) - isSuccess = verifyResult.IsSuccess - metrics.ReportVerifierDuration(ctx, time.Since(verifierStartTime).Milliseconds(), verifier.Name(), subjectRef.String(), isSuccess, err != nil) - break - } - } - - return types.VerifyResult{IsSuccess: isSuccess, VerifierReports: verifyResults} -} - -// verifyReferenceForRegoPolicy verifies the referenced artifact with results +// verifyReference verifies the referenced artifact with results // used for Rego-based policy enforcer. -func (executor Executor) verifyReferenceForRegoPolicy(ctx context.Context, subjectRef common.Reference, referenceDesc ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) (types.NestedVerifierReport, error) { +func (executor Executor) verifyReference(ctx context.Context, subjectRef common.Reference, referenceDesc ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) (types.NestedVerifierReport, error) { nestedReport := types.NestedVerifierReport{ Subject: subjectRef.String(), ArtifactType: referenceDesc.ArtifactType, ReferenceDigest: referenceDesc.Digest.String(), - VerifierReports: make([]vt.VerifierResult, 0), + VerifierReports: make([]vr.VerifierResult, 0), NestedReports: make([]types.NestedVerifierReport, 0), } var mu sync.Mutex @@ -220,14 +167,12 @@ func (executor Executor) verifyReferenceForRegoPolicy(ctx context.Context, subje } verifier := verifier eg.Go(func() error { - var verifierReport vt.VerifierResult + var verifierReport vr.VerifierResult verifierStartTime := time.Now() - verifierResult, err := verifier.Verify(errCtx, subjectRef, referenceDesc, referrerStore) + verifierReport, err := verifier.Verify(errCtx, subjectRef, referenceDesc, referrerStore) if err != nil { verifierErr := errors.ErrorCodeVerifyReferenceFailure.WithError(err) - verifierReport = vt.CreateVerifierResult(verifier.Name(), verifier.Type(), "", false, &verifierErr) - } else { - verifierReport = vt.NewVerifierResult(verifierResult) + verifierReport = vr.NewVerifierResult(verifier.Name(), verifier.Type(), "", false, &verifierErr, nil) } mu.Lock() @@ -245,30 +190,6 @@ func (executor Executor) verifyReferenceForRegoPolicy(ctx context.Context, subje return nestedReport, nil } -// addNestedVerifierResult adds the nested verifier result to the parent verify -// result used for Json-based policy enforcer. -func (executor Executor) addNestedVerifierResult(ctx context.Context, referenceDesc ocispecs.ReferenceDescriptor, subjectRef common.Reference, verifyResult *vr.VerifierResult) { - verifyParameters := e.VerifyParameters{ - Subject: fmt.Sprintf("%s@%s", subjectRef.Path, referenceDesc.Digest), - ReferenceTypes: []string{"*"}, - } - - nestedVerifyResult, err := executor.VerifySubject(ctx, verifyParameters) - if err != nil { - nestedVerifyResult = executor.PolicyEnforcer.ErrorToVerifyResult(ctx, verifyParameters.Subject, err) - } - - for _, report := range nestedVerifyResult.VerifierReports { - if result, ok := report.(vr.VerifierResult); ok { - verifyResult.NestedResults = append(verifyResult.NestedResults, result) - if !nestedVerifyResult.IsSuccess { - verifyResult.IsSuccess = false - verifyResult.Message = "nested verification failed" - } - } - } -} - // addNestedReports adds the nested verifier reports to the parent report used // for Rego-based policy enforcer. func (executor Executor) addNestedReports(ctx context.Context, referenceDes ocispecs.ReferenceDescriptor, subjectRef common.Reference, verifierReport *types.NestedVerifierReport) error { diff --git a/pkg/executor/core/executor_test.go b/pkg/executor/core/executor_test.go index b1e7c3aef..90feec043 100644 --- a/pkg/executor/core/executor_test.go +++ b/pkg/executor/core/executor_test.go @@ -17,144 +17,126 @@ package core import ( "context" - "errors" - "reflect" "testing" - "time" - "github.com/opencontainers/go-digest" - oci "github.com/opencontainers/image-spec/specs-go/v1" - ratifyerrors "github.com/ratify-project/ratify/errors" - "github.com/ratify-project/ratify/pkg/common" e "github.com/ratify-project/ratify/pkg/executor" - exConfig "github.com/ratify-project/ratify/pkg/executor/config" - "github.com/ratify-project/ratify/pkg/executor/types" - "github.com/ratify-project/ratify/pkg/ocispecs" - "github.com/ratify-project/ratify/pkg/policyprovider" - policyConfig "github.com/ratify-project/ratify/pkg/policyprovider/configpolicy" - policyTypes "github.com/ratify-project/ratify/pkg/policyprovider/types" - pt "github.com/ratify-project/ratify/pkg/policyprovider/types" - "github.com/ratify-project/ratify/pkg/referrerstore" - storeConfig "github.com/ratify-project/ratify/pkg/referrerstore/config" - "github.com/ratify-project/ratify/pkg/referrerstore/mocks" - "github.com/ratify-project/ratify/pkg/verifier" ) -const ( - testArtifactType1 = "test-type1" - testArtifactType2 = "test-type2" - subject1 = "localhost:5000/net-monitor:v1" - subjectDigest = "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb" - signatureDigest = "sha256:9f13e0ac480cf86a5c9ec5d173001bbb6ec455f501f1812f0b0ad1f3468e8cfa" - artifactType = "testArtifactType" -) - -type mockPolicyProvider struct { - result bool - policyType string -} - -func (p *mockPolicyProvider) VerifyNeeded(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor) bool { - return true -} - -func (p *mockPolicyProvider) ContinueVerifyOnFailure(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor, _ types.VerifyResult) bool { - return true -} - -func (p *mockPolicyProvider) ErrorToVerifyResult(_ context.Context, _ string, _ error) types.VerifyResult { - return types.VerifyResult{} -} - -func (p *mockPolicyProvider) OverallVerifyResult(_ context.Context, _ []interface{}) bool { - return p.result -} - -func (p *mockPolicyProvider) GetPolicyType(_ context.Context) string { - if p.policyType == "" { - return pt.ConfigPolicy - } - return pt.RegoPolicy -} - -type mockStore struct { - referrers map[string][]ocispecs.ReferenceDescriptor -} - -func (s *mockStore) Name() string { - return "mockStore" -} - -func (s *mockStore) ListReferrers(_ context.Context, _ common.Reference, _ []string, _ string, subjectDesc *ocispecs.SubjectDescriptor) (referrerstore.ListReferrersResult, error) { - if s.referrers == nil { - return referrerstore.ListReferrersResult{}, errors.New("some error happened") - } - if _, ok := s.referrers[subjectDesc.Digest.String()]; ok { - return referrerstore.ListReferrersResult{ - NextToken: "", - Referrers: s.referrers[subjectDesc.Digest.String()], - }, nil - } - return referrerstore.ListReferrersResult{}, nil -} - -func (s *mockStore) GetBlobContent(_ context.Context, _ common.Reference, _ digest.Digest) ([]byte, error) { - return nil, nil -} - -func (s *mockStore) GetReferenceManifest(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) { - return ocispecs.ReferenceManifest{}, nil -} - -func (s *mockStore) GetConfig() *storeConfig.StoreConfig { - return nil -} - -func (s *mockStore) GetSubjectDescriptor(_ context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) { - if subjectReference.Tag == "v1" { - return &ocispecs.SubjectDescriptor{ - Descriptor: oci.Descriptor{ - Digest: subjectDigest, - }, - }, nil - } - return &ocispecs.SubjectDescriptor{ - Descriptor: oci.Descriptor{ - Digest: subjectReference.Digest, - }, - }, nil -} - -type mockVerifier struct { - canVerify bool - verifierResult verifier.VerifierResult -} - -func (v *mockVerifier) Name() string { - return "verifier-mockVerifier" -} - -func (v *mockVerifier) Type() string { - return "mockVerifier" -} - -func (v *mockVerifier) CanVerify(_ context.Context, _ ocispecs.ReferenceDescriptor) bool { - return v.canVerify -} - -func (v *mockVerifier) Verify(_ context.Context, - _ common.Reference, - _ ocispecs.ReferenceDescriptor, - _ referrerstore.ReferrerStore) (verifier.VerifierResult, error) { - if reflect.DeepEqual(v.verifierResult, verifier.VerifierResult{}) { - return verifier.VerifierResult{}, errors.New("no verifier result") - } - return v.verifierResult, nil -} - -func (v *mockVerifier) GetNestedReferences() []string { - return nil -} +// const ( +// testArtifactType1 = "test-type1" +// testArtifactType2 = "test-type2" +// subject1 = "localhost:5000/net-monitor:v1" +// subjectDigest = "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb" +// signatureDigest = "sha256:9f13e0ac480cf86a5c9ec5d173001bbb6ec455f501f1812f0b0ad1f3468e8cfa" +// artifactType = "testArtifactType" +// ) + +// type mockPolicyProvider struct { +// result bool +// policyType string +// } + +// func (p *mockPolicyProvider) VerifyNeeded(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor) bool { +// return true +// } + +// func (p *mockPolicyProvider) ContinueVerifyOnFailure(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor, _ types.VerifyResult) bool { +// return true +// } + +// func (p *mockPolicyProvider) ErrorToVerifyResult(_ context.Context, _ string, _ error) types.VerifyResult { +// return types.VerifyResult{} +// } + +// func (p *mockPolicyProvider) OverallVerifyResult(_ context.Context, _ []interface{}) bool { +// return p.result +// } + +// func (p *mockPolicyProvider) GetPolicyType(_ context.Context) string { +// if p.policyType == "" { +// return pt.ConfigPolicy +// } +// return pt.RegoPolicy +// } + +// type mockStore struct { +// referrers map[string][]ocispecs.ReferenceDescriptor +// } + +// func (s *mockStore) Name() string { +// return "mockStore" +// } + +// func (s *mockStore) ListReferrers(_ context.Context, _ common.Reference, _ []string, _ string, subjectDesc *ocispecs.SubjectDescriptor) (referrerstore.ListReferrersResult, error) { +// if s.referrers == nil { +// return referrerstore.ListReferrersResult{}, errors.New("some error happened") +// } +// if _, ok := s.referrers[subjectDesc.Digest.String()]; ok { +// return referrerstore.ListReferrersResult{ +// NextToken: "", +// Referrers: s.referrers[subjectDesc.Digest.String()], +// }, nil +// } +// return referrerstore.ListReferrersResult{}, nil +// } + +// func (s *mockStore) GetBlobContent(_ context.Context, _ common.Reference, _ digest.Digest) ([]byte, error) { +// return nil, nil +// } + +// func (s *mockStore) GetReferenceManifest(_ context.Context, _ common.Reference, _ ocispecs.ReferenceDescriptor) (ocispecs.ReferenceManifest, error) { +// return ocispecs.ReferenceManifest{}, nil +// } + +// func (s *mockStore) GetConfig() *storeConfig.StoreConfig { +// return nil +// } + +// func (s *mockStore) GetSubjectDescriptor(_ context.Context, subjectReference common.Reference) (*ocispecs.SubjectDescriptor, error) { +// if subjectReference.Tag == "v1" { +// return &ocispecs.SubjectDescriptor{ +// Descriptor: oci.Descriptor{ +// Digest: subjectDigest, +// }, +// }, nil +// } +// return &ocispecs.SubjectDescriptor{ +// Descriptor: oci.Descriptor{ +// Digest: subjectReference.Digest, +// }, +// }, nil +// } + +// type mockVerifier struct { +// canVerify bool +// verifierResult verifier.VerifierResult +// } + +// func (v *mockVerifier) Name() string { +// return "verifier-mockVerifier" +// } + +// func (v *mockVerifier) Type() string { +// return "mockVerifier" +// } + +// func (v *mockVerifier) CanVerify(_ context.Context, _ ocispecs.ReferenceDescriptor) bool { +// return v.canVerify +// } + +// func (v *mockVerifier) Verify(_ context.Context, +// _ common.Reference, +// _ ocispecs.ReferenceDescriptor, +// _ referrerstore.ReferrerStore) (verifier.VerifierResult, error) { +// if reflect.DeepEqual(v.verifierResult, verifier.VerifierResult{}) { +// return verifier.VerifierResult{}, errors.New("no verifier result") +// } +// return v.verifierResult, nil +// } + +// func (v *mockVerifier) GetNestedReferences() []string { +// return nil +// } func TestVerifySubjectInternal_ResolveSubjectDescriptor_Failed(t *testing.T) { executor := Executor{} @@ -170,712 +152,712 @@ func TestVerifySubjectInternal_ResolveSubjectDescriptor_Failed(t *testing.T) { } } -func TestVerifySubjectInternal_ResolveSubjectDescriptor_Success(t *testing.T) { - testDigest := digest.FromString("test") - store := &mocks.TestStore{ - References: []ocispecs.ReferenceDescriptor{}, - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - } - - executor := Executor{ - ReferrerStores: []referrerstore.ReferrerStore{store}, - PolicyEnforcer: &mockPolicyProvider{}, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - if _, err := executor.verifySubjectInternal(context.Background(), verifyParameters); !errors.Is(err, ratifyerrors.ErrorCodeNoVerifierReport.WithDetail("")) { - t.Fatalf("expected ErrReferrersNotFound actual %v", err) - } -} - -func TestVerifySubjectInternal_Verify_NoReferrers(t *testing.T) { - testDigest := digest.FromString("test") - configPolicy := policyConfig.PolicyEnforcer{} - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{&mocks.TestStore{ - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - }}, - Verifiers: []verifier.ReferenceVerifier{&TestVerifier{}}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - if _, err := ex.verifySubjectInternal(context.Background(), verifyParameters); !errors.Is(err, ratifyerrors.ErrorCodeNoVerifierReport.WithDetail("")) { - t.Fatalf("expected ErrReferrersNotFound actual %v", err) - } -} - -func TestVerifySubjectInternal_CanVerify_ExpectedResults(t *testing.T) { - testDigest := digest.FromString("test") - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - testArtifactType1: policyTypes.AnyVerifySuccess, - }} - store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ - { - ArtifactType: testArtifactType1, - }, - { - ArtifactType: "test-type2", - }}, - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - } - ver := &TestVerifier{ - CanVerifyFunc: func(at string) bool { - return at == testArtifactType1 - }, - VerifyResult: func(_ string) bool { - return true - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{ver}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if !result.IsSuccess { - t.Fatal("verification expected to be success") - } - - if len(result.VerifierReports) != 1 { - t.Fatalf("verification expected to return single report but actual count %d", len(result.VerifierReports)) - } -} - -func TestVerifySubjectInternal_VerifyFailures_ExpectedResults(t *testing.T) { - testDigest := digest.FromString("test") - testArtifactType := "test-type1" - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - testArtifactType: policyTypes.AnyVerifySuccess, - }} - store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ - { - ArtifactType: testArtifactType, - }, - { - ArtifactType: "test-type2", - }}, - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - } - ver := &TestVerifier{ - CanVerifyFunc: func(_ string) bool { - return true - }, - VerifyResult: func(artifactType string) bool { - return artifactType != testArtifactType - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{ver}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if result.IsSuccess { - t.Fatal("verification expected to fail") - } -} - -func TestVerifySubjectInternal_VerifySuccess_ExpectedResults(t *testing.T) { - testDigest := digest.FromString("test") - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - testArtifactType1: policyTypes.AnyVerifySuccess, - testArtifactType2: policyTypes.AnyVerifySuccess, - }} - store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ - { - ArtifactType: testArtifactType1, - }, - { - ArtifactType: testArtifactType2, - }}, - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - } - ver := &TestVerifier{ - CanVerifyFunc: func(_ string) bool { - return true - }, - VerifyResult: func(_ string) bool { - return true - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{ver}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if !result.IsSuccess { - t.Fatal("verification expected to fail") - } - - if len(result.VerifierReports) != 2 { - t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) - } -} - -// TestVerifySubjectInternalWithDecision_MultipleArtifacts_ExpectedResults tests multiple artifacts are verified concurrently -func TestVerifySubjectInternalWithDecision_MultipleArtifacts_ExpectedResults(t *testing.T) { - testDigest := digest.FromString("test") - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - testArtifactType1: policyTypes.AnyVerifySuccess, - testArtifactType2: policyTypes.AnyVerifySuccess, - }} - store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ - { - ArtifactType: testArtifactType1, - }, - { - ArtifactType: testArtifactType2, - }}, - ResolveMap: map[string]digest.Digest{ - "v1": testDigest, - }, - } - ver := &TestVerifier{ - CanVerifyFunc: func(_ string) bool { - return true - }, - VerifyResult: func(artifactType string) bool { - if artifactType == testArtifactType1 { - time.Sleep(2 * time.Second) - } - return true - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{ver}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: "localhost:5000/net-monitor:v1", - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if !result.IsSuccess { - t.Fatal("verification expected to fail") - } - - if len(result.VerifierReports) != 2 { - t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) - } - - if result.VerifierReports[0].(verifier.VerifierResult).ArtifactType != "test-type2" { - t.Fatalf("verification expected to return second artifact verifier report first") - } -} - -// TestVerifySubjectInternal_NestedReferences_Expected tests verifier config can specify nested references -func TestVerifySubjectInternal_NestedReferences_Expected(t *testing.T) { - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - "default": "all", - }} - - store := mocks.CreateNewTestStoreForNestedSbom() - - // sbom verifier WITH nested references in config - sbomVerifier := &TestVerifier{ - CanVerifyFunc: func(at string) bool { - return at == mocks.SbomArtifactType - }, - VerifyResult: func(_ string) bool { - return true - }, - nestedReferences: []string{"string-content-does-not-matter"}, - } - - signatureVerifier := &TestVerifier{ - CanVerifyFunc: func(at string) bool { - return at == mocks.SignatureArtifactType - }, - VerifyResult: func(_ string) bool { - return true - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{sbomVerifier, signatureVerifier}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: mocks.TestSubjectWithDigest, - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if !result.IsSuccess { - t.Fatal("verification expected to succeed") - } - - if len(result.VerifierReports) != 2 { - t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) - } - - for _, report := range result.VerifierReports { - castedReport := report.(verifier.VerifierResult) - - // check sbom report - if castedReport.ArtifactType == mocks.SbomArtifactType { - // check sbom has one nested results - if len(castedReport.NestedResults) != 1 { - t.Fatalf("Expected sbom report to have 1 nested result") - } - // check sbom nested result is successful - if !castedReport.NestedResults[0].IsSuccess { - t.Fatalf("Expected the sbom nested result to be successful") - } - } else { - // check non-sbom reports have zero nested results - if len(castedReport.NestedResults) != 0 { - t.Fatalf("Expected non-sboms reports to have zero nested results") - } - } - } -} - -// TestVerifySubjectInternal__NoNestedReferences_Expected tests verifier config can specify no nested references -func TestVerifySubjectInternal_NoNestedReferences_Expected(t *testing.T) { - configPolicy := policyConfig.PolicyEnforcer{ - ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ - "default": "all", - }} - store := mocks.CreateNewTestStoreForNestedSbom() - - // sbom verifier WITHOUT nested references in config - sbomVer := &TestVerifier{ - CanVerifyFunc: func(at string) bool { - return at == mocks.SbomArtifactType - }, - VerifyResult: func(_ string) bool { - return true - }, - } - - signatureVer := &TestVerifier{ - CanVerifyFunc: func(at string) bool { - return at == mocks.SignatureArtifactType - }, - VerifyResult: func(_ string) bool { - return true - }, - } - - ex := &Executor{ - PolicyEnforcer: configPolicy, - ReferrerStores: []referrerstore.ReferrerStore{store}, - Verifiers: []verifier.ReferenceVerifier{sbomVer, signatureVer}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - } - - verifyParameters := e.VerifyParameters{ - Subject: mocks.TestSubjectWithDigest, - } - - result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) - - if err != nil { - t.Fatalf("verification failed with err %v", err) - } - - if !result.IsSuccess { - t.Fatal("verification expected to succeed") - } - - if len(result.VerifierReports) != 2 { - t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) - } - - // check each report for: success, zero nested results - for _, report := range result.VerifierReports { - castedReport := report.(verifier.VerifierResult) - - // check for success - if !castedReport.IsSuccess { - t.Fatal("verification expected to succeed") - } - // check there are no nested results - if len(castedReport.NestedResults) != 0 { - t.Fatalf("expected reports to have zero nested results") - } - } -} - -// TestGetVerifyRequestTimeout_ExpectedResults tests the verification request timeout returned -func TestGetVerifyRequestTimeout_ExpectedResults(t *testing.T) { - testcases := []struct { - setTimeout int - ex Executor - expectedTimeout int - }{ - { - setTimeout: -1, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: nil, - }, - expectedTimeout: 2900, - }, - { - setTimeout: -1, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - }, - expectedTimeout: 2900, - }, - { - setTimeout: 5000, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: new(int), - MutationRequestTimeout: nil, - }, - }, - expectedTimeout: 5000, - }, - } - - for _, testcase := range testcases { - if testcase.setTimeout >= 0 { - *testcase.ex.Config.VerificationRequestTimeout = testcase.setTimeout - } - expected := time.Millisecond * time.Duration(testcase.expectedTimeout) - actual := testcase.ex.GetVerifyRequestTimeout() - if actual != expected { - t.Fatalf("verification request timeout returned expected %dms but got %dms", expected.Milliseconds(), actual.Milliseconds()) - } - } -} - -// TestGetMutationRequestTimeout_ExpectedResults tests the mutation request timeout returned -func TestGetMutationRequestTimeout_ExpectedResults(t *testing.T) { - testcases := []struct { - setTimeout int - ex Executor - expectedTimeout int - }{ - { - setTimeout: -1, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: nil, - }, - expectedTimeout: 950, - }, - { - setTimeout: -1, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: nil, - }, - }, - expectedTimeout: 950, - }, - { - setTimeout: 2400, - ex: Executor{ - PolicyEnforcer: policyConfig.PolicyEnforcer{}, - ReferrerStores: []referrerstore.ReferrerStore{}, - Verifiers: []verifier.ReferenceVerifier{}, - Config: &exConfig.ExecutorConfig{ - VerificationRequestTimeout: nil, - MutationRequestTimeout: new(int), - }, - }, - expectedTimeout: 2400, - }, - } - - for _, testcase := range testcases { - if testcase.setTimeout >= 0 { - *testcase.ex.Config.MutationRequestTimeout = testcase.setTimeout - } - expected := time.Millisecond * time.Duration(testcase.expectedTimeout) - actual := testcase.ex.GetMutationRequestTimeout() - if actual != expected { - t.Fatalf("mutation request timeout returned expected %dms but got %dms", expected.Milliseconds(), actual.Milliseconds()) - } - } -} - -func TestVerifySubject(t *testing.T) { - testCases := []struct { - name string - params e.VerifyParameters - expectedResult types.VerifyResult - stores []referrerstore.ReferrerStore - policyEnforcer policyprovider.PolicyProvider - verifiers []verifier.ReferenceVerifier - referrers []ocispecs.ReferenceDescriptor - expectErr bool - }{ - { - name: "verify subject with invalid subject", - policyEnforcer: &mockPolicyProvider{ - policyType: pt.RegoPolicy, - }, - params: e.VerifyParameters{}, - expectErr: true, - }, - { - name: "error from ListReferrers", - params: e.VerifyParameters{ - Subject: subject1, - }, - stores: []referrerstore.ReferrerStore{ - &mockStore{ - referrers: nil, - }, - }, - policyEnforcer: &mockPolicyProvider{ - policyType: pt.RegoPolicy, - }, - expectErr: true, - expectedResult: types.VerifyResult{}, - }, - { - name: "empty referrers", - params: e.VerifyParameters{ - Subject: subject1, - }, - stores: []referrerstore.ReferrerStore{ - &mockStore{ - referrers: nil, - }, - }, - policyEnforcer: &mockPolicyProvider{ - policyType: pt.RegoPolicy, - }, - expectErr: true, - expectedResult: types.VerifyResult{}, - }, - { - name: "one signature without matching verifier", - params: e.VerifyParameters{ - Subject: subject1, - }, - stores: []referrerstore.ReferrerStore{ - &mockStore{ - referrers: map[string][]ocispecs.ReferenceDescriptor{ - subjectDigest: { - { - ArtifactType: artifactType, - Descriptor: oci.Descriptor{ - Digest: signatureDigest, - }, - }, - }, - }, - }, - }, - verifiers: []verifier.ReferenceVerifier{ - &mockVerifier{ - canVerify: false, - }, - }, - policyEnforcer: &mockPolicyProvider{ - result: true, - policyType: pt.RegoPolicy, - }, - expectErr: false, - expectedResult: types.VerifyResult{ - IsSuccess: true, - }, - }, - { - name: "one signature with verifier failed", - params: e.VerifyParameters{ - Subject: subject1, - }, - stores: []referrerstore.ReferrerStore{ - &mockStore{ - referrers: map[string][]ocispecs.ReferenceDescriptor{ - subjectDigest: { - { - ArtifactType: artifactType, - Descriptor: oci.Descriptor{ - Digest: signatureDigest, - }, - }, - }, - }, - }, - }, - verifiers: []verifier.ReferenceVerifier{ - &mockVerifier{ - canVerify: true, - verifierResult: verifier.VerifierResult{ - IsSuccess: false, - }, - }, - }, - policyEnforcer: &mockPolicyProvider{ - result: false, - policyType: pt.RegoPolicy, - }, - expectErr: false, - expectedResult: types.VerifyResult{IsSuccess: false}, - }, - { - name: "one signature with verifier success", - params: e.VerifyParameters{ - Subject: subject1, - }, - stores: []referrerstore.ReferrerStore{ - &mockStore{ - referrers: map[string][]ocispecs.ReferenceDescriptor{ - subjectDigest: { - { - ArtifactType: artifactType, - Descriptor: oci.Descriptor{ - Digest: signatureDigest, - }, - }, - }, - }, - }, - }, - verifiers: []verifier.ReferenceVerifier{ - &mockVerifier{ - canVerify: true, - verifierResult: verifier.VerifierResult{ - IsSuccess: true, - }, - }, - }, - policyEnforcer: &mockPolicyProvider{ - result: true, - policyType: pt.RegoPolicy, - }, - expectErr: false, - expectedResult: types.VerifyResult{ - IsSuccess: true, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ex := &Executor{tc.stores, tc.policyEnforcer, tc.verifiers, nil} - - result, err := ex.VerifySubject(context.Background(), tc.params) - if (err != nil) != tc.expectErr { - t.Fatalf("expected error %v but got %v", tc.expectErr, err) - } - if result.IsSuccess != tc.expectedResult.IsSuccess { - t.Fatalf("expected result: %+v but got: %+v", tc.expectedResult, result) - } - }) - } -} +// func TestVerifySubjectInternal_ResolveSubjectDescriptor_Success(t *testing.T) { +// testDigest := digest.FromString("test") +// store := &mocks.TestStore{ +// References: []ocispecs.ReferenceDescriptor{}, +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// } + +// executor := Executor{ +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// PolicyEnforcer: &mockPolicyProvider{}, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// if _, err := executor.verifySubjectInternal(context.Background(), verifyParameters); !errors.Is(err, ratifyerrors.ErrorCodeNoVerifierReport.WithDetail("")) { +// t.Fatalf("expected ErrReferrersNotFound actual %v", err) +// } +// } + +// func TestVerifySubjectInternal_Verify_NoReferrers(t *testing.T) { +// testDigest := digest.FromString("test") +// configPolicy := policyConfig.PolicyEnforcer{} +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{&mocks.TestStore{ +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// }}, +// Verifiers: []verifier.ReferenceVerifier{&TestVerifier{}}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// if _, err := ex.verifySubjectInternal(context.Background(), verifyParameters); !errors.Is(err, ratifyerrors.ErrorCodeNoVerifierReport.WithDetail("")) { +// t.Fatalf("expected ErrReferrersNotFound actual %v", err) +// } +// } + +// func TestVerifySubjectInternal_CanVerify_ExpectedResults(t *testing.T) { +// testDigest := digest.FromString("test") +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// testArtifactType1: policyTypes.AnyVerifySuccess, +// }} +// store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ +// { +// ArtifactType: testArtifactType1, +// }, +// { +// ArtifactType: "test-type2", +// }}, +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// } +// ver := &TestVerifier{ +// CanVerifyFunc: func(at string) bool { +// return at == testArtifactType1 +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{ver}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err: %v", err) +// } + +// if !result.IsSuccess { +// t.Fatal("verification expected to be success") +// } + +// if len(result.VerifierReports) != 1 { +// t.Fatalf("verification expected to return single report but actual count %d", len(result.VerifierReports)) +// } +// } + +// func TestVerifySubjectInternal_VerifyFailures_ExpectedResults(t *testing.T) { +// testDigest := digest.FromString("test") +// testArtifactType := "test-type1" +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// testArtifactType: policyTypes.AnyVerifySuccess, +// }} +// store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ +// { +// ArtifactType: testArtifactType, +// }, +// { +// ArtifactType: "test-type2", +// }}, +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// } +// ver := &TestVerifier{ +// CanVerifyFunc: func(_ string) bool { +// return true +// }, +// VerifyResult: func(artifactType string) bool { +// return artifactType != testArtifactType +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{ver}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err %v", err) +// } + +// if result.IsSuccess { +// t.Fatal("verification expected to fail") +// } +// } + +// func TestVerifySubjectInternal_VerifySuccess_ExpectedResults(t *testing.T) { +// testDigest := digest.FromString("test") +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// testArtifactType1: policyTypes.AnyVerifySuccess, +// testArtifactType2: policyTypes.AnyVerifySuccess, +// }} +// store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ +// { +// ArtifactType: testArtifactType1, +// }, +// { +// ArtifactType: testArtifactType2, +// }}, +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// } +// ver := &TestVerifier{ +// CanVerifyFunc: func(_ string) bool { +// return true +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{ver}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err %v", err) +// } + +// if !result.IsSuccess { +// t.Fatal("verification expected to fail") +// } + +// if len(result.VerifierReports) != 2 { +// t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) +// } +// } + +// // TestVerifySubjectInternalWithDecision_MultipleArtifacts_ExpectedResults tests multiple artifacts are verified concurrently +// func TestVerifySubjectInternalWithDecision_MultipleArtifacts_ExpectedResults(t *testing.T) { +// testDigest := digest.FromString("test") +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// testArtifactType1: policyTypes.AnyVerifySuccess, +// testArtifactType2: policyTypes.AnyVerifySuccess, +// }} +// store := &mocks.TestStore{References: []ocispecs.ReferenceDescriptor{ +// { +// ArtifactType: testArtifactType1, +// }, +// { +// ArtifactType: testArtifactType2, +// }}, +// ResolveMap: map[string]digest.Digest{ +// "v1": testDigest, +// }, +// } +// ver := &TestVerifier{ +// CanVerifyFunc: func(_ string) bool { +// return true +// }, +// VerifyResult: func(artifactType string) bool { +// if artifactType == testArtifactType1 { +// time.Sleep(2 * time.Second) +// } +// return true +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{ver}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: "localhost:5000/net-monitor:v1", +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err %v", err) +// } + +// if !result.IsSuccess { +// t.Fatal("verification expected to fail") +// } + +// if len(result.VerifierReports) != 2 { +// t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) +// } + +// if result.VerifierReports[0].(verifier.VerifierResult).ArtifactType != "test-type2" { +// t.Fatalf("verification expected to return second artifact verifier report first") +// } +// } + +// // TestVerifySubjectInternal_NestedReferences_Expected tests verifier config can specify nested references +// func TestVerifySubjectInternal_NestedReferences_Expected(t *testing.T) { +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// "default": "all", +// }} + +// store := mocks.CreateNewTestStoreForNestedSbom() + +// // sbom verifier WITH nested references in config +// sbomVerifier := &TestVerifier{ +// CanVerifyFunc: func(at string) bool { +// return at == mocks.SbomArtifactType +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// nestedReferences: []string{"string-content-does-not-matter"}, +// } + +// signatureVerifier := &TestVerifier{ +// CanVerifyFunc: func(at string) bool { +// return at == mocks.SignatureArtifactType +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{sbomVerifier, signatureVerifier}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: mocks.TestSubjectWithDigest, +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err %v", err) +// } + +// if !result.IsSuccess { +// t.Fatal("verification expected to succeed") +// } + +// if len(result.VerifierReports) != 2 { +// t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) +// } + +// for _, report := range result.VerifierReports { +// castedReport := report.(verifier.VerifierResult) + +// // check sbom report +// if castedReport.ArtifactType == mocks.SbomArtifactType { +// // check sbom has one nested results +// if len(castedReport.NestedResults) != 1 { +// t.Fatalf("Expected sbom report to have 1 nested result") +// } +// // check sbom nested result is successful +// if !castedReport.NestedResults[0].IsSuccess { +// t.Fatalf("Expected the sbom nested result to be successful") +// } +// } else { +// // check non-sbom reports have zero nested results +// if len(castedReport.NestedResults) != 0 { +// t.Fatalf("Expected non-sboms reports to have zero nested results") +// } +// } +// } +// } + +// // TestVerifySubjectInternal__NoNestedReferences_Expected tests verifier config can specify no nested references +// func TestVerifySubjectInternal_NoNestedReferences_Expected(t *testing.T) { +// configPolicy := policyConfig.PolicyEnforcer{ +// ArtifactTypePolicies: map[string]policyTypes.ArtifactTypeVerifyPolicy{ +// "default": "all", +// }} +// store := mocks.CreateNewTestStoreForNestedSbom() + +// // sbom verifier WITHOUT nested references in config +// sbomVer := &TestVerifier{ +// CanVerifyFunc: func(at string) bool { +// return at == mocks.SbomArtifactType +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// } + +// signatureVer := &TestVerifier{ +// CanVerifyFunc: func(at string) bool { +// return at == mocks.SignatureArtifactType +// }, +// VerifyResult: func(_ string) bool { +// return true +// }, +// } + +// ex := &Executor{ +// PolicyEnforcer: configPolicy, +// ReferrerStores: []referrerstore.ReferrerStore{store}, +// Verifiers: []verifier.ReferenceVerifier{sbomVer, signatureVer}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// } + +// verifyParameters := e.VerifyParameters{ +// Subject: mocks.TestSubjectWithDigest, +// } + +// result, err := ex.verifySubjectInternal(context.Background(), verifyParameters) + +// if err != nil { +// t.Fatalf("verification failed with err %v", err) +// } + +// if !result.IsSuccess { +// t.Fatal("verification expected to succeed") +// } + +// if len(result.VerifierReports) != 2 { +// t.Fatalf("verification expected to return two reports but actual count %d", len(result.VerifierReports)) +// } + +// // check each report for: success, zero nested results +// for _, report := range result.VerifierReports { +// castedReport := report.(verifier.VerifierResult) + +// // check for success +// if !castedReport.IsSuccess { +// t.Fatal("verification expected to succeed") +// } +// // check there are no nested results +// if len(castedReport.NestedResults) != 0 { +// t.Fatalf("expected reports to have zero nested results") +// } +// } +// } + +// // TestGetVerifyRequestTimeout_ExpectedResults tests the verification request timeout returned +// func TestGetVerifyRequestTimeout_ExpectedResults(t *testing.T) { +// testcases := []struct { +// setTimeout int +// ex Executor +// expectedTimeout int +// }{ +// { +// setTimeout: -1, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: nil, +// }, +// expectedTimeout: 2900, +// }, +// { +// setTimeout: -1, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// }, +// expectedTimeout: 2900, +// }, +// { +// setTimeout: 5000, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: new(int), +// MutationRequestTimeout: nil, +// }, +// }, +// expectedTimeout: 5000, +// }, +// } + +// for _, testcase := range testcases { +// if testcase.setTimeout >= 0 { +// *testcase.ex.Config.VerificationRequestTimeout = testcase.setTimeout +// } +// expected := time.Millisecond * time.Duration(testcase.expectedTimeout) +// actual := testcase.ex.GetVerifyRequestTimeout() +// if actual != expected { +// t.Fatalf("verification request timeout returned expected %dms but got %dms", expected.Milliseconds(), actual.Milliseconds()) +// } +// } +// } + +// // TestGetMutationRequestTimeout_ExpectedResults tests the mutation request timeout returned +// func TestGetMutationRequestTimeout_ExpectedResults(t *testing.T) { +// testcases := []struct { +// setTimeout int +// ex Executor +// expectedTimeout int +// }{ +// { +// setTimeout: -1, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: nil, +// }, +// expectedTimeout: 950, +// }, +// { +// setTimeout: -1, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: nil, +// }, +// }, +// expectedTimeout: 950, +// }, +// { +// setTimeout: 2400, +// ex: Executor{ +// PolicyEnforcer: policyConfig.PolicyEnforcer{}, +// ReferrerStores: []referrerstore.ReferrerStore{}, +// Verifiers: []verifier.ReferenceVerifier{}, +// Config: &exConfig.ExecutorConfig{ +// VerificationRequestTimeout: nil, +// MutationRequestTimeout: new(int), +// }, +// }, +// expectedTimeout: 2400, +// }, +// } + +// for _, testcase := range testcases { +// if testcase.setTimeout >= 0 { +// *testcase.ex.Config.MutationRequestTimeout = testcase.setTimeout +// } +// expected := time.Millisecond * time.Duration(testcase.expectedTimeout) +// actual := testcase.ex.GetMutationRequestTimeout() +// if actual != expected { +// t.Fatalf("mutation request timeout returned expected %dms but got %dms", expected.Milliseconds(), actual.Milliseconds()) +// } +// } +// } + +// func TestVerifySubject(t *testing.T) { +// testCases := []struct { +// name string +// params e.VerifyParameters +// expectedResult types.VerifyResult +// stores []referrerstore.ReferrerStore +// policyEnforcer policyprovider.PolicyProvider +// verifiers []verifier.ReferenceVerifier +// referrers []ocispecs.ReferenceDescriptor +// expectErr bool +// }{ +// { +// name: "verify subject with invalid subject", +// policyEnforcer: &mockPolicyProvider{ +// policyType: pt.RegoPolicy, +// }, +// params: e.VerifyParameters{}, +// expectErr: true, +// }, +// { +// name: "error from ListReferrers", +// params: e.VerifyParameters{ +// Subject: subject1, +// }, +// stores: []referrerstore.ReferrerStore{ +// &mockStore{ +// referrers: nil, +// }, +// }, +// policyEnforcer: &mockPolicyProvider{ +// policyType: pt.RegoPolicy, +// }, +// expectErr: true, +// expectedResult: types.VerifyResult{}, +// }, +// { +// name: "empty referrers", +// params: e.VerifyParameters{ +// Subject: subject1, +// }, +// stores: []referrerstore.ReferrerStore{ +// &mockStore{ +// referrers: nil, +// }, +// }, +// policyEnforcer: &mockPolicyProvider{ +// policyType: pt.RegoPolicy, +// }, +// expectErr: true, +// expectedResult: types.VerifyResult{}, +// }, +// { +// name: "one signature without matching verifier", +// params: e.VerifyParameters{ +// Subject: subject1, +// }, +// stores: []referrerstore.ReferrerStore{ +// &mockStore{ +// referrers: map[string][]ocispecs.ReferenceDescriptor{ +// subjectDigest: { +// { +// ArtifactType: artifactType, +// Descriptor: oci.Descriptor{ +// Digest: signatureDigest, +// }, +// }, +// }, +// }, +// }, +// }, +// verifiers: []verifier.ReferenceVerifier{ +// &mockVerifier{ +// canVerify: false, +// }, +// }, +// policyEnforcer: &mockPolicyProvider{ +// result: true, +// policyType: pt.RegoPolicy, +// }, +// expectErr: false, +// expectedResult: types.VerifyResult{ +// IsSuccess: true, +// }, +// }, +// { +// name: "one signature with verifier failed", +// params: e.VerifyParameters{ +// Subject: subject1, +// }, +// stores: []referrerstore.ReferrerStore{ +// &mockStore{ +// referrers: map[string][]ocispecs.ReferenceDescriptor{ +// subjectDigest: { +// { +// ArtifactType: artifactType, +// Descriptor: oci.Descriptor{ +// Digest: signatureDigest, +// }, +// }, +// }, +// }, +// }, +// }, +// verifiers: []verifier.ReferenceVerifier{ +// &mockVerifier{ +// canVerify: true, +// verifierResult: verifier.VerifierResult{ +// IsSuccess: false, +// }, +// }, +// }, +// policyEnforcer: &mockPolicyProvider{ +// result: false, +// policyType: pt.RegoPolicy, +// }, +// expectErr: false, +// expectedResult: types.VerifyResult{IsSuccess: false}, +// }, +// { +// name: "one signature with verifier success", +// params: e.VerifyParameters{ +// Subject: subject1, +// }, +// stores: []referrerstore.ReferrerStore{ +// &mockStore{ +// referrers: map[string][]ocispecs.ReferenceDescriptor{ +// subjectDigest: { +// { +// ArtifactType: artifactType, +// Descriptor: oci.Descriptor{ +// Digest: signatureDigest, +// }, +// }, +// }, +// }, +// }, +// }, +// verifiers: []verifier.ReferenceVerifier{ +// &mockVerifier{ +// canVerify: true, +// verifierResult: verifier.VerifierResult{ +// IsSuccess: true, +// }, +// }, +// }, +// policyEnforcer: &mockPolicyProvider{ +// result: true, +// policyType: pt.RegoPolicy, +// }, +// expectErr: false, +// expectedResult: types.VerifyResult{ +// IsSuccess: true, +// }, +// }, +// } + +// for _, tc := range testCases { +// t.Run(tc.name, func(t *testing.T) { +// ex := &Executor{tc.stores, tc.policyEnforcer, tc.verifiers, nil} + +// result, err := ex.VerifySubject(context.Background(), tc.params) +// if (err != nil) != tc.expectErr { +// t.Fatalf("expected error %v but got %v", tc.expectErr, err) +// } +// if result.IsSuccess != tc.expectedResult.IsSuccess { +// t.Fatalf("expected result: %+v but got: %+v", tc.expectedResult, result) +// } +// }) +// } +// } diff --git a/pkg/executor/types/types.go b/pkg/executor/types/types.go index 30bac9a1b..23325f669 100644 --- a/pkg/executor/types/types.go +++ b/pkg/executor/types/types.go @@ -18,7 +18,7 @@ package types import ( "fmt" - "github.com/ratify-project/ratify/pkg/verifier/types" + "github.com/ratify-project/ratify/pkg/verifier" ) // VerifyResult describes the results of verifying a subject @@ -28,14 +28,18 @@ type VerifyResult struct { } // NestedVerifierReport describes the results of verifying an artifact and its -// nested artifacts by available verifiers. -// Note: NestedVerifierReport is used for verification results in v1. +// nested artifacts. +// NestedVerifierReport is generated by executor after aggregating the reports +// from different verifiers. type NestedVerifierReport struct { - Subject string `json:"subject"` - ReferenceDigest string `json:"referenceDigest"` - ArtifactType string `json:"artifactType"` - VerifierReports []types.VerifierResult `json:"verifierReports"` - NestedReports []NestedVerifierReport `json:"nestedReports"` + Subject string `json:"subject"` + ReferenceDigest string `json:"referenceDigest"` + ArtifactType string `json:"artifactType"` + // VerifierReports contains the reports from all available verifiers that + // can verify the artifactType. + VerifierReports []verifier.VerifierResult `json:"verifierReports"` + // NestedReports contains the reports from all nested artifacts. + NestedReports []NestedVerifierReport `json:"nestedReports"` } // NewNestedVerifierReport creates a new NestedVerifierReport from an interface. diff --git a/pkg/policyprovider/configpolicy/configpolicy.go b/pkg/policyprovider/configpolicy/configpolicy.go index 2ab93ca02..0455d0028 100644 --- a/pkg/policyprovider/configpolicy/configpolicy.go +++ b/pkg/policyprovider/configpolicy/configpolicy.go @@ -19,6 +19,7 @@ import ( "context" "encoding/json" "fmt" + "sync" re "github.com/ratify-project/ratify/errors" "github.com/ratify-project/ratify/pkg/common" @@ -34,11 +35,13 @@ import ( // PolicyEnforcer describes different polices that are enforced during verification type PolicyEnforcer struct { ArtifactTypePolicies map[string]vt.ArtifactTypeVerifyPolicy + passthroughEnabled bool } type configPolicyEnforcerConf struct { Name string `json:"name"` ArtifactVerificationPolicies map[string]vt.ArtifactTypeVerifyPolicy `json:"artifactVerificationPolicies,omitempty"` + PassthroughEnabled bool `json:"passthroughEnabled"` } const ( @@ -47,6 +50,55 @@ const ( type configPolicyFactory struct{} +type verifyResult struct { + artifactTypeToResult map[string]bool + mu sync.Mutex +} + +type verifiedArtifactTypes struct { + artifactTypes map[string]struct{} + mu sync.Mutex +} + +func newVerifyResult() *verifyResult { + return &verifyResult{ + artifactTypeToResult: map[string]bool{}, + mu: sync.Mutex{}, + } +} + +func newVerifiedArtifactTypes() *verifiedArtifactTypes { + return &verifiedArtifactTypes{ + artifactTypes: map[string]struct{}{}, + mu: sync.Mutex{}, + } +} + +func (r *verifyResult) set(artifactType string, result bool) { + r.mu.Lock() + defer r.mu.Unlock() + r.artifactTypeToResult[artifactType] = result +} + +func (r *verifyResult) get(artifactType string) bool { + r.mu.Lock() + defer r.mu.Unlock() + return r.artifactTypeToResult[artifactType] +} + +func (t *verifiedArtifactTypes) add(artifactType string) { + t.mu.Lock() + defer t.mu.Unlock() + t.artifactTypes[artifactType] = struct{}{} +} + +func (t *verifiedArtifactTypes) exist(artifactType string) bool { + t.mu.Lock() + defer t.mu.Unlock() + _, ok := t.artifactTypes[artifactType] + return ok +} + // init calls Register for our config policy provider func init() { pf.Register(vt.ConfigPolicy, &configPolicyFactory{}) @@ -98,7 +150,7 @@ func (enforcer PolicyEnforcer) ContinueVerifyOnFailure(_ context.Context, _ comm // ErrorToVerifyResult converts an error to a properly formatted verify result func (enforcer PolicyEnforcer) ErrorToVerifyResult(_ context.Context, subjectRefString string, verifyError error) types.VerifyResult { verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail(fmt.Sprintf("failed to verify artifact: %s", subjectRefString)).WithError(verifyError) - errorReport := verifier.NewVerifierResult(subjectRefString, "", "", "", false, &verifierErr, nil) + errorReport := verifier.NewVerifierResult("", "", "", false, &verifierErr, nil) var reports []interface{} reports = append(reports, errorReport) return types.VerifyResult{IsSuccess: false, VerifierReports: reports} @@ -107,51 +159,50 @@ func (enforcer PolicyEnforcer) ErrorToVerifyResult(_ context.Context, subjectRef // OverallVerifyResult determines the final outcome of verification that is constructed using the results from // individual verifications func (enforcer PolicyEnforcer) OverallVerifyResult(_ context.Context, verifierReports []interface{}) bool { - if len(verifierReports) <= 0 { + if enforcer.passthroughEnabled || len(verifierReports) <= 0 { return false } - - // use boolean map to track if each artifact type policy constraint is satisfied - verifySuccess := map[string]bool{} - for artifactType := range enforcer.ArtifactTypePolicies { - // add all policies except for default - if artifactType != defaultPolicyName { - verifySuccess[artifactType] = false + result := newVerifyResult() + existingArtifactTypes := newVerifiedArtifactTypes() + + for artifactType, policyType := range enforcer.ArtifactTypePolicies { + if policyType == vt.AllVerifySuccess { + result.set(artifactType, true) + } else { + result.set(artifactType, false) } } - for _, report := range verifierReports { - castedReport := report.(verifier.VerifierResult) - // extract the policy for the artifact type of the verified artifact if specified - policyType, ok := enforcer.ArtifactTypePolicies[castedReport.ArtifactType] - // if artifact type policy not specified, set policy to be default policy and add artifact type to success map - if !ok { - policyType = enforcer.ArtifactTypePolicies[defaultPolicyName] - } - // set the artifact type success field in map to false to start - if _, ok = verifySuccess[castedReport.ArtifactType]; !ok { - verifySuccess[castedReport.ArtifactType] = false - } + castedVerifierReports := make([]types.NestedVerifierReport, len(verifierReports)) + for i, report := range verifierReports { + castedReport := report.(types.NestedVerifierReport) + castedVerifierReports[i] = castedReport + } + + enforcer.verifyReports(castedVerifierReports, result, existingArtifactTypes) + + // if no artifact types are verified, return false + if len(existingArtifactTypes.artifactTypes) == 0 { + return false + } - if policyType == vt.AnyVerifySuccess && castedReport.IsSuccess { - // if policy is 'any' and report is successful - verifySuccess[castedReport.ArtifactType] = true - } else if policyType == vt.AllVerifySuccess { - // if policy is 'all' - if !castedReport.IsSuccess { - // return false after first failure + for artifactType, policyType := range enforcer.ArtifactTypePolicies { + // if default policy is not evaluated, all artifacts should have been evaluated + // by corresponding policies, just skip the default policy. + if artifactType == defaultPolicyName && !existingArtifactTypes.exist(defaultPolicyName) { + continue + } + if policyType == vt.AnyVerifySuccess { + if !result.get(artifactType) { + return false + } + } else { + if !result.get(artifactType) || !existingArtifactTypes.exist(artifactType) { return false } - verifySuccess[castedReport.ArtifactType] = true } } - // all booleans in map must be true for overall success to be true - for artifactType := range verifySuccess { - if !verifySuccess[artifactType] { - return false - } - } return true } @@ -159,3 +210,43 @@ func (enforcer PolicyEnforcer) OverallVerifyResult(_ context.Context, verifierRe func (enforcer PolicyEnforcer) GetPolicyType(_ context.Context) string { return vt.ConfigPolicy } + +func (enforcer PolicyEnforcer) verifyReports(verifierReports []types.NestedVerifierReport, result *verifyResult, existingArtifactTypes *verifiedArtifactTypes) { + wg := sync.WaitGroup{} + for _, report := range verifierReports { + report := report + if len(report.VerifierReports) == 0 { + continue + } + wg.Add(1) + go func() { + defer wg.Done() + existingArtifactTypes.add(report.ArtifactType) + + // extract the policy for the artifact type of the verified artifact if specified + artifactType := report.ArtifactType + policyType, ok := enforcer.ArtifactTypePolicies[report.ArtifactType] + // if artifact type policy not specified, set policy to be default policy and add artifact type to success map + if !ok { + policyType = enforcer.ArtifactTypePolicies[defaultPolicyName] + artifactType = defaultPolicyName + } + existingArtifactTypes.add(artifactType) + + for _, verifierReport := range report.VerifierReports { + if policyType == vt.AnyVerifySuccess { + if verifierReport.IsSuccess { + result.set(artifactType, true) + } + } else { + if !verifierReport.IsSuccess { + result.set(artifactType, false) + } + } + } + + enforcer.verifyReports(report.NestedReports, result, existingArtifactTypes) + }() + } + wg.Wait() +} diff --git a/pkg/policyprovider/configpolicy/configpolicy_test.go b/pkg/policyprovider/configpolicy/configpolicy_test.go index 565359ef3..6b2a32b13 100644 --- a/pkg/policyprovider/configpolicy/configpolicy_test.go +++ b/pkg/policyprovider/configpolicy/configpolicy_test.go @@ -21,7 +21,7 @@ import ( oci "github.com/opencontainers/image-spec/specs-go/v1" "github.com/ratify-project/ratify/pkg/common" - vt "github.com/ratify-project/ratify/pkg/executor/types" + et "github.com/ratify-project/ratify/pkg/executor/types" "github.com/ratify-project/ratify/pkg/ocispecs" pc "github.com/ratify-project/ratify/pkg/policyprovider/config" pf "github.com/ratify-project/ratify/pkg/policyprovider/factory" @@ -60,7 +60,7 @@ func TestPolicyEnforcer_ContinueVerifyOnFailure(t *testing.T) { Descriptor: oci.Descriptor{}, ArtifactType: "application/vnd.cncf.notary.signature", } - result := vt.VerifyResult{ + result := et.VerifyResult{ IsSuccess: false, VerifierReports: nil, } @@ -90,12 +90,13 @@ func TestPolicyEnforcer_ContinueVerifyOnFailure(t *testing.T) { func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { testcases := []struct { + name string configPolicyConfig map[string]interface{} verifierReports []interface{} output bool }{ { - // no artifact policies or verifier reports + name: "no artifact policies and no referres", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{}, @@ -104,42 +105,43 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { output: false, }, { - // no artifact policies + name: "no artifact policies and no matching verifier reports", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{}, }, verifierReports: []interface{}{ - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: false, ArtifactType: "application/vnd.cncf.notary.signature", }, }, output: false, }, { - // no artifact policies but 1 verifier result is false + name: "no artifact policies and 1 verifier result is false", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{}, }, verifierReports: []interface{}{ - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, - ArtifactType: "application/vnd.cncf.notary.signature", - }, - vr.VerifierResult{ - Subject: "", - IsSuccess: false, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + { + IsSuccess: false, + }, + }, }, }, output: false, }, { - // no artifact policies but default relaxed to 'any' and 1 verifier result is false + name: "no artifact policies but default relaxed to 'any' and 1 verifier result is false", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -147,21 +149,23 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ - Subject: "", - IsSuccess: true, - ArtifactType: "application/vnd.cncf.notary.signature", - }, - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: false, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + { + IsSuccess: false, + }, + }, }, }, output: true, }, { - // any notation artifact policy but no artifact verifier reports + name: "any notation artifact policy but no artifact verifier reports", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -172,7 +176,7 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { output: false, }, { - // any notation artifact policy and only 1 notation report is true + name: "any notation artifact policy and 1 success notation report", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -180,21 +184,23 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, - ArtifactType: "application/vnd.cncf.notary.signature", - }, - vr.VerifierResult{ - Subject: "", - IsSuccess: false, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + { + IsSuccess: false, + }, + }, }, }, output: true, }, { - // all notation artifact policy but only 1 notation report is true + name: "all notation artifact policy and 1 success notation report", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -202,21 +208,23 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ - Subject: "", - IsSuccess: true, - ArtifactType: "application/vnd.cncf.notary.signature", - }, - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: false, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + { + IsSuccess: false, + }, + }, }, }, output: false, }, { - // all notation artifact policy and both notation reports are true + name: "all notation artifact policy and both notation reports are true", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -224,21 +232,24 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ - Subject: "", - IsSuccess: true, - ArtifactType: "application/vnd.cncf.notary.signature", - }, - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + { + IsSuccess: true, + }, + }, }, }, output: true, }, { // any notation artifact policy, any sbom artifact policy and notation report is true and sbom is false + name: "any notation artifact policy, any sbom artifact policy and notation report is true and sbom is false", configPolicyConfig: map[string]interface{}{ "name": "configPolicy", "artifactVerificationPolicies": map[string]types.ArtifactTypeVerifyPolicy{ @@ -247,15 +258,23 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + }, }, - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: false, ArtifactType: "application/spdx+json", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: false, + }, + }, }, }, output: false, @@ -270,15 +289,23 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { }, }, verifierReports: []interface{}{ - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, ArtifactType: "application/vnd.cncf.notary.signature", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + }, }, - vr.VerifierResult{ + et.NestedVerifierReport{ Subject: "", - IsSuccess: true, ArtifactType: "application/spdx+json", + VerifierReports: []vr.VerifierResult{ + { + IsSuccess: true, + }, + }, }, }, output: true, @@ -288,20 +315,22 @@ func TestPolicyEnforcer_OverallVerifyResult(t *testing.T) { ctx := context.Background() for _, testcase := range testcases { - config := pc.PoliciesConfig{ - Version: "1.0.0", - PolicyPlugin: testcase.configPolicyConfig, - } + t.Run(testcase.name, func(t *testing.T) { + config := pc.PoliciesConfig{ + Version: "1.0.0", + PolicyPlugin: testcase.configPolicyConfig, + } - policyEnforcer, err := pf.CreatePolicyProviderFromConfig(config) - if err != nil { - t.Fatalf("PolicyEnforcer should create from PoliciesConfig") - } + policyEnforcer, err := pf.CreatePolicyProviderFromConfig(config) + if err != nil { + t.Fatalf("PolicyEnforcer should create from PoliciesConfig") + } - overallVerifyResult := policyEnforcer.OverallVerifyResult(ctx, testcase.verifierReports) - if overallVerifyResult != testcase.output { - t.Fatalf("Expected %v from OverallVerifyResult but got %v", testcase.output, overallVerifyResult) - } + overallVerifyResult := policyEnforcer.OverallVerifyResult(ctx, testcase.verifierReports) + if overallVerifyResult != testcase.output { + t.Fatalf("Expected %v from OverallVerifyResult but got %v", testcase.output, overallVerifyResult) + } + }) } } diff --git a/pkg/policyprovider/mocks/types.go b/pkg/policyprovider/mocks/types.go index bf57b1870..2c24dc811 100644 --- a/pkg/policyprovider/mocks/types.go +++ b/pkg/policyprovider/mocks/types.go @@ -34,9 +34,8 @@ func (p *TestPolicyProvider) ContinueVerifyOnFailure(_ context.Context, _ common return true } -func (p *TestPolicyProvider) ErrorToVerifyResult(_ context.Context, subjectRefString string, _ error) types.VerifyResult { +func (p *TestPolicyProvider) ErrorToVerifyResult(_ context.Context, _ string, _ error) types.VerifyResult { errorReport := verifier.VerifierResult{ - Subject: subjectRefString, IsSuccess: false, Message: "this a test", } diff --git a/pkg/verifier/cosign/cosign.go b/pkg/verifier/cosign/cosign.go index 86442859a..b36d817b5 100644 --- a/pkg/verifier/cosign/cosign.go +++ b/pkg/verifier/cosign/cosign.go @@ -285,7 +285,6 @@ func (v *cosignVerifier) verifyInternal(ctx context.Context, subjectReference co if hasValidSignature { return verifier.NewVerifierResult( - "", v.name, v.verifierType, "Verification success. Valid signatures found. Please refer to extensions field for verifications performed.", @@ -398,7 +397,6 @@ func (v *cosignVerifier) verifyLegacy(ctx context.Context, subjectReference comm if len(signatures) > 0 { return verifier.NewVerifierResult( - "", v.name, v.verifierType, "Verification success. Valid signatures found", @@ -487,7 +485,6 @@ func staticLayerOpts(desc imgspec.Descriptor) ([]static.Option, error) { func errorToVerifyResult(name string, verifierType string, err error) verifier.VerifierResult { verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail("Failed to validate the Cosign signature").WithError(err) return verifier.NewVerifierResult( - "", name, verifierType, "", diff --git a/pkg/verifier/cosign/cosign_test.go b/pkg/verifier/cosign/cosign_test.go index aea1cf5a7..0fb20a7cc 100644 --- a/pkg/verifier/cosign/cosign_test.go +++ b/pkg/verifier/cosign/cosign_test.go @@ -422,11 +422,11 @@ func TestErrorToVerifyResult(t *testing.T) { if verifierResult.IsSuccess { t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.IsSuccess, false) } - if verifierResult.Name != "test" { - t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.Name, "test") + if verifierResult.VerifierName != "test" { + t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.VerifierName, "test") } - if verifierResult.Type != "cosign" { - t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.Type, "cosign") + if verifierResult.VerifierType != "cosign" { + t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.VerifierType, "cosign") } if verifierResult.Message != "Failed to validate the Cosign signature" { t.Errorf("errorToVerifyResult() = %v, want %v", verifierResult.Message, "Failed to validate the Cosign signature") diff --git a/pkg/verifier/notation/notation.go b/pkg/verifier/notation/notation.go index 52fdcbda6..d3ca80f46 100644 --- a/pkg/verifier/notation/notation.go +++ b/pkg/verifier/notation/notation.go @@ -174,7 +174,7 @@ func (v *notationPluginVerifier) Verify(ctx context.Context, extensions["Issuer"] = cert.Issuer.String() extensions["SN"] = cert.Subject.String() - return verifier.NewVerifierResult("", v.name, v.verifierType, "Notation signature verification success", true, nil, extensions), nil + return verifier.NewVerifierResult(v.name, v.verifierType, "Notation signature verification success", true, nil, extensions), nil } func getVerifierService(conf *NotationPluginVerifierConfig, pluginDirectory string, revocationFactory RevocationFactory) (notation.Verifier, error) { diff --git a/pkg/verifier/plugin/plugin.go b/pkg/verifier/plugin/plugin.go index 9357b46bd..865568533 100644 --- a/pkg/verifier/plugin/plugin.go +++ b/pkg/verifier/plugin/plugin.go @@ -148,7 +148,7 @@ func (vp *VerifierPlugin) verifyReference( return nil, re.ErrorCodeVerifyPluginFailure.NewError(re.Verifier, vp.name, re.EmptyLink, err, nil, re.HideStackTrace) } - result, err := types.GetVerifierResult(stdoutBytes) + result, err := verifier.DecodeVerifierResult(stdoutBytes) if err != nil { return nil, err } diff --git a/pkg/verifier/plugin/skel/skel.go b/pkg/verifier/plugin/skel/skel.go index 05b0ebd08..b29558f39 100644 --- a/pkg/verifier/plugin/skel/skel.go +++ b/pkg/verifier/plugin/skel/skel.go @@ -109,7 +109,7 @@ func (pc *pcontext) pluginMainCore(_, version string, verifyReference VerifyRefe return plugin.NewError(types.ErrPluginCmdFailure, fmt.Sprintf("plugin command %s failed", vp.VerifyCommand), err.Error()) } - err = types.WriteVerifyResultResult(result, pc.Stdout) + err = verifier.WriteVerifyResultResult(result, pc.Stdout) if err != nil { return plugin.NewError(types.ErrIOFailure, "failed to write plugin output", err.Error()) } diff --git a/pkg/verifier/result.go b/pkg/verifier/result.go index dff28e9e8..123752317 100644 --- a/pkg/verifier/result.go +++ b/pkg/verifier/result.go @@ -15,30 +15,26 @@ limitations under the License. package verifier -import "github.com/ratify-project/ratify/errors" +import ( + "encoding/json" + "io" -// VerifierResult describes the result of verifying a reference manifest for a subject. -// Note: This struct is used to represent the result of verification in v0. + "github.com/ratify-project/ratify/errors" +) + +// VerifierResult describes the verification result returned from the verifier plugin type VerifierResult struct { //nolint:revive // ignore linter to have unique type name - Subject string `json:"subject,omitempty"` - IsSuccess bool `json:"isSuccess"` - // Name will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707 - Name string `json:"name,omitempty"` - VerifierName string `json:"verifierName,omitempty"` - // Type will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707 - Type string `json:"type,omitempty"` - VerifierType string `json:"verifierType,omitempty"` - ReferenceDigest string `json:"referenceDigest,omitempty"` - ArtifactType string `json:"artifactType,omitempty"` - Message string `json:"message,omitempty"` - ErrorReason string `json:"errorReason,omitempty"` - Remediation string `json:"remediation,omitempty"` - Extensions interface{} `json:"extensions,omitempty"` - NestedResults []VerifierResult `json:"nestedResults,omitempty"` + IsSuccess bool `json:"isSuccess"` + Message string `json:"message"` + ErrorReason string `json:"errorReason,omitempty"` + Remediation string `json:"remediation,omitempty"` + VerifierName string `json:"verifierName,omitempty"` + VerifierType string `json:"verifierType,omitempty"` + Extensions interface{} `json:"extensions"` } // NewVerifierResult creates a new VerifierResult object with the given parameters. -func NewVerifierResult(subject, verifierName, verifierType, message string, isSuccess bool, err *errors.Error, extensions interface{}) VerifierResult { +func NewVerifierResult(verifierName, verifierType, message string, isSuccess bool, err *errors.Error, extensions interface{}) VerifierResult { var errorReason, remediation string if err != nil { if err.GetDetail() != "" { @@ -48,10 +44,7 @@ func NewVerifierResult(subject, verifierName, verifierType, message string, isSu remediation = err.GetRemediation() } return VerifierResult{ - Subject: subject, IsSuccess: isSuccess, - Name: verifierName, - Type: verifierType, VerifierName: verifierName, VerifierType: verifierType, Message: message, @@ -60,3 +53,23 @@ func NewVerifierResult(subject, verifierName, verifierType, message string, isSu Extensions: extensions, } } + +// WriteVerifyResultResult writes the given result as JSON data to the writer w +func WriteVerifyResultResult(result *VerifierResult, w io.Writer) error { + return json.NewEncoder(w).Encode(result) +} + +// DecodeVerifierResult encodes the given JSON data into verify result object +func DecodeVerifierResult(result []byte) (*VerifierResult, error) { + vResult := VerifierResult{} + if err := json.Unmarshal(result, &vResult); err != nil { + return nil, err + } + return &VerifierResult{ + IsSuccess: vResult.IsSuccess, + Message: vResult.Message, + VerifierName: vResult.VerifierName, + VerifierType: vResult.VerifierType, + Extensions: vResult.Extensions, + }, nil +} diff --git a/pkg/verifier/result_test.go b/pkg/verifier/result_test.go index 64efd2c52..acedff381 100644 --- a/pkg/verifier/result_test.go +++ b/pkg/verifier/result_test.go @@ -68,7 +68,7 @@ func TestNewVerifierResult(t *testing.T) { err = nil } - result := NewVerifierResult("", "", "", tt.message, false, err, nil) + result := NewVerifierResult("", "", tt.message, false, err, nil) if result.Message != tt.expectedMsg { t.Errorf("expected message %s, got %s", tt.expectedMsg, result.Message) } diff --git a/pkg/verifier/types/types.go b/pkg/verifier/types/types.go index d21e9b098..53aab30fc 100644 --- a/pkg/verifier/types/types.go +++ b/pkg/verifier/types/types.go @@ -15,14 +15,6 @@ limitations under the License. package types -import ( - "encoding/json" - "io" - - "github.com/ratify-project/ratify/errors" - "github.com/ratify-project/ratify/pkg/verifier" -) - const ( SpecVersion string = "0.1.0" Version string = "version" @@ -45,80 +37,3 @@ const ( ErrPluginCmdFailure // 8 ErrInternalFailure uint = 999 ) - -// VerifierResult describes the verification result returned from the verifier plugin -type VerifierResult struct { - IsSuccess bool `json:"isSuccess"` - Message string `json:"message"` - ErrorReason string `json:"errorReason,omitempty"` - Remediation string `json:"remediation,omitempty"` - // Name will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707 - Name string `json:"name"` - VerifierName string `json:"verifierName,omitempty"` - // Type will be deprecated in v2, tracking issue: https://github.com/ratify-project/ratify/issues/1707 - Type string `json:"type,omitempty"` - VerifierType string `json:"verifierType,omitempty"` - Extensions interface{} `json:"extensions"` -} - -// GetVerifierResult encodes the given JSON data into verify result object -func GetVerifierResult(result []byte) (*verifier.VerifierResult, error) { - vResult := VerifierResult{} - if err := json.Unmarshal(result, &vResult); err != nil { - return nil, err - } - return &verifier.VerifierResult{ - IsSuccess: vResult.IsSuccess, - Message: vResult.Message, - Name: vResult.Name, - Type: vResult.Type, - VerifierName: vResult.Name, - VerifierType: vResult.Type, - Extensions: vResult.Extensions, - }, nil -} - -// WriteVerifyResultResult writes the given result as JSON data to the writer w -func WriteVerifyResultResult(result *verifier.VerifierResult, w io.Writer) error { - return json.NewEncoder(w).Encode(result) -} - -// CreateVerifierResult creates a new verifier result object from given input. -func CreateVerifierResult(verifierName, verifierType, message string, isSuccess bool, err *errors.Error) VerifierResult { - var errorReason string - var remediation string - if err != nil { - if err.GetDetail() != "" { - message = err.GetDetail() - } - errorReason = err.GetErrorReason() - remediation = err.GetRemediation() - } - - return VerifierResult{ - IsSuccess: isSuccess, - Name: verifierName, - Type: verifierType, - VerifierName: verifierName, - VerifierType: verifierType, - Message: message, - ErrorReason: errorReason, - Remediation: remediation, - } -} - -// NewVerifierResult creates a new verifier result object from the given -// verifier.VerifierResult. -func NewVerifierResult(result verifier.VerifierResult) VerifierResult { - return VerifierResult{ - IsSuccess: result.IsSuccess, - Message: result.Message, - Name: result.Name, - Type: result.Type, - VerifierName: result.VerifierName, - VerifierType: result.VerifierType, - Extensions: result.Extensions, - ErrorReason: result.ErrorReason, - Remediation: result.Remediation, - } -} diff --git a/pkg/verifier/types/types_test.go b/pkg/verifier/types/types_test.go deleted file mode 100644 index ce1cd39f6..000000000 --- a/pkg/verifier/types/types_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright The Ratify Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package types - -import ( - "fmt" - "testing" - - "github.com/ratify-project/ratify/errors" -) - -const ( - testMsg1 = "test message 1" - testMsg2 = "test message 2" - testErrReason = "test error reason" - testRemediation = "test remediation" -) - -func TestCreateVerifierResult(t *testing.T) { - tests := []struct { - name string - message string - err errors.Error - expectedMsg string - expectedErrReason string - expectedRemediation string - }{ - { - name: "nil error", - message: testMsg1, - err: errors.Error{}, - expectedMsg: testMsg1, - }, - { - name: "error without detail", - message: testMsg1, - err: errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation), - expectedMsg: testMsg1, - expectedErrReason: testErrReason, - expectedRemediation: testRemediation, - }, - { - name: "error with detail", - message: testMsg1, - err: errors.ErrorCodeUnknown.WithError(fmt.Errorf(testErrReason)).WithRemediation(testRemediation).WithDetail(testMsg2), - expectedMsg: testMsg2, - expectedErrReason: testErrReason, - expectedRemediation: testRemediation, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := &tt.err - if tt.err == (errors.Error{}) { - err = nil - } - - result := CreateVerifierResult("", "", tt.message, false, err) - if result.Message != tt.expectedMsg { - t.Errorf("expected message %s, got %s", tt.expectedMsg, result.Message) - } - if result.ErrorReason != tt.expectedErrReason { - t.Errorf("expected error reason %s, got %s", tt.expectedErrReason, result.ErrorReason) - } - if result.Remediation != tt.expectedRemediation { - t.Errorf("expected remediation %s, got %s", tt.expectedRemediation, result.Remediation) - } - }) - } -} diff --git a/plugins/verifier/licensechecker/licensechecker.go b/plugins/verifier/licensechecker/licensechecker.go index 0e252f2a4..74e7c1296 100644 --- a/plugins/verifier/licensechecker/licensechecker.go +++ b/plugins/verifier/licensechecker/licensechecker.go @@ -73,10 +73,10 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, desc if len(referenceManifest.Blobs) == 0 { return &verifier.VerifierResult{ - Name: input.Name, - Type: verifierType, - IsSuccess: false, - Message: fmt.Sprintf("License Check FAILED: no blobs found for referrer %s@%s", subjectReference.Path, descriptor.Digest.String()), + VerifierName: input.Name, + VerifierType: verifierType, + IsSuccess: false, + Message: fmt.Sprintf("License Check FAILED: no blobs found for referrer %s@%s", subjectReference.Path, descriptor.Digest.String()), }, nil } @@ -96,18 +96,18 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, desc if len(disallowedLicenses) > 0 { return &verifier.VerifierResult{ - Name: input.Name, - Type: verifierType, - IsSuccess: false, - Message: fmt.Sprintf("License Check: FAILED. %s", disallowedLicenses), + VerifierName: input.Name, + VerifierType: verifierType, + IsSuccess: false, + Message: fmt.Sprintf("License Check: FAILED. %s", disallowedLicenses), }, nil } } return &verifier.VerifierResult{ - Name: input.Name, - Type: verifierType, - IsSuccess: true, - Message: "License Check: SUCCESS. All packages have allowed licenses", + VerifierName: input.Name, + VerifierType: verifierType, + IsSuccess: true, + Message: "License Check: SUCCESS. All packages have allowed licenses", }, nil } diff --git a/plugins/verifier/sample/sample.go b/plugins/verifier/sample/sample.go index 1fb80f9b8..89cd92093 100644 --- a/plugins/verifier/sample/sample.go +++ b/plugins/verifier/sample/sample.go @@ -75,9 +75,9 @@ func VerifyReference(args *skel.CmdArgs, _ common.Reference, referenceDescriptor } return &verifier.VerifierResult{ - Name: input.Name, - Type: verifierType, - IsSuccess: referenceDescriptor.Size > 0, - Message: "Sample verification success", + VerifierName: input.Name, + VerifierType: verifierType, + IsSuccess: referenceDescriptor.Size > 0, + Message: "Sample verification success", }, nil } diff --git a/plugins/verifier/sbom/sbom.go b/plugins/verifier/sbom/sbom.go index 70a18ad2a..185f12030 100644 --- a/plugins/verifier/sbom/sbom.go +++ b/plugins/verifier/sbom/sbom.go @@ -85,13 +85,13 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe referenceManifest, err := referrerStore.GetReferenceManifest(ctx, subjectReference, referenceDescriptor) if err != nil { storeErr := re.ErrorCodeGetReferenceManifestFailure.WithDetail(fmt.Sprintf("Failed to fetch reference manifest for subject: %s reference descriptor: %v", subjectReference, referenceDescriptor.Descriptor)).WithError(err) - result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &storeErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "", false, &storeErr, nil) return &result, nil } if len(referenceManifest.Blobs) == 0 { noBlobErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No layers found in manifest for referrer %s@%s", subjectReference.Path, referenceDescriptor.Digest.String())) - result := verifier.NewVerifierResult("", input.Name, verifierType, "SBOM validation failed", false, &noBlobErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "SBOM validation failed", false, &noBlobErr, nil) return &result, nil } @@ -101,7 +101,7 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if err != nil { storeErr := re.ErrorCodeGetBlobContentFailure.WithDetail(fmt.Sprintf("Failed to fetch blob for subject: %s digest: %s", subjectReference, blobDesc.Digest)).WithError(err) - result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &storeErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "", false, &storeErr, nil) return &result, nil } @@ -110,11 +110,11 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe return processSpdxJSONMediaType(input.Name, verifierType, refBlob, input.DisallowedLicenses, input.DisallowedPackages), nil default: storeErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Unsupported artifactType: %s", artifactType)) - result := verifier.NewVerifierResult("", input.Name, verifierType, "Failed to process SBOM blobs.", false, &storeErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "Failed to process SBOM blobs.", false, &storeErr, nil) return &result, nil } } - result := verifier.NewVerifierResult("", input.Name, verifierType, "SBOM verification success. No license or package violation found.", true, nil, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "SBOM verification success. No license or package violation found.", true, nil, nil) return &result, nil } @@ -164,13 +164,12 @@ func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte, if len(licenseViolation) != 0 || len(packageViolation) != 0 { sbomErr := errors.ErrorCodeVerifyPluginFailure.WithDetail("License or package violation found.").WithRemediation("Please review extensions data for license and package violation found.") - result := verifier.NewVerifierResult("", name, verifierType, "SBOM validation failed", false, &sbomErr, extensionData) + result := verifier.NewVerifierResult(name, verifierType, "SBOM validation failed", false, &sbomErr, extensionData) return &result } } result := verifier.NewVerifierResult( - "", name, verifierType, "SBOM verification success. No license or package violation found.", @@ -181,7 +180,7 @@ func processSpdxJSONMediaType(name string, verifierType string, refBlob []byte, return &result } verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("failed to verify artifact: %s", name)).WithError(err) - result := verifier.NewVerifierResult("", name, verifierType, "", false, &verifierErr, nil) + result := verifier.NewVerifierResult(name, verifierType, "", false, &verifierErr, nil) return &result } diff --git a/plugins/verifier/schemavalidator/schema_validator.go b/plugins/verifier/schemavalidator/schema_validator.go index d31886222..b9e3ea4d0 100644 --- a/plugins/verifier/schemavalidator/schema_validator.go +++ b/plugins/verifier/schemavalidator/schema_validator.go @@ -74,7 +74,7 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if len(referenceManifest.Blobs) == 0 { noBlobErr := errors.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No blobs found for referrer %s@%s.", subjectReference.Path, referenceDescriptor.Digest.String())) - result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &noBlobErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "", false, &noBlobErr, nil) return &result, nil } @@ -87,12 +87,12 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe err = processMediaType(schemaMap, blobDesc.MediaType, refBlob) if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("schema validation failed for digest:[%s], media type:[%s].", blobDesc.Digest, blobDesc.MediaType)).WithError(err) - result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &verifierErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "", false, &verifierErr, nil) return &result, nil } } - result := verifier.NewVerifierResult("", input.Name, verifierType, "schema validation passed for configured media types", true, nil, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "schema validation passed for configured media types", true, nil, nil) return &result, nil } diff --git a/plugins/verifier/vulnerabilityreport/vulnerability_report.go b/plugins/verifier/vulnerabilityreport/vulnerability_report.go index ec4004f8f..546ce2353 100644 --- a/plugins/verifier/vulnerabilityreport/vulnerability_report.go +++ b/plugins/verifier/vulnerabilityreport/vulnerability_report.go @@ -96,7 +96,7 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe createdTime, err := extractCreationTimestamp(input.CreatedAnnotationName, referenceDescriptor) if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to create timestamp annotation.").WithError(err) - result := verifier.NewVerifierResult("", input.Name, verifierType, "", false, &verifierErr, nil) + result := verifier.NewVerifierResult(input.Name, verifierType, "", false, &verifierErr, nil) return &result, nil } @@ -106,7 +106,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to validate maximum age.").WithError(err) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -119,7 +118,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if !ok { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Report is older than maximum age:[%s].", input.MaximumAge)) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -137,7 +135,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to fetch reference manifest for subject: %s, reference descriptor: %v.", subjectReference, referenceDescriptor)).WithError(err) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -151,7 +148,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if len(referenceManifest.Blobs) == 0 { noBlobErr := errors.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("No layers found in manifest for referrer %s@%s.", subjectReference.Path, referenceDescriptor.Digest.String())) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -167,7 +163,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to fetch blob for subject:[%s] digest:[%s].", subjectReference, blobDesc.Digest)).WithError(err) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -181,7 +176,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe // skip all validation if passthrough is enabled if input.Passthrough { result := verifier.NewVerifierResult( - "", input.Name, verifierType, "Validation skipped. passthrough enabled", @@ -200,7 +194,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe if err := verifyJSONSchema(referenceDescriptor.ArtifactType, refBlob, input.SchemaURL); err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Schema validation failed for digest:[%s],artifact type:[%s].", blobDesc.Digest, referenceDescriptor.ArtifactType)).WithError(err) result := verifier.NewVerifierResult( - "", input.Name, verifierType, "", @@ -216,7 +209,6 @@ func VerifyReference(args *skel.CmdArgs, subjectReference common.Reference, refe } result := verifier.NewVerifierResult( - "", input.Name, verifierType, "Validation succeeded", @@ -251,7 +243,6 @@ func processSarifReport(input *PluginConfig, verifierName string, verifierType s if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to parse sarif report.").WithError(err) result := verifier.NewVerifierResult( - "", verifierName, verifierType, "", @@ -264,7 +255,6 @@ func processSarifReport(input *PluginConfig, verifierName string, verifierType s // verify that there is at least one run in the report if len(sarifReport.Runs) < 1 { result := verifier.NewVerifierResult( - "", verifierName, verifierType, "No runs found in sarif report.", @@ -295,7 +285,6 @@ func processSarifReport(input *PluginConfig, verifierName string, verifierType s } result := verifier.NewVerifierResult( - "", verifierName, verifierType, "Validation succeeded", @@ -323,7 +312,6 @@ func verifyDenyListCVEs(verifierName string, verifierType string, scannerName st for _, result := range sarifReport.Runs[0].Results { if result.RuleID == nil || *result.RuleID == "" { verifierResult := verifier.NewVerifierResult( - "", verifierName, verifierType, fmt.Sprintf("Rule id not found for result:[%v].", result), @@ -351,7 +339,6 @@ func verifyDenyListCVEs(verifierName string, verifierType string, scannerName st if len(denylistViolations) > 0 { result := verifier.NewVerifierResult( - "", verifierName, verifierType, "Found denied CVEs. See extensions field for details.", @@ -368,7 +355,6 @@ func verifyDenyListCVEs(verifierName string, verifierType string, scannerName st } result := verifier.NewVerifierResult( - "", verifierName, verifierType, "Validation succeeded", @@ -394,7 +380,6 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne for _, result := range sarifReport.Runs[0].Results { if result.RuleID == nil || *result.RuleID == "" { verifierResult := verifier.NewVerifierResult( - "", verifierName, verifierType, fmt.Sprintf("Rule id not found for result:[%v].", result), @@ -410,7 +395,6 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne rule, ok := ruleMap[*result.RuleID] if !ok { verifierResult := verifier.NewVerifierResult( - "", verifierName, verifierType, fmt.Sprintf("Rule not found for result:[%v].", result), @@ -427,7 +411,6 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne if err != nil { verifierErr := re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to extract severity.").WithError(err) verifierResult := verifier.NewVerifierResult( - "", verifierName, verifierType, "", @@ -450,7 +433,6 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne // if there are violating rules, return them as custom extension field if len(violatingRules) > 0 { result := verifier.NewVerifierResult( - "", verifierName, verifierType, "Found disallowed severities. See extensions field for details.", @@ -466,7 +448,6 @@ func verifyDisallowedSeverities(verifierName string, verifierType string, scanne return &result, nil } result := verifier.NewVerifierResult( - "", verifierName, verifierType, "Validation succeeded",