From 127ee1391ee3f1c6b0ef7b905d0015bd62d67813 Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 17:32:13 +0000 Subject: [PATCH 1/6] raw vulnerabilities seperation --- checker/raw_result.go | 14 +++++++++ checks/vulnerabilities.go | 55 ++++++++++------------------------ checks/vulnerabilities_test.go | 2 +- e2e/vulnerabilities_test.go | 4 +-- pkg/json_raw_results.go | 25 ++++++++++++++++ 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/checker/raw_result.go b/checker/raw_result.go index 63350d76e80..c36defde2f9 100644 --- a/checker/raw_result.go +++ b/checker/raw_result.go @@ -17,12 +17,19 @@ package checker // RawResults contains results before a policy // is applied. type RawResults struct { + VulnerabilitiesResults VulnerabilitiesData BinaryArtifactResults BinaryArtifactData SecurityPolicyResults SecurityPolicyData DependencyUpdateToolResults DependencyUpdateToolData BranchProtectionResults BranchProtectionsData } +// VulnerabilitiesData contains the raw results +// for the Vulnerabilities check. +type VulnerabilitiesData struct { + Vulnerabilities []Vulnerability +} + // SecurityPolicyData contains the raw results // for the Security-Policy check. type SecurityPolicyData struct { @@ -111,3 +118,10 @@ type File struct { Type FileType // Type of file. // TODO: add hash. } + +type Vulnerability struct { + // For OSV: OSV-2020-484 + // For CVE: CVE-2022-23945 + ID string + // TODO: additional information +} diff --git a/checks/vulnerabilities.go b/checks/vulnerabilities.go index 97222ef1b50..0aac7127e96 100644 --- a/checks/vulnerabilities.go +++ b/checks/vulnerabilities.go @@ -1,4 +1,4 @@ -// Copyright 2021 Security Scorecard Authors +// Copyright 2020 Security Scorecard Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,61 +15,36 @@ package checks import ( - "fmt" - "strings" - "github.com/ossf/scorecard/v4/checker" - "github.com/ossf/scorecard/v4/clients" + "github.com/ossf/scorecard/v4/checks/evaluation" + "github.com/ossf/scorecard/v4/checks/raw" sce "github.com/ossf/scorecard/v4/errors" ) -const ( - // CheckVulnerabilities is the registered name for the OSV check. - CheckVulnerabilities = "Vulnerabilities" -) +// CheckVulnerabilities is the registered name for the OSV check. +const CheckVulnerabilities = "Vulnerabilities" //nolint:gochecknoinits func init() { - if err := registerCheck(CheckVulnerabilities, HasUnfixedVulnerabilities); err != nil { + if err := registerCheck(CheckVulnerabilities, Vulnerabilities); err != nil { // this should never happen panic(err) } } -func getVulnerabilities(resp *clients.VulnerabilitiesResponse) []string { - ids := make([]string, 0, len(resp.Vulns)) - for _, vuln := range resp.Vulns { - ids = append(ids, vuln.ID) - } - return ids -} - -// HasUnfixedVulnerabilities runs Vulnerabilities check. -func HasUnfixedVulnerabilities(c *checker.CheckRequest) checker.CheckResult { - commits, err := c.RepoClient.ListCommits() - if err != nil { - e := sce.WithMessage(sce.ErrScorecardInternal, "Client.Repositories.ListCommits") - return checker.CreateRuntimeErrorResult(CheckVulnerabilities, e) - } - - if len(commits) < 1 || commits[0].SHA == "" { - return checker.CreateInconclusiveResult(CheckVulnerabilities, "no commits found") - } - - resp, err := c.VulnerabilitiesClient.HasUnfixedVulnerabilities(c.Ctx, commits[0].SHA) +// Vulnerabilities runs Vulnerabilities check. +func Vulnerabilities(c *checker.CheckRequest) checker.CheckResult { + rawData, err := raw.Vulnerabilities(c) if err != nil { - e := sce.WithMessage(sce.ErrScorecardInternal, "VulnerabilitiesClient.HasUnfixedVulnerabilities") + e := sce.WithMessage(sce.ErrScorecardInternal, err.Error()) return checker.CreateRuntimeErrorResult(CheckVulnerabilities, e) } - // TODO: take severity into account. - vulnIDs := getVulnerabilities(&resp) - if len(vulnIDs) > 0 { - c.Dlogger.Warn3(&checker.LogMessage{ - Text: fmt.Sprintf("HEAD is vulnerable to %s", strings.Join(vulnIDs, ", ")), - }) - return checker.CreateMinScoreResult(CheckVulnerabilities, "existing vulnerabilities detected") + // Set the raw results. + if c.RawResults != nil { + c.RawResults.VulnerabilitiesResults = rawData + return checker.CheckResult{} } - return checker.CreateMaxScoreResult(CheckVulnerabilities, "no vulnerabilities detected") + return evaluation.Vulnerabilities(CheckVulnerabilities, c.Dlogger, &rawData) } diff --git a/checks/vulnerabilities_test.go b/checks/vulnerabilities_test.go index 0bf642fde2d..f0cee6c942b 100644 --- a/checks/vulnerabilities_test.go +++ b/checks/vulnerabilities_test.go @@ -66,7 +66,7 @@ func TestVulnerabilities(t *testing.T) { Ctx: context.TODO(), VulnerabilitiesClient: mockVulnClient, } - res := HasUnfixedVulnerabilities(&req) + res := Vulnerabilities(&req) if !tt.isError && res.Error != nil { t.Fail() } else if tt.isError && res.Error == nil { diff --git a/e2e/vulnerabilities_test.go b/e2e/vulnerabilities_test.go index 68dd0f8d806..b6d87252864 100644 --- a/e2e/vulnerabilities_test.go +++ b/e2e/vulnerabilities_test.go @@ -52,7 +52,7 @@ var _ = Describe("E2E TEST:Vulnerabilities", func() { NumberOfDebug: 0, } - result := checks.HasUnfixedVulnerabilities(&req) + result := checks.Vulnerabilities(&req) // UPGRADEv2: to remove. // Old version. Expect(result.Error).Should(BeNil()) @@ -84,7 +84,7 @@ var _ = Describe("E2E TEST:Vulnerabilities", func() { NumberOfInfo: 0, NumberOfDebug: 0, } - result := checks.HasUnfixedVulnerabilities(&checkRequest) + result := checks.Vulnerabilities(&checkRequest) // UPGRADEv2: to remove. // Old version. Expect(result.Error).Should(BeNil()) diff --git a/pkg/json_raw_results.go b/pkg/json_raw_results.go index 6c94320e56a..8ad40bc068e 100644 --- a/pkg/json_raw_results.go +++ b/pkg/json_raw_results.go @@ -65,6 +65,7 @@ type jsonBranchProtection struct { } type jsonRawResults struct { + DatabaseVulnerabilities []jsonDatabaseVulnerability `json:"database-vulnerabilities"` // List of binaries found in the repo. Binaries []jsonFile `json:"binaries"` // List of security policy files found in the repo. @@ -77,6 +78,25 @@ type jsonRawResults struct { BranchProtections []jsonBranchProtection `json:"branch-protections"` } +type jsonDatabaseVulnerability struct { + // For OSV: OSV-2020-484 + // For CVE: CVE-2022-23945 + ID string + // TODO: additional information +} + +//nolint:unparam +func (r *jsonScorecardRawResult) addVulnerbilitiesRawResults(vd *checker.VulnerabilitiesData) error { + r.Results.DatabaseVulnerabilities = []jsonDatabaseVulnerability{} + for _, v := range vd.Vulnerabilities { + r.Results.DatabaseVulnerabilities = append(r.Results.DatabaseVulnerabilities, + jsonDatabaseVulnerability{ + ID: v.ID, + }) + } + return nil +} + //nolint:unparam func (r *jsonScorecardRawResult) addBinaryArtifactRawResults(ba *checker.BinaryArtifactData) error { r.Results.Binaries = []jsonFile{} @@ -148,6 +168,11 @@ func (r *jsonScorecardRawResult) addBranchProtectionRawResults(bp *checker.Branc } func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) error { + // Vulnerabiliries. + if err := r.addVulnerbilitiesRawResults(&raw.VulnerabilitiesResults); err != nil { + return sce.WithMessage(sce.ErrScorecardInternal, err.Error()) + } + // Binary-Artifacts. if err := r.addBinaryArtifactRawResults(&raw.BinaryArtifactResults); err != nil { return sce.WithMessage(sce.ErrScorecardInternal, err.Error()) From 6e0bbe92747529a49648cbe7c721d5f43ced833a Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 17:34:15 +0000 Subject: [PATCH 2/6] update year --- checks/vulnerabilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/vulnerabilities.go b/checks/vulnerabilities.go index 0aac7127e96..9a16e4147de 100644 --- a/checks/vulnerabilities.go +++ b/checks/vulnerabilities.go @@ -1,4 +1,4 @@ -// Copyright 2020 Security Scorecard Authors +// Copyright 2022 Security Scorecard Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From f8a86f77270aa10e267cb94e8ad2db35fd5eccc7 Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 17:35:14 +0000 Subject: [PATCH 3/6] missing files --- checks/evaluation/vulnerabilities.go | 53 ++++++++++++++++++++++++++ checks/raw/vulnerabilities.go | 57 ++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 checks/evaluation/vulnerabilities.go create mode 100644 checks/raw/vulnerabilities.go diff --git a/checks/evaluation/vulnerabilities.go b/checks/evaluation/vulnerabilities.go new file mode 100644 index 00000000000..7d53ce19b48 --- /dev/null +++ b/checks/evaluation/vulnerabilities.go @@ -0,0 +1,53 @@ +// Copyright 2022 Security Scorecard 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 evaluation + +import ( + "fmt" + "strings" + + "github.com/ossf/scorecard/v4/checker" + sce "github.com/ossf/scorecard/v4/errors" +) + +// Vulnerabilities applies the score policy for the Vulnerabilities check. +func Vulnerabilities(name string, dl checker.DetailLogger, + r *checker.VulnerabilitiesData) checker.CheckResult { + if r == nil { + e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data") + return checker.CreateRuntimeErrorResult(name, e) + } + + score := checker.MaxResultScore + IDs := []string{} + for _, vuln := range r.Vulnerabilities { + IDs = append(IDs, vuln.ID) + score-- + } + + if score < 0 { + score = 0 + } + + if len(IDs) > 0 { + dl.Warn3(&checker.LogMessage{ + Text: fmt.Sprintf("HEAD is vulnerable to %s", strings.Join(IDs, ", ")), + }) + return checker.CreateResultWithScore(name, + fmt.Sprintf("%v existing vulnerabilities detected", len(IDs)), score) + } + + return checker.CreateMaxScoreResult(name, "no vulnerabilities detected") +} diff --git a/checks/raw/vulnerabilities.go b/checks/raw/vulnerabilities.go new file mode 100644 index 00000000000..ff76937c2ec --- /dev/null +++ b/checks/raw/vulnerabilities.go @@ -0,0 +1,57 @@ +// Copyright 2022 Security Scorecard 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 raw + +import ( + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/clients" + sce "github.com/ossf/scorecard/v4/errors" +) + +// Vulnerabilities retrieves the raw data for the Vulnerabilities check. +func Vulnerabilities(c *checker.CheckRequest) (checker.VulnerabilitiesData, error) { + commits, err := c.RepoClient.ListCommits() + if err != nil { + return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "Client.Repositories.ListCommits") + } + + if len(commits) < 1 || commits[0].SHA == "" { + return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "no commits found") + } + + resp, err := c.VulnerabilitiesClient.HasUnfixedVulnerabilities(c.Ctx, commits[0].SHA) + if err != nil { + return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "VulnerabilitiesClient.HasUnfixedVulnerabilities") + } + + vulnIDs := getVulnerabilities(&resp) + vulns := []checker.Vulnerability{} + for _, id := range vulnIDs { + v := checker.Vulnerability{ + ID: id, + // TODO: add fields. + } + vulns = append(vulns, v) + } + return checker.VulnerabilitiesData{Vulnerabilities: vulns}, nil +} + +func getVulnerabilities(resp *clients.VulnerabilitiesResponse) []string { + ids := make([]string, 0, len(resp.Vulns)) + for _, vuln := range resp.Vulns { + ids = append(ids, vuln.ID) + } + return ids +} From 9062fa3b8f83ba42ce9d4bcc7c36d7578b650b16 Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 17:36:42 +0000 Subject: [PATCH 4/6] linter --- checks/raw/vulnerabilities.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/checks/raw/vulnerabilities.go b/checks/raw/vulnerabilities.go index ff76937c2ec..722a5481e0c 100644 --- a/checks/raw/vulnerabilities.go +++ b/checks/raw/vulnerabilities.go @@ -24,16 +24,19 @@ import ( func Vulnerabilities(c *checker.CheckRequest) (checker.VulnerabilitiesData, error) { commits, err := c.RepoClient.ListCommits() if err != nil { - return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "Client.Repositories.ListCommits") + return checker.VulnerabilitiesData{}, + sce.WithMessage(sce.ErrScorecardInternal, "Client.Repositories.ListCommits") } if len(commits) < 1 || commits[0].SHA == "" { - return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "no commits found") + return checker.VulnerabilitiesData{}, + sce.WithMessage(sce.ErrScorecardInternal, "no commits found") } resp, err := c.VulnerabilitiesClient.HasUnfixedVulnerabilities(c.Ctx, commits[0].SHA) if err != nil { - return checker.VulnerabilitiesData{}, sce.WithMessage(sce.ErrScorecardInternal, "VulnerabilitiesClient.HasUnfixedVulnerabilities") + return checker.VulnerabilitiesData{}, + sce.WithMessage(sce.ErrScorecardInternal, "VulnerabilitiesClient.HasUnfixedVulnerabilities") } vulnIDs := getVulnerabilities(&resp) From ebde8c714379ce941aeba6b3117e87da73098663 Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 18:14:33 +0000 Subject: [PATCH 5/6] linter --- checker/raw_result.go | 4 +++- checks/raw/vulnerabilities.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/checker/raw_result.go b/checker/raw_result.go index c36defde2f9..4f8acd8d7f9 100644 --- a/checker/raw_result.go +++ b/checker/raw_result.go @@ -119,9 +119,11 @@ type File struct { // TODO: add hash. } +// Vulnerability defines a vulnerability +// from a database. type Vulnerability struct { // For OSV: OSV-2020-484 // For CVE: CVE-2022-23945 ID string - // TODO: additional information + // Note:: add fields here if needed. } diff --git a/checks/raw/vulnerabilities.go b/checks/raw/vulnerabilities.go index 722a5481e0c..61f90b04cda 100644 --- a/checks/raw/vulnerabilities.go +++ b/checks/raw/vulnerabilities.go @@ -44,7 +44,7 @@ func Vulnerabilities(c *checker.CheckRequest) (checker.VulnerabilitiesData, erro for _, id := range vulnIDs { v := checker.Vulnerability{ ID: id, - // TODO: add fields. + // Note: add fields if needed. } vulns = append(vulns, v) } From 2ea7b6694b5b21be0d37977d370f553d374178de Mon Sep 17 00:00:00 2001 From: laurentsimon Date: Wed, 26 Jan 2022 18:31:23 +0000 Subject: [PATCH 6/6] tests --- checker/raw_result.go | 2 +- e2e/vulnerabilities_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/checker/raw_result.go b/checker/raw_result.go index 4f8acd8d7f9..3ffa5503548 100644 --- a/checker/raw_result.go +++ b/checker/raw_result.go @@ -125,5 +125,5 @@ type Vulnerability struct { // For OSV: OSV-2020-484 // For CVE: CVE-2022-23945 ID string - // Note:: add fields here if needed. + // TODO(vuln): Add additional fields, if needed. } diff --git a/e2e/vulnerabilities_test.go b/e2e/vulnerabilities_test.go index b6d87252864..42c1776afa2 100644 --- a/e2e/vulnerabilities_test.go +++ b/e2e/vulnerabilities_test.go @@ -79,7 +79,7 @@ var _ = Describe("E2E TEST:Vulnerabilities", func() { } expected := scut.TestReturn{ Error: nil, - Score: checker.MinResultScore, + Score: checker.MaxResultScore - 3, // 3 vulnerabilities remove 3 points. NumberOfWarn: 1, NumberOfInfo: 0, NumberOfDebug: 0,