From d7f2ae9bedd73a1c8f4a4c1e6f9b93a7c7227ab1 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Wed, 12 Apr 2023 06:46:38 -0500 Subject: [PATCH] :seedling: Unit tests for attestor policy - Add tests for `GetRequiredChecksForPolicy` and `EvaluateResults` - Add checks for binary artifacts, vulnerabilities, unpinned dependencies, and code review [attestor/policy/attestation_policy_test.go] - Add `github.com/google/go-cmp/cmp` to imports - Add a test for `GetRequiredChecksForPolicy` - Add a test for `EvaluateResults` Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- .codecov.yml | 1 + attestor/policy/attestation_policy.go | 9 +- attestor/policy/attestation_policy_test.go | 164 +++++++++++++++++++++ 3 files changed, 169 insertions(+), 5 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 51c0e9691f3..f5e0d5c0d57 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -7,6 +7,7 @@ codecov: ignore: - "cron/**/*" - "clients/mockclients/**/*" + - "attestor/command/**/*" coverage: precision: 2 round: down diff --git a/attestor/policy/attestation_policy.go b/attestor/policy/attestation_policy.go index 6af48d44240..1052bbad948 100644 --- a/attestor/policy/attestation_policy.go +++ b/attestor/policy/attestation_policy.go @@ -71,8 +71,7 @@ type Dependency struct { Version string `yaml:"version"` } -// Allows us to run fewer scorecard checks if some policy values -// are don't-cares. +// GetRequiredChecksForPolicy Allows us to run fewer scorecard checks if some policy values are don't-cares. func (ap *AttestationPolicy) GetRequiredChecksForPolicy() map[string]bool { requiredChecks := make(map[string]bool) @@ -95,7 +94,7 @@ func (ap *AttestationPolicy) GetRequiredChecksForPolicy() map[string]bool { return requiredChecks } -// Run attestation policy checks on raw data. +// EvaluateResults Run attestation policy checks on raw data. func (ap *AttestationPolicy) EvaluateResults(raw *checker.RawResults) (PolicyResult, error) { logger := sclog.NewLogger(sclog.DefaultLevel) if ap.PreventBinaryArtifacts { @@ -291,7 +290,7 @@ func isUnpinnedDependencyAllowed(d checker.Dependency, allowed []Dependency) boo return false } -// ParseFromFile takes a policy file and returns an AttestationPolicy. +// ParseAttestationPolicyFromFile takes a policy file and returns an AttestationPolicy. func ParseAttestationPolicyFromFile(policyFile string) (*AttestationPolicy, error) { if policyFile != "" { data, err := os.ReadFile(policyFile) @@ -315,7 +314,7 @@ func ParseAttestationPolicyFromFile(policyFile string) (*AttestationPolicy, erro return nil, nil } -// Parses a policy file and returns a AttestationPolicy. +// ParseAttestationPolicyFromYAML parses a policy file and returns a AttestationPolicy. func ParseAttestationPolicyFromYAML(b []byte) (*AttestationPolicy, error) { ap := AttestationPolicy{} diff --git a/attestor/policy/attestation_policy_test.go b/attestor/policy/attestation_policy_test.go index bc9cc8c4f07..61e222b906d 100644 --- a/attestor/policy/attestation_policy_test.go +++ b/attestor/policy/attestation_policy_test.go @@ -20,6 +20,8 @@ import ( "fmt" "testing" + "github.com/google/go-cmp/cmp" + "github.com/ossf/scorecard/v4/checker" "github.com/ossf/scorecard/v4/clients" sce "github.com/ossf/scorecard/v4/errors" @@ -538,3 +540,165 @@ func TestAttestationPolicyRead(t *testing.T) { }) } } + +func TestAttestationPolicy_GetRequiredChecksForPolicy(t *testing.T) { + t.Parallel() + type fields struct { //nolint:govet + PreventBinaryArtifacts bool + AllowedBinaryArtifacts []string + PreventKnownVulnerabilities bool + PreventUnpinnedDependencies bool + AllowedUnpinnedDependencies []Dependency + EnsureCodeReviewed bool + CodeReviewRequirements CodeReviewRequirements + } + tests := []struct { //nolint:govet + name string + fields fields + want map[string]bool + }{ + { + name: "all checks", + fields: fields{ + PreventBinaryArtifacts: true, + AllowedBinaryArtifacts: []string{}, + PreventKnownVulnerabilities: true, + PreventUnpinnedDependencies: true, + AllowedUnpinnedDependencies: []Dependency{}, + EnsureCodeReviewed: true, + CodeReviewRequirements: CodeReviewRequirements{MinReviewers: 1}, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ap := &AttestationPolicy{ + PreventBinaryArtifacts: tt.fields.PreventBinaryArtifacts, + AllowedBinaryArtifacts: tt.fields.AllowedBinaryArtifacts, + PreventKnownVulnerabilities: tt.fields.PreventKnownVulnerabilities, + PreventUnpinnedDependencies: tt.fields.PreventUnpinnedDependencies, + AllowedUnpinnedDependencies: tt.fields.AllowedUnpinnedDependencies, + EnsureCodeReviewed: tt.fields.EnsureCodeReviewed, + CodeReviewRequirements: tt.fields.CodeReviewRequirements, + } + if got := ap.GetRequiredChecksForPolicy(); cmp.Equal(got, tt.want) { + t.Errorf("GetRequiredChecksForPolicy() %v, want %v", cmp.Diff(got, tt.want), tt.want) + } + }) + } +} + +func TestAttestationPolicy_EvaluateResults(t *testing.T) { + t.Parallel() + type fields struct { //nolint:govet + PreventBinaryArtifacts bool + AllowedBinaryArtifacts []string + PreventKnownVulnerabilities bool + PreventUnpinnedDependencies bool + AllowedUnpinnedDependencies []Dependency + EnsureCodeReviewed bool + CodeReviewRequirements CodeReviewRequirements + } + type args struct { + raw *checker.RawResults + } + tests := []struct { //nolint:govet + name string + fields fields + args args + want PolicyResult + wantErr bool + }{ + { + name: "vulnerabilities", + fields: fields{ + PreventKnownVulnerabilities: true, + }, + args: args{ + raw: &checker.RawResults{ + VulnerabilitiesResults: checker.VulnerabilitiesData{ + Vulnerabilities: []clients.Vulnerability{ + {ID: "foo"}, + }, + }, + }, + }, + want: false, + }, + { + name: "binary artifacts", + fields: fields{ + PreventBinaryArtifacts: true, + }, + args: args{ + raw: &checker.RawResults{ + BinaryArtifactResults: checker.BinaryArtifactData{Files: []checker.File{ + {Path: "a"}, + {Path: "b"}, + }}, + }, + }, + want: false, + }, + { + name: "unpinned dependencies", + fields: fields{ + PreventUnpinnedDependencies: true, + }, + args: args{ + raw: &checker.RawResults{ + PinningDependenciesResults: checker.PinningDependenciesData{ + Dependencies: []checker.Dependency{ + {Name: asPointer("foo"), PinnedAt: asPointer("abcdef")}, + }, + }, + }, + }, + want: true, + }, + { + name: "code review", + fields: fields{ + EnsureCodeReviewed: true, + }, + args: args{ + raw: &checker.RawResults{ + CodeReviewResults: checker.CodeReviewData{ + DefaultBranchChangesets: []checker.Changeset{ + { + RevisionID: "1", + Commits: []clients.Commit{{SHA: "a"}}, + }, + }, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ap := &AttestationPolicy{ + PreventBinaryArtifacts: tt.fields.PreventBinaryArtifacts, + AllowedBinaryArtifacts: tt.fields.AllowedBinaryArtifacts, + PreventKnownVulnerabilities: tt.fields.PreventKnownVulnerabilities, + PreventUnpinnedDependencies: tt.fields.PreventUnpinnedDependencies, + AllowedUnpinnedDependencies: tt.fields.AllowedUnpinnedDependencies, + EnsureCodeReviewed: tt.fields.EnsureCodeReviewed, + CodeReviewRequirements: tt.fields.CodeReviewRequirements, + } + got, err := ap.EvaluateResults(tt.args.raw) + if (err != nil) != tt.wantErr { + t.Errorf("EvaluateResults() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("EvaluateResults() got = %v, want %v", got, tt.want) + } + }) + } +}