diff --git a/checker/check.go b/checker/check.go deleted file mode 100644 index 3f2e75ca..00000000 --- a/checker/check.go +++ /dev/null @@ -1,105 +0,0 @@ -// 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. -// 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 checker - -type CheckResult struct { - Pass bool - Details []string - Confidence int - ShouldRetry bool - Error error -} - -var InconclusiveResult = CheckResult{ - Pass: false, - Confidence: 0, -} - -var retryResult = CheckResult{ - Pass: false, - ShouldRetry: true, -} - -const pass int = 10 - -var PassResult = CheckResult{ - Pass: true, - Confidence: pass, -} - -var maxConfidence int = 10 - -func RetryResult(err error) CheckResult { - r := retryResult - r.Error = err - return r -} - -type CheckFn func(Checker) CheckResult - -func Bool2int(b bool) int { - if b { - return 1 - } - return 0 -} - -func MultiCheck(fns ...CheckFn) CheckFn { - return func(c Checker) CheckResult { - var maxResult CheckResult - - for _, fn := range fns { - result := fn(c) - if Bool2int(result.Pass) < Bool2int(maxResult.Pass) { - continue - } - if result.Pass && result.Confidence >= maxConfidence { - return result - } - if result.Confidence >= maxResult.Confidence { - maxResult = result - } - } - return maxResult - } -} - -func ProportionalResult(numerator int, denominator int, threshold float32) CheckResult { - if numerator == 0 { - return CheckResult{ - Pass: false, - Confidence: maxConfidence, - } - } - - actual := float32(numerator) / float32(denominator) - const confidence = 10 - if actual >= threshold { - return CheckResult{ - Pass: true, - Confidence: int(actual * confidence), - } - } - - return CheckResult{ - Pass: false, - Confidence: maxConfidence - int(actual*confidence), - } -} - -type NamedCheck struct { - Name string - Fn CheckFn -} diff --git a/checker/checker.go b/checker/check_request.go similarity index 57% rename from checker/checker.go rename to checker/check_request.go index 2e5da7fa..c2bd1214 100644 --- a/checker/checker.go +++ b/checker/check_request.go @@ -16,15 +16,13 @@ package checker import ( "context" - "fmt" "net/http" - "strings" "github.com/google/go-github/v32/github" "github.com/shurcooL/githubv4" ) -type Checker struct { +type CheckRequest struct { Ctx context.Context Client *github.Client GraphClient *githubv4.Client @@ -32,34 +30,3 @@ type Checker struct { Owner, Repo string Logf func(s string, f ...interface{}) } - -type logger struct { - messages []string -} - -func (l *logger) Logf(s string, f ...interface{}) { - l.messages = append(l.messages, fmt.Sprintf(s, f...)) -} - -type Runner struct { - Checker Checker -} - -func (r *Runner) Run(f CheckFn) CheckResult { - var res CheckResult - var l logger - for retriesRemaining := 3; retriesRemaining > 0; retriesRemaining-- { - checker := r.Checker - l = logger{} - checker.Logf = l.Logf - res = f(checker) - if res.ShouldRetry && !strings.Contains(res.Error.Error(), "invalid header field value") { - checker.Logf("error, retrying: %s", res.Error) - continue - } - break - - } - res.Details = l.messages - return res -} diff --git a/checker/check_result.go b/checker/check_result.go new file mode 100644 index 00000000..8a53291d --- /dev/null +++ b/checker/check_result.go @@ -0,0 +1,79 @@ +// 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. +// 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 checker + +const MaxResultConfidence = 10 + +type CheckResult struct { + Name string + Pass bool + Confidence int + Details []string + ShouldRetry bool `json:"-"` + Error error `json:"-"` +} + +func MakeInconclusiveResult(name string) CheckResult { + return CheckResult{ + Name: name, + Pass: false, + Confidence: 0, + } +} + +func MakePassResult(name string) CheckResult { + return CheckResult{ + Name: name, + Pass: true, + Confidence: MaxResultConfidence, + } +} + +func MakeRetryResult(name string, err error) CheckResult { + return CheckResult{ + Name: name, + Pass: false, + ShouldRetry: true, + Error: err, + } +} + +func MakeProportionalResult(name string, numerator int, denominator int, + threshold float32) CheckResult { + if denominator == 0 { + return MakeInconclusiveResult(name) + } + if numerator == 0 { + return CheckResult{ + Name: name, + Pass: false, + Confidence: MaxResultConfidence, + } + } + actual := float32(numerator) / float32(denominator) + if actual >= threshold { + return CheckResult{ + Name: name, + Pass: true, + Confidence: int(actual * MaxResultConfidence), + } + } + + return CheckResult{ + Name: name, + Pass: false, + Confidence: MaxResultConfidence - int(actual*MaxResultConfidence), + } +} diff --git a/checker/check_runner.go b/checker/check_runner.go new file mode 100644 index 00000000..956728e9 --- /dev/null +++ b/checker/check_runner.go @@ -0,0 +1,84 @@ +// 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. +// 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 checker + +import ( + "fmt" + "strings" +) + +const checkRetries = 3 + +type Runner struct { + CheckRequest CheckRequest +} + +type CheckFn func(CheckRequest) CheckResult + +type CheckNameToFnMap map[string]CheckFn + +type logger struct { + messages []string +} + +func (l *logger) Logf(s string, f ...interface{}) { + l.messages = append(l.messages, fmt.Sprintf(s, f...)) +} + +func (r *Runner) Run(f CheckFn) CheckResult { + var res CheckResult + var l logger + for retriesRemaining := checkRetries; retriesRemaining > 0; retriesRemaining-- { + checkRequest := r.CheckRequest + l = logger{} + checkRequest.Logf = l.Logf + res = f(checkRequest) + if res.ShouldRetry && !strings.Contains(res.Error.Error(), "invalid header field value") { + checkRequest.Logf("error, retrying: %s", res.Error) + continue + } + break + + } + res.Details = l.messages + return res +} + +func Bool2int(b bool) int { + if b { + return 1 + } + return 0 +} + +func MultiCheck(fns ...CheckFn) CheckFn { + return func(c CheckRequest) CheckResult { + var maxResult CheckResult + + for _, fn := range fns { + result := fn(c) + if Bool2int(result.Pass) < Bool2int(maxResult.Pass) { + continue + } + if result.Pass && result.Confidence >= MaxResultConfidence { + return result + } + if result.Confidence >= maxResult.Confidence { + maxResult = result + } + } + return maxResult + } +} diff --git a/checks/active.go b/checks/active.go index 168f4249..21936481 100644 --- a/checks/active.go +++ b/checks/active.go @@ -21,16 +21,19 @@ import ( "github.com/ossf/scorecard/checker" ) -var lookbackDays int = 90 +const ( + activeStr = "Active" + lookbackDays = 90 +) func init() { - registerCheck("Active", IsActive) + registerCheck(activeStr, IsActive) } -func IsActive(c checker.Checker) checker.CheckResult { +func IsActive(c checker.CheckRequest) checker.CheckResult { commits, _, err := c.Client.Repositories.ListCommits(c.Ctx, c.Owner, c.Repo, &github.CommitsListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(activeStr, err) } tz, _ := time.LoadLocation("UTC") @@ -39,7 +42,7 @@ func IsActive(c checker.Checker) checker.CheckResult { for _, commit := range commits { commitFull, _, err := c.Client.Git.GetCommit(c.Ctx, c.Owner, c.Repo, commit.GetSHA()) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(activeStr, err) } if commitFull.GetAuthor().GetDate().After(threshold) { totalCommits++ @@ -49,6 +52,7 @@ func IsActive(c checker.Checker) checker.CheckResult { const numCommits = 2 const confidence = 10 return checker.CheckResult{ + Name: activeStr, Pass: totalCommits >= numCommits, Confidence: confidence, } diff --git a/checks/all_checks.go b/checks/all_checks.go index 638f806d..345c4792 100644 --- a/checks/all_checks.go +++ b/checks/all_checks.go @@ -16,11 +16,8 @@ package checks import "github.com/ossf/scorecard/checker" -var AllChecks = []checker.NamedCheck{} +var AllChecks = checker.CheckNameToFnMap{} func registerCheck(name string, fn checker.CheckFn) { - AllChecks = append(AllChecks, checker.NamedCheck{ - Name: name, - Fn: fn, - }) + AllChecks[name] = fn } diff --git a/checks/branch_protected.go b/checks/branch_protected.go index de8780f5..c314f597 100644 --- a/checks/branch_protected.go +++ b/checks/branch_protected.go @@ -19,27 +19,30 @@ import ( "github.com/ossf/scorecard/checker" ) +const branchProtectionStr = "Branch-Protection" + func init() { - registerCheck("Branch-Protection", BranchProtection) + registerCheck(branchProtectionStr, BranchProtection) } -func BranchProtection(c checker.Checker) checker.CheckResult { +func BranchProtection(c checker.CheckRequest) checker.CheckResult { repo, _, err := c.Client.Repositories.Get(c.Ctx, c.Owner, c.Repo) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(branchProtectionStr, err) } protection, resp, err := c.Client.Repositories. GetBranchProtection(c.Ctx, c.Owner, c.Repo, *repo.DefaultBranch) const fileNotFound = 404 if resp.StatusCode == fileNotFound { - return checker.RetryResult(err) + return checker.MakeRetryResult(branchProtectionStr, err) } if err != nil { c.Logf("!! branch protection not enabled") const confidence = 10 return checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Confidence: confidence, } @@ -48,7 +51,7 @@ func BranchProtection(c checker.Checker) checker.CheckResult { } -func IsBranchProtected(protection *github.Protection, c checker.Checker) checker.CheckResult { +func IsBranchProtected(protection *github.Protection, c checker.CheckRequest) checker.CheckResult { totalChecks := 6 totalSuccess := 0 @@ -104,5 +107,5 @@ func IsBranchProtected(protection *github.Protection, c checker.Checker) checker } } - return checker.ProportionalResult(totalSuccess, totalChecks, 1.0) + return checker.MakeProportionalResult(branchProtectionStr, totalSuccess, totalChecks, 1.0) } diff --git a/checks/branch_protected_test.go b/checks/branch_protected_test.go index a385b4c6..a131c81f 100644 --- a/checks/branch_protected_test.go +++ b/checks/branch_protected_test.go @@ -22,6 +22,7 @@ import ( "github.com/ossf/scorecard/checker" ) +// TODO: these logging functions are repeated from lib/check_fn.go. Reuse code. type log struct { messages []string } @@ -33,7 +34,7 @@ func (l *log) Logf(s string, f ...interface{}) { func TestIsBranchProtected(t *testing.T) { type args struct { protection *github.Protection - c checker.Checker + c checker.CheckRequest } l := log{} @@ -77,8 +78,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 7, @@ -120,8 +122,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 5, @@ -163,8 +166,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 7, @@ -207,8 +211,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 5, @@ -250,8 +255,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 5, @@ -293,8 +299,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 5, @@ -336,8 +343,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 9, @@ -378,8 +386,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: true, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: false, Details: nil, Confidence: 9, @@ -419,8 +428,9 @@ func TestIsBranchProtected(t *testing.T) { Enabled: false, }, }, - c: checker.Checker{Logf: l.Logf}}, + c: checker.CheckRequest{Logf: l.Logf}}, want: checker.CheckResult{ + Name: branchProtectionStr, Pass: true, Details: nil, Confidence: 10, diff --git a/checks/checkforfile.go b/checks/checkforfile.go index d091a302..28b4878c 100644 --- a/checks/checkforfile.go +++ b/checks/checkforfile.go @@ -26,30 +26,30 @@ import ( // CheckIfFileExists downloads the tar of the repository and calls the predicate to check // for the occurrence. -func CheckIfFileExists(c checker.Checker, predicate func(name string, +func CheckIfFileExists(checkName string, c checker.CheckRequest, predicate func(name string, Logf func(s string, f ...interface{})) bool) checker.CheckResult { r, _, err := c.Client.Repositories.Get(c.Ctx, c.Owner, c.Repo) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(checkName, err) } url := r.GetArchiveURL() url = strings.Replace(url, "{archive_format}", "tarball/", 1) url = strings.Replace(url, "{/ref}", r.GetDefaultBranch(), 1) - // Using the http.get instead of the checker httpClient because + // Using the http.get instead of the lib httpClient because // the default checker.HTTPClient caches everything in the memory and it causes oom. //https://securego.io/docs/rules/g107.html //nolint resp, err := http.Get(url) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(checkName, err) } defer resp.Body.Close() gz, err := gzip.NewReader(resp.Body) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(checkName, err) } tr := tar.NewReader(gz) @@ -58,7 +58,7 @@ func CheckIfFileExists(c checker.Checker, predicate func(name string, if err == io.EOF { break } else if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(checkName, err) } // Strip the repo name @@ -70,11 +70,12 @@ func CheckIfFileExists(c checker.Checker, predicate func(name string, name := names[1] if predicate(name, c.Logf) { - return checker.PassResult + return checker.MakePassResult(checkName) } } const confidence = 5 return checker.CheckResult{ + Name: checkName, Pass: false, Confidence: confidence, } diff --git a/checks/ci_tests.go b/checks/ci_tests.go index 7e1bc237..52971abe 100644 --- a/checks/ci_tests.go +++ b/checks/ci_tests.go @@ -21,16 +21,18 @@ import ( "github.com/ossf/scorecard/checker" ) +const ciTestsStr = "CI-Tests" + func init() { - registerCheck("CI-Tests", CITests) + registerCheck(ciTestsStr, CITests) } -func CITests(c checker.Checker) checker.CheckResult { +func CITests(c checker.CheckRequest) checker.CheckResult { prs, _, err := c.Client.PullRequests.List(c.Ctx, c.Owner, c.Repo, &github.PullRequestListOptions{ State: "closed", }) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciTestsStr, err) } const ( @@ -56,7 +58,7 @@ func CITests(c checker.Checker) checker.CheckResult { if usedSystem <= githubStatuses { statuses, _, err := c.Client.Repositories.ListStatuses(c.Ctx, c.Owner, c.Repo, pr.GetHead().GetSHA(), &github.ListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciTestsStr, err) } for _, status := range statuses { @@ -81,7 +83,7 @@ func CITests(c checker.Checker) checker.CheckResult { if usedSystem == githubCheckRuns || usedSystem == unknown { crs, _, err := c.Client.Checks.ListCheckRunsForRef(c.Ctx, c.Owner, c.Repo, pr.GetHead().GetSHA(), &github.ListCheckRunsOptions{}) if err != nil || crs == nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciTestsStr, err) } for _, cr := range crs.CheckRuns { @@ -107,7 +109,7 @@ func CITests(c checker.Checker) checker.CheckResult { } c.Logf("found CI tests for %d of %d merged PRs", totalTested, totalMerged) - return checker.ProportionalResult(totalTested, totalMerged, .75) + return checker.MakeProportionalResult(ciTestsStr, totalTested, totalMerged, .75) } func isTest(s string) bool { diff --git a/checks/cii_best_practices.go b/checks/cii_best_practices.go index d2a6e7d0..be5ce65d 100644 --- a/checks/cii_best_practices.go +++ b/checks/cii_best_practices.go @@ -22,39 +22,41 @@ import ( "github.com/ossf/scorecard/checker" ) +const ciiBestPracticesStr = "CII-Best-Practices" + func init() { - registerCheck("CII-Best-Practices", CIIBestPractices) + registerCheck(ciiBestPracticesStr, CIIBestPractices) } type response struct { BadgeLevel string `json:"badge_level"` } -func CIIBestPractices(c checker.Checker) checker.CheckResult { +func CIIBestPractices(c checker.CheckRequest) checker.CheckResult { repoUrl := fmt.Sprintf("https://github.com/%s/%s", c.Owner, c.Repo) url := fmt.Sprintf("https://bestpractices.coreinfrastructure.org/projects.json?url=%s", repoUrl) resp, err := c.HttpClient.Get(url) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciiBestPracticesStr, err) } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciiBestPracticesStr, err) } parsedResponse := []response{} if err := json.Unmarshal(b, &parsedResponse); err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(ciiBestPracticesStr, err) } - const confidence = 10 if len(parsedResponse) < 1 { c.Logf("no badge found") return checker.CheckResult{ + Name: ciiBestPracticesStr, Pass: false, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } @@ -63,13 +65,15 @@ func CIIBestPractices(c checker.Checker) checker.CheckResult { if result.BadgeLevel != "" { return checker.CheckResult{ + Name: ciiBestPracticesStr, Pass: true, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } return checker.CheckResult{ + Name: ciiBestPracticesStr, Pass: false, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } diff --git a/checks/code_review.go b/checks/code_review.go index 329bf9e6..eafdc1bc 100644 --- a/checks/code_review.go +++ b/checks/code_review.go @@ -21,8 +21,10 @@ import ( "github.com/ossf/scorecard/checker" ) +const codeReviewStr = "Code-Review" + func init() { - registerCheck("Code-Review", DoesCodeReview) + registerCheck(codeReviewStr, DoesCodeReview) } // DoesCodeReview attempts to determine whether a project requires review before code gets merged. @@ -30,7 +32,7 @@ func init() { // - Looking at the repo configuration to see if reviews are required // - Checking if most of the recent merged PRs were "Approved" // - Looking for other well-known review labels -func DoesCodeReview(c checker.Checker) checker.CheckResult { +func DoesCodeReview(c checker.CheckRequest) checker.CheckResult { return checker.MultiCheck( IsPrReviewRequired, GithubCodeReview, @@ -39,13 +41,13 @@ func DoesCodeReview(c checker.Checker) checker.CheckResult { )(c) } -func GithubCodeReview(c checker.Checker) checker.CheckResult { +func GithubCodeReview(c checker.CheckRequest) checker.CheckResult { // Look at some merged PRs to see if they were reviewed prs, _, err := c.Client.PullRequests.List(c.Ctx, c.Owner, c.Repo, &github.PullRequestListOptions{ State: "closed", }) if err != nil { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } totalMerged := 0 @@ -91,39 +93,40 @@ func GithubCodeReview(c checker.Checker) checker.CheckResult { if totalReviewed > 0 { c.Logf("github code reviews found") } - return checker.ProportionalResult(totalReviewed, totalMerged, .75) + return checker.MakeProportionalResult(codeReviewStr, totalReviewed, totalMerged, .75) } -func IsPrReviewRequired(c checker.Checker) checker.CheckResult { +func IsPrReviewRequired(c checker.CheckRequest) checker.CheckResult { // Look to see if review is enforced. r, _, err := c.Client.Repositories.Get(c.Ctx, c.Owner, c.Repo) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(codeReviewStr, err) } // Check the branch protection rules, we may not be able to get these though. bp, _, err := c.Client.Repositories.GetBranchProtection(c.Ctx, c.Owner, c.Repo, r.GetDefaultBranch()) if err != nil { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } if bp.GetRequiredPullRequestReviews().RequiredApprovingReviewCount >= 1 { c.Logf("pr review policy enforced") const confidence = 5 return checker.CheckResult{ + Name: codeReviewStr, Pass: true, Confidence: confidence, } } - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } -func ProwCodeReview(c checker.Checker) checker.CheckResult { +func ProwCodeReview(c checker.CheckRequest) checker.CheckResult { // Look at some merged PRs to see if they were reviewed prs, _, err := c.Client.PullRequests.List(c.Ctx, c.Owner, c.Repo, &github.PullRequestListOptions{ State: "closed", }) if err != nil { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } totalMerged := 0 @@ -142,16 +145,16 @@ func ProwCodeReview(c checker.Checker) checker.CheckResult { } if totalReviewed == 0 { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } c.Logf("prow code reviews found") - return checker.ProportionalResult(totalReviewed, totalMerged, .75) + return checker.MakeProportionalResult(codeReviewStr, totalReviewed, totalMerged, .75) } -func CommitMessageHints(c checker.Checker) checker.CheckResult { +func CommitMessageHints(c checker.CheckRequest) checker.CheckResult { commits, _, err := c.Client.Repositories.ListCommits(c.Ctx, c.Owner, c.Repo, &github.CommitsListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(codeReviewStr, err) } total := 0 @@ -182,8 +185,8 @@ func CommitMessageHints(c checker.Checker) checker.CheckResult { } if totalReviewed == 0 { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(codeReviewStr) } c.Logf("code reviews found") - return checker.ProportionalResult(totalReviewed, total, .75) + return checker.MakeProportionalResult(codeReviewStr, totalReviewed, total, .75) } diff --git a/checks/contributors.go b/checks/contributors.go index 3ad72a99..dc6e7948 100644 --- a/checks/contributors.go +++ b/checks/contributors.go @@ -21,24 +21,29 @@ import ( "github.com/ossf/scorecard/checker" ) +const ( + minContributionsPerUser = 5 + minOrganizationCount = 2 + contributorsStr = "Contributors" +) + func init() { - registerCheck("Contributors", Contributors) + registerCheck(contributorsStr, Contributors) } -func Contributors(c checker.Checker) checker.CheckResult { +func Contributors(c checker.CheckRequest) checker.CheckResult { contribs, _, err := c.Client.Repositories.ListContributors(c.Ctx, c.Owner, c.Repo, &github.ListContributorsOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(contributorsStr, err) } companies := map[string]struct{}{} for _, contrib := range contribs { - const contributorsCount = 5 //nolint:nestif - if contrib.GetContributions() >= contributorsCount { + if contrib.GetContributions() >= minContributionsPerUser { u, _, err := c.Client.Users.Get(c.Ctx, contrib.GetLogin()) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(contributorsStr, err) } orgs, _, err := c.Client.Organizations.List(c.Ctx, contrib.GetLogin(), nil) if err != nil { @@ -65,16 +70,16 @@ func Contributors(c checker.Checker) checker.CheckResult { names = append(names, c) } c.Logf("companies found: %v", strings.Join(names, ",")) - const numContributors = 2 - const confidence = 10 - if len(companies) >= numContributors { + if len(companies) >= minOrganizationCount { return checker.CheckResult{ + Name: contributorsStr, Pass: true, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } return checker.CheckResult{ + Name: contributorsStr, Pass: false, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } diff --git a/checks/frozen_deps.go b/checks/frozen_deps.go index 59a96c36..bca59101 100644 --- a/checks/frozen_deps.go +++ b/checks/frozen_deps.go @@ -20,13 +20,15 @@ import ( "github.com/ossf/scorecard/checker" ) +const frozenDepsStr = "Frozen-Deps" + func init() { - registerCheck("Frozen-Deps", FrozenDeps) + registerCheck(frozenDepsStr, FrozenDeps) } // FrozenDeps will check the repository if it contains frozen dependecies. -func FrozenDeps(c checker.Checker) checker.CheckResult { - return CheckIfFileExists(c, filePredicate) +func FrozenDeps(c checker.CheckRequest) checker.CheckResult { + return CheckIfFileExists(frozenDepsStr, c, filePredicate) } // filePredicate will validate the if frozen dependecies file name exists. diff --git a/checks/fuzzing.go b/checks/fuzzing.go index 14957790..938f51f3 100644 --- a/checks/fuzzing.go +++ b/checks/fuzzing.go @@ -21,29 +21,32 @@ import ( "github.com/ossf/scorecard/checker" ) +const fuzzingStr = "Fuzzing" + func init() { - registerCheck("Fuzzing", Fuzzing) + registerCheck(fuzzingStr, Fuzzing) } -func Fuzzing(c checker.Checker) checker.CheckResult { +func Fuzzing(c checker.CheckRequest) checker.CheckResult { url := fmt.Sprintf("github.com/%s/%s", c.Owner, c.Repo) searchString := url + " repo:google/oss-fuzz in:file filename:project.yaml" results, _, err := c.Client.Search.Code(c.Ctx, searchString, &github.SearchOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(fuzzingStr, err) } - const confidence = 10 if *results.Total > 0 { c.Logf("found project in OSS-Fuzz") return checker.CheckResult{ + Name: fuzzingStr, Pass: true, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } return checker.CheckResult{ + Name: fuzzingStr, Pass: false, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } diff --git a/checks/packaging.go b/checks/packaging.go index 3a12f963..74f02950 100644 --- a/checks/packaging.go +++ b/checks/packaging.go @@ -23,22 +23,23 @@ import ( "github.com/ossf/scorecard/checker" ) +const packagingStr = "Packaging" + func init() { - registerCheck("Packaging", Packaging) + registerCheck(packagingStr, Packaging) } -func Packaging(c checker.Checker) checker.CheckResult { +func Packaging(c checker.CheckRequest) checker.CheckResult { _, dc, _, err := c.Client.Repositories.GetContents(c.Ctx, c.Owner, c.Repo, ".github/workflows", &github.RepositoryContentGetOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(packagingStr, err) } - const confidence = 10 for _, f := range dc { fp := f.GetPath() fo, _, _, err := c.Client.Repositories.GetContents(c.Ctx, c.Owner, c.Repo, fp, &github.RepositoryContentGetOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(packagingStr, err) } if fo == nil { // path is a directory, not a file. skip. @@ -46,7 +47,7 @@ func Packaging(c checker.Checker) checker.CheckResult { } fc, err := fo.GetContent() if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(packagingStr, err) } if !isPackagingWorkflow(fc, fp, c) { @@ -57,25 +58,27 @@ func Packaging(c checker.Checker) checker.CheckResult { Status: "success", }) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(packagingStr, err) } if *runs.TotalCount > 0 { c.Logf("found a completed run: %s", runs.WorkflowRuns[0].GetHTMLURL()) return checker.CheckResult{ + Name: packagingStr, Pass: true, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } c.Logf("!! no run completed") } return checker.CheckResult{ + Name: packagingStr, Pass: false, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } -func isPackagingWorkflow(s string, fp string, c checker.Checker) bool { +func isPackagingWorkflow(s string, fp string, c checker.CheckRequest) bool { // nodejs packages if strings.Contains(s, "uses: actions/setup-node@") { r1, _ := regexp.Compile(`(?s)registry-url.*https://registry\.npmjs\.org`) diff --git a/checks/pull_requests.go b/checks/pull_requests.go index 43a86704..48657598 100644 --- a/checks/pull_requests.go +++ b/checks/pull_requests.go @@ -21,14 +21,16 @@ import ( "github.com/ossf/scorecard/checker" ) +const pullRequestsStr = "Pull-Requests" + func init() { - registerCheck("Pull-Requests", PullRequests) + registerCheck(pullRequestsStr, PullRequests) } -func PullRequests(c checker.Checker) checker.CheckResult { +func PullRequests(c checker.CheckRequest) checker.CheckResult { commits, _, err := c.Client.Repositories.ListCommits(c.Ctx, c.Owner, c.Repo, &github.CommitsListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(pullRequestsStr, err) } total := 0 @@ -59,7 +61,7 @@ func PullRequests(c checker.Checker) checker.CheckResult { prs, _, err := c.Client.PullRequests.ListPullRequestsWithCommit(c.Ctx, c.Owner, c.Repo, commit.GetSHA(), &github.PullRequestListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(pullRequestsStr, err) } if len(prs) > 0 { totalWithPrs++ @@ -69,5 +71,5 @@ func PullRequests(c checker.Checker) checker.CheckResult { } } c.Logf("found PRs for %d out of %d commits", totalWithPrs, total) - return checker.ProportionalResult(totalWithPrs, total, .75) + return checker.MakeProportionalResult(pullRequestsStr, totalWithPrs, total, .75) } diff --git a/checks/sast.go b/checks/sast.go index b7c38daa..c87af892 100644 --- a/checks/sast.go +++ b/checks/sast.go @@ -19,25 +19,27 @@ import ( "github.com/ossf/scorecard/checker" ) +const sastStr = "SAST" + var sastTools map[string]bool = map[string]bool{"github-code-scanning": true, "sonarcloud": true} func init() { - registerCheck("SAST", SAST) + registerCheck(sastStr, SAST) } -func SAST(c checker.Checker) checker.CheckResult { +func SAST(c checker.CheckRequest) checker.CheckResult { return checker.MultiCheck( CodeQLInCheckDefinitions, SASTToolInCheckRuns, )(c) } -func SASTToolInCheckRuns(c checker.Checker) checker.CheckResult { +func SASTToolInCheckRuns(c checker.CheckRequest) checker.CheckResult { prs, _, err := c.Client.PullRequests.List(c.Ctx, c.Owner, c.Repo, &github.PullRequestListOptions{ State: "closed", }) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(sastStr, err) } totalMerged := 0 @@ -49,10 +51,10 @@ func SASTToolInCheckRuns(c checker.Checker) checker.CheckResult { totalMerged++ crs, _, err := c.Client.Checks.ListCheckRunsForRef(c.Ctx, c.Owner, c.Repo, pr.GetHead().GetSHA(), &github.ListCheckRunsOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(sastStr, err) } if crs == nil { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(sastStr) } for _, cr := range crs.CheckRuns { if cr.GetStatus() != "completed" { @@ -69,25 +71,25 @@ func SASTToolInCheckRuns(c checker.Checker) checker.CheckResult { } } if totalTested == 0 { - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(sastStr) } - return checker.ProportionalResult(totalTested, totalMerged, .75) + return checker.MakeProportionalResult(sastStr, totalTested, totalMerged, .75) } -func CodeQLInCheckDefinitions(c checker.Checker) checker.CheckResult { +func CodeQLInCheckDefinitions(c checker.CheckRequest) checker.CheckResult { searchQuery := ("github/codeql-action path:/.github/workflows repo:" + c.Owner + "/" + c.Repo) results, _, err := c.Client.Search.Code(c.Ctx, searchQuery, &github.SearchOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(sastStr, err) } for _, result := range results.CodeResults { c.Logf("found CodeQL definition: %s", result.GetPath()) } - const confidence = 10 return checker.CheckResult{ + Name: sastStr, Pass: *results.Total > 0, - Confidence: confidence, + Confidence: checker.MaxResultConfidence, } } diff --git a/checks/security_policy.go b/checks/security_policy.go index e4901108..285d7f24 100644 --- a/checks/security_policy.go +++ b/checks/security_policy.go @@ -20,13 +20,15 @@ import ( "github.com/ossf/scorecard/checker" ) +const securityPolicyStr = "Security-Policy" + func init() { - registerCheck("Security-Policy", SecurityPolicy) + registerCheck(securityPolicyStr, SecurityPolicy) } -func SecurityPolicy(c checker.Checker) checker.CheckResult { +func SecurityPolicy(c checker.CheckRequest) checker.CheckResult { // check repository for repository-specific policy - result := CheckIfFileExists(c, func(name string, logf func(s string, f ...interface{})) bool { + result := CheckIfFileExists(securityPolicyStr, c, func(name string, logf func(s string, f ...interface{})) bool { if strings.EqualFold(name, "security.md") { logf("security policy : %s", name) return true @@ -43,7 +45,7 @@ func SecurityPolicy(c checker.Checker) checker.CheckResult { dotGitHub := c dotGitHub.Repo = ".github" - return CheckIfFileExists(dotGitHub, func(name string, logf func(s string, f ...interface{})) bool { + return CheckIfFileExists(securityPolicyStr, dotGitHub, func(name string, logf func(s string, f ...interface{})) bool { if strings.EqualFold(name, "security.md") { logf("security policy within .github folder : %s", name) return true diff --git a/checks/signed_releases.go b/checks/signed_releases.go index a7500b30..5c193a40 100644 --- a/checks/signed_releases.go +++ b/checks/signed_releases.go @@ -21,16 +21,19 @@ import ( "github.com/ossf/scorecard/checker" ) -var releaseLookBack int = 5 +const ( + signedReleasesStr = "Signed-Releases" + releaseLookBackDays = 5 +) func init() { - registerCheck("Signed-Releases", SignedReleases) + registerCheck(signedReleasesStr, SignedReleases) } -func SignedReleases(c checker.Checker) checker.CheckResult { +func SignedReleases(c checker.CheckRequest) checker.CheckResult { releases, _, err := c.Client.Repositories.ListReleases(c.Ctx, c.Owner, c.Repo, &github.ListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(signedReleasesStr, err) } artifactExtensions := []string{".asc", ".minisig", ".sig"} @@ -40,7 +43,7 @@ func SignedReleases(c checker.Checker) checker.CheckResult { for _, r := range releases { assets, _, err := c.Client.Repositories.ListReleaseAssets(c.Ctx, c.Owner, c.Repo, r.GetID(), &github.ListOptions{}) if err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(signedReleasesStr, err) } if len(assets) == 0 { continue @@ -64,16 +67,16 @@ func SignedReleases(c checker.Checker) checker.CheckResult { if !signed { c.Logf("!! release %s has no signed artifacts", r.GetTagName()) } - if totalReleases > releaseLookBack { + if totalReleases > releaseLookBackDays { break } } if totalReleases == 0 { c.Logf("no releases found") - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(signedReleasesStr) } c.Logf("found signed artifacts for %d out of %d releases", totalSigned, totalReleases) - return checker.ProportionalResult(totalSigned, totalReleases, 0.8) + return checker.MakeProportionalResult(signedReleasesStr, totalSigned, totalReleases, 0.8) } diff --git a/checks/signed_tags.go b/checks/signed_tags.go index 260d0c1e..4318e81f 100644 --- a/checks/signed_tags.go +++ b/checks/signed_tags.go @@ -19,13 +19,16 @@ import ( "github.com/shurcooL/githubv4" ) -var tagLookBack int = 5 +const ( + signedTagsStr = "Signed-Tags" + tagLookBack = 5 +) func init() { - registerCheck("Signed-Tags", SignedTags) + registerCheck(signedTagsStr, SignedTags) } -func SignedTags(c checker.Checker) checker.CheckResult { +func SignedTags(c checker.CheckRequest) checker.CheckResult { type ref struct { Name githubv4.String Target struct { @@ -47,7 +50,7 @@ func SignedTags(c checker.Checker) checker.CheckResult { } if err := c.GraphClient.Query(c.Ctx, &query, variables); err != nil { - return checker.RetryResult(err) + return checker.MakeRetryResult(signedTagsStr, err) } totalTags := 0 totalSigned := 0 @@ -69,9 +72,9 @@ func SignedTags(c checker.Checker) checker.CheckResult { if totalTags == 0 { c.Logf("no tags found") - return checker.InconclusiveResult + return checker.MakeInconclusiveResult(signedTagsStr) } c.Logf("found %d out of %d verified tags", totalSigned, totalTags) - return checker.ProportionalResult(totalSigned, totalTags, 0.8) + return checker.MakeProportionalResult(signedTagsStr, totalSigned, totalTags, 0.8) } diff --git a/cmd/root.go b/cmd/root.go index b8685844..8feb5828 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -68,7 +68,7 @@ or ./scorecard --{npm,pypi,rubgems}= [--checks=check1,...] [--show defer logger.Sync() // flushes buffer, if any sugar := logger.Sugar() - var outputFn func([]pkg.Result) + var outputFn func([]checker.CheckResult) switch format { case formatCSV: outputFn = outputCSV @@ -110,30 +110,26 @@ or ./scorecard --{npm,pypi,rubgems}= [--checks=check1,...] [--show } } - enabledChecks := []checker.NamedCheck{} + enabledChecks := checker.CheckNameToFnMap{} if len(checksToRun) != 0 { - checkNames := map[string]struct{}{} - for _, s := range checksToRun { - checkNames[s] = struct{}{} - } - for _, c := range checks.AllChecks { - if _, ok := checkNames[c.Name]; ok { - enabledChecks = append(enabledChecks, c) + for _, checkToRun := range checksToRun { + if checkFn, ok := checks.AllChecks[checkToRun]; ok { + enabledChecks[checkToRun] = checkFn } } } else { enabledChecks = checks.AllChecks } - for _, c := range enabledChecks { + for checkName := range enabledChecks { if format == formatDefault { - fmt.Fprintf(os.Stderr, "Starting [%s]\n", c.Name) + fmt.Fprintf(os.Stderr, "Starting [%s]\n", checkName) } } ctx := context.Background() resultsCh := pkg.RunScorecards(ctx, sugar, repo, enabledChecks) // Collect results - results := []pkg.Result{} + results := []checker.CheckResult{} for result := range resultsCh { if format == formatDefault { fmt.Fprintf(os.Stderr, "Finished [%s]\n", result.Name) @@ -150,13 +146,6 @@ or ./scorecard --{npm,pypi,rubgems}= [--checks=check1,...] [--show }, } -type checkResult struct { - CheckName string - Pass bool - Confidence int - Details []string -} - type npmSearchResults struct { Objects []struct { Package struct { @@ -182,11 +171,11 @@ type rubyGemsSearchResults struct { type record struct { Repo string Date string - Checks []checkResult + Checks []checker.CheckResult MetaData []string } -func outputJSON(results []pkg.Result) { +func outputJSON(results []checker.CheckResult) { d := time.Now() or := record{ Repo: repo.String(), @@ -195,16 +184,15 @@ func outputJSON(results []pkg.Result) { } for _, r := range results { - var details []string - if showDetails { - details = r.Cr.Details + tmpResult := checker.CheckResult{ + Name: r.Name, + Pass: r.Pass, + Confidence: r.Confidence, } - or.Checks = append(or.Checks, checkResult{ - CheckName: r.Name, - Pass: r.Cr.Pass, - Confidence: r.Cr.Confidence, - Details: details, - }) + if showDetails { + tmpResult.Details = r.Details + } + or.Checks = append(or.Checks, tmpResult) } output, err := json.Marshal(or) if err != nil { @@ -213,13 +201,13 @@ func outputJSON(results []pkg.Result) { fmt.Println(string(output)) } -func outputCSV(results []pkg.Result) { +func outputCSV(results []checker.CheckResult) { w := csv.NewWriter(os.Stdout) record := []string{repo.String()} columns := []string{"Repository"} for _, r := range results { columns = append(columns, r.Name+"-Pass", r.Name+"-Confidence") - record = append(record, strconv.FormatBool(r.Cr.Pass), strconv.Itoa(r.Cr.Confidence)) + record = append(record, strconv.FormatBool(r.Pass), strconv.Itoa(r.Confidence)) } fmt.Fprintln(os.Stderr, "CSV COLUMN NAMES") fmt.Fprintf(os.Stderr, "%s\n", strings.Join(columns, ",")) @@ -229,14 +217,14 @@ func outputCSV(results []pkg.Result) { w.Flush() } -func outputDefault(results []pkg.Result) { +func outputDefault(results []checker.CheckResult) { fmt.Println() fmt.Println("RESULTS") fmt.Println("-------") for _, r := range results { - fmt.Println(r.Name+":", displayResult(r.Cr.Pass), r.Cr.Confidence) + fmt.Println(r.Name+":", displayResult(r.Pass), r.Confidence) if showDetails { - for _, d := range r.Cr.Details { + for _, d := range r.Details { fmt.Println(" " + d) } } @@ -352,8 +340,8 @@ func init() { rootCmd.Flags().BoolVar(&showDetails, "show-details", false, "show extra details about each check") checkNames := []string{} - for _, c := range checks.AllChecks { - checkNames = append(checkNames, c.Name) + for checkName := range checks.AllChecks { + checkNames = append(checkNames, checkName) } rootCmd.Flags().StringSliceVar(&checksToRun, "checks", []string{}, fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ","))) diff --git a/cmd/serve.go b/cmd/serve.go index ca3ca4c5..60a5da78 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -24,6 +24,7 @@ import ( "strings" "time" + "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" "github.com/ossf/scorecard/pkg" "github.com/spf13/cobra" @@ -101,7 +102,7 @@ var serveCmd = &cobra.Command{ } // encodeJson encodes the result to json -func encodeJson(repo string, results []pkg.Result) ([]byte, error) { +func encodeJson(repo string, results []checker.CheckResult) ([]byte, error) { d := time.Now() or := record{ Repo: repo, @@ -109,16 +110,15 @@ func encodeJson(repo string, results []pkg.Result) ([]byte, error) { } for _, r := range results { - var details []string - if showDetails { - details = r.Cr.Details + tmpResult := checker.CheckResult{ + Name: r.Name, + Pass: r.Pass, + Confidence: r.Confidence, } - or.Checks = append(or.Checks, checkResult{ - CheckName: r.Name, - Pass: r.Cr.Pass, - Confidence: r.Cr.Confidence, - Details: details, - }) + if showDetails { + tmpResult.Details = r.Details + } + or.Checks = append(or.Checks, tmpResult) } output, err := json.Marshal(or) if err != nil { @@ -129,7 +129,7 @@ func encodeJson(repo string, results []pkg.Result) ([]byte, error) { type tc struct { URL string - Results []pkg.Result + Results []checker.CheckResult } const tpl = ` @@ -142,7 +142,7 @@ const tpl = ` {{range .Results}}
-

{{ .Name }}: {{ .Cr.Pass }}

+

{{ .Name }}: {{ .Pass }}

{{end}} diff --git a/cron/cron b/cron/cron new file mode 100755 index 00000000..fe9b382e Binary files /dev/null and b/cron/cron differ diff --git a/e2e/active_test.go b/e2e/active_test.go index dcbecdcf..5c8a2164 100644 --- a/e2e/active_test.go +++ b/e2e/active_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:Active", func() { Context("E2E TEST:Validating active status", func() { It("Should return valid active status", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:Active", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.IsActive(checker) + result := checks.IsActive(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/branchprotection_test.go b/e2e/branchprotection_test.go index a7f61550..894c6d23 100644 --- a/e2e/branchprotection_test.go +++ b/e2e/branchprotection_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:Branch Protection", func() { Context("E2E TEST:Validating branch protection", func() { It("Should fail to return branch protection on other repositories", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:Branch Protection", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.BranchProtection(checker) + result := checks.BranchProtection(checkRequest) Expect(result.Error).ShouldNot(BeNil()) Expect(result.Pass).Should(BeFalse()) }) diff --git a/e2e/ci_tests_test.go b/e2e/ci_tests_test.go index 52faf43c..75b07826 100644 --- a/e2e/ci_tests_test.go +++ b/e2e/ci_tests_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:CITests", func() { Context("E2E TEST:Validating use of CI tests", func() { It("Should return use of CI tests", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:CITests", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.CITests(checker) + result := checks.CITests(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/cii_best_practices_test.go b/e2e/cii_best_practices_test.go index 308c626b..ad90c22c 100644 --- a/e2e/cii_best_practices_test.go +++ b/e2e/cii_best_practices_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:CIIBestPractices", func() { Context("E2E TEST:Validating use of CII Best Practices", func() { It("Should return use of CII Best Practices", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:CIIBestPractices", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.CIIBestPractices(checker) + result := checks.CIIBestPractices(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/code_review_test.go b/e2e/code_review_test.go index 35d9b6fa..f599340d 100644 --- a/e2e/code_review_test.go +++ b/e2e/code_review_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:CodeReview", func() { Context("E2E TEST:Validating use of code reviews", func() { It("Should return use of code reviews", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:CodeReview", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.DoesCodeReview(checker) + result := checks.DoesCodeReview(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/contributors_test.go b/e2e/contributors_test.go index 17bb532e..8dd81fb5 100644 --- a/e2e/contributors_test.go +++ b/e2e/contributors_test.go @@ -27,7 +27,7 @@ var _ = Describe("E2E TEST:CodeReview", func() { Context("E2E TEST:Validating project contributors", func() { It("Should return valid project contributors", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,13 +36,13 @@ var _ = Describe("E2E TEST:CodeReview", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.Contributors(checker) + result := checks.Contributors(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) It("Should return valid project contributors", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -51,7 +51,7 @@ var _ = Describe("E2E TEST:CodeReview", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.Contributors(checker) + result := checks.Contributors(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/frozen_deps_test.go b/e2e/frozen_deps_test.go index 3473394f..dcc04ec9 100644 --- a/e2e/frozen_deps_test.go +++ b/e2e/frozen_deps_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:FrozenDeps", func() { Context("E2E TEST:Validating deps are frozen", func() { It("Should return deps are frozen", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:FrozenDeps", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.FrozenDeps(checker) + result := checks.FrozenDeps(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/fuzzing_test.go b/e2e/fuzzing_test.go index 58f7d7c4..6aca7f90 100644 --- a/e2e/fuzzing_test.go +++ b/e2e/fuzzing_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:Fuzzing", func() { Context("E2E TEST:Validating use of fuzzing tools", func() { It("Should return use of fuzzing tools", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:Fuzzing", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.Fuzzing(checker) + result := checks.Fuzzing(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/packaging_test.go b/e2e/packaging_test.go index 1d50713c..05aa4975 100644 --- a/e2e/packaging_test.go +++ b/e2e/packaging_test.go @@ -27,7 +27,7 @@ var _ = Describe("E2E TEST:Packaging", func() { Context("E2E TEST:Validating use of packaging in CI/CD", func() { It("Should return use of packaging in CI/CD", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,13 +36,13 @@ var _ = Describe("E2E TEST:Packaging", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.Packaging(checker) + result := checks.Packaging(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) It("Should return use of packaging in CI/CD for scorecard", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -51,7 +51,7 @@ var _ = Describe("E2E TEST:Packaging", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.Packaging(checker) + result := checks.Packaging(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/pull_requests_test.go b/e2e/pull_requests_test.go index 2749b970..2cba289a 100644 --- a/e2e/pull_requests_test.go +++ b/e2e/pull_requests_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:PullRequests", func() { Context("E2E TEST:Validating use of pull requests", func() { It("Should return use of pull requests", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:PullRequests", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.PullRequests(checker) + result := checks.PullRequests(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/sast_test.go b/e2e/sast_test.go index 283d7e75..4b1ab5e1 100644 --- a/e2e/sast_test.go +++ b/e2e/sast_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:SAST", func() { Context("E2E TEST:Validating use of SAST tools", func() { It("Should return use of SAST tools", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:SAST", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.SAST(checker) + result := checks.SAST(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/security_policy_test.go b/e2e/security_policy_test.go index 51785ff6..fad1bd0b 100644 --- a/e2e/security_policy_test.go +++ b/e2e/security_policy_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:SecurityPolicy", func() { Context("E2E TEST:Validating security policy", func() { It("Should return valid security policy", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:SecurityPolicy", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.SecurityPolicy(checker) + result := checks.SecurityPolicy(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/signedreleases_test.go b/e2e/signedreleases_test.go index 99a493fb..d22d1640 100644 --- a/e2e/signedreleases_test.go +++ b/e2e/signedreleases_test.go @@ -19,15 +19,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:Signedreleases", func() { Context("E2E TEST:Validating signed releases", func() { It("Should return valid signed releases", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -36,7 +36,7 @@ var _ = Describe("E2E TEST:Signedreleases", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.SignedReleases(checker) + result := checks.SignedReleases(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/e2e/signedtags_test.go b/e2e/signedtags_test.go index 8d7ff6f4..ece9c60f 100644 --- a/e2e/signedtags_test.go +++ b/e2e/signedtags_test.go @@ -20,15 +20,15 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/ossf/scorecard/checker" "github.com/ossf/scorecard/checks" + "github.com/ossf/scorecard/checker" ) var _ = Describe("E2E TEST:Signedtags", func() { Context("E2E TEST:Validating signed tags", func() { It("Should return valid signed tags", func() { l := log{} - checker := checker.Checker{ + checkRequest := checker.CheckRequest{ Ctx: context.Background(), Client: ghClient, HttpClient: client, @@ -37,7 +37,7 @@ var _ = Describe("E2E TEST:Signedtags", func() { GraphClient: graphClient, Logf: l.Logf, } - result := checks.SignedTags(checker) + result := checks.SignedTags(checkRequest) Expect(result.Error).Should(BeNil()) Expect(result.Pass).Should(BeTrue()) }) diff --git a/go.mod b/go.mod index 082d5886..6973ba31 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( go.uber.org/zap v1.16.0 gocloud.dev v0.22.0 golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19 - golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c // indirect + golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect + golang.org/x/tools v0.1.0 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 972ad2f5..589d1dd2 100644 --- a/go.sum +++ b/go.sum @@ -566,8 +566,10 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -636,8 +638,8 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201202200335-bef1c476418a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201203202102-a1a1cbeaa516/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c h1:dS09fXwOFF9cXBnIzZexIuUBj95U1NyQjkEhkgidDow= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/scorecard.go b/pkg/scorecard.go index 21268101..ff904e09 100644 --- a/pkg/scorecard.go +++ b/pkg/scorecard.go @@ -30,12 +30,6 @@ import ( "go.uber.org/zap" ) -type Result struct { - Cr checker.CheckResult - Name string - MetaData []string -} - type RepoURL struct { Host, Owner, Repo string } @@ -80,11 +74,11 @@ func (r *RepoURL) Set(s string) error { } func RunScorecards(ctx context.Context, logger *zap.SugaredLogger, - repo RepoURL, checksToRun []checker.NamedCheck) <-chan Result { - resultsCh := make(chan Result) + repo RepoURL, checksToRun checker.CheckNameToFnMap) <-chan checker.CheckResult { + resultsCh := make(chan checker.CheckResult) wg := sync.WaitGroup{} - for _, check := range checksToRun { - check := check + for _, checkFn := range checksToRun { + checkFn := checkFn wg.Add(1) go func() { // Use our custom roundtripper @@ -96,7 +90,7 @@ func RunScorecards(ctx context.Context, logger *zap.SugaredLogger, ghClient := github.NewClient(client) graphClient := githubv4.NewClient(client) - c := checker.Checker{ + c := checker.CheckRequest{ Ctx: ctx, Client: ghClient, HttpClient: client, @@ -105,12 +99,8 @@ func RunScorecards(ctx context.Context, logger *zap.SugaredLogger, GraphClient: graphClient, } defer wg.Done() - runner := checker.Runner{Checker: c} - r := runner.Run(check.Fn) - resultsCh <- Result{ - Name: check.Name, - Cr: r, - } + runner := checker.Runner{CheckRequest: c} + resultsCh <- runner.Run(checkFn) }() } go func() {