diff --git a/checks/evaluation/dependency_update_tool_test.go b/checks/evaluation/dependency_update_tool_test.go index cd477762..98be6cee 100644 --- a/checks/evaluation/dependency_update_tool_test.go +++ b/checks/evaluation/dependency_update_tool_test.go @@ -123,6 +123,10 @@ func TestDependencyUpdateTool(t *testing.T) { Probe: "toolSonatypeLiftInstalled", Outcome: finding.OutcomePositive, }, + { + Probe: "toolRenovateInstalled", + Outcome: finding.OutcomeNegative, + }, }, want: checker.CheckResult{ Score: 10, @@ -153,7 +157,50 @@ func TestDependencyUpdateTool(t *testing.T) { }, }, { - name: "empty tool list", + name: "missing probes renovate", + findings: []finding.Finding{ + { + Probe: "toolDependabotInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolPyUpInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolSonatypeInstalled", + Outcome: finding.OutcomeNegative, + }, + }, + err: true, + want: checker.CheckResult{ + Score: -1, + }, + }, + { + name: "invalid probe name", + findings: []finding.Finding{ + { + Probe: "toolDependabotInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolRenovateInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolPyUpInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolSonatypeInstalled", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "toolInvalidProbeName", + Outcome: finding.OutcomeNegative, + }, + }, want: checker.CheckResult{ Score: -1, }, diff --git a/checks/evaluation/fuzzing.go b/checks/evaluation/fuzzing.go index 90695ff7..8fe621ed 100644 --- a/checks/evaluation/fuzzing.go +++ b/checks/evaluation/fuzzing.go @@ -15,40 +15,44 @@ package evaluation import ( - "fmt" - "github.com/ossf/scorecard/v4/checker" sce "github.com/ossf/scorecard/v4/errors" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/fuzzedWithClusterFuzzLite" + "github.com/ossf/scorecard/v4/probes/fuzzedWithGoNative" + "github.com/ossf/scorecard/v4/probes/fuzzedWithOSSFuzz" + "github.com/ossf/scorecard/v4/probes/fuzzedWithOneFuzz" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedHaskell" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedJavascript" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedTypescript" ) // Fuzzing applies the score policy for the Fuzzing check. -func Fuzzing(name string, dl checker.DetailLogger, - r *checker.FuzzingData, +func Fuzzing(name string, + findings []finding.Finding, ) checker.CheckResult { - if r == nil { - e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data") + // We have 7 unique probes, each should have a finding. + expectedProbes := []string{ + fuzzedWithClusterFuzzLite.Probe, + fuzzedWithGoNative.Probe, + fuzzedWithOneFuzz.Probe, + fuzzedWithOSSFuzz.Probe, + fuzzedWithPropertyBasedHaskell.Probe, + fuzzedWithPropertyBasedJavascript.Probe, + fuzzedWithPropertyBasedTypescript.Probe, + } + + if !finding.UniqueProbesEqual(findings, expectedProbes) { + e := sce.WithMessage(sce.ErrScorecardInternal, "invalid probe results") return checker.CreateRuntimeErrorResult(name, e) } - if len(r.Fuzzers) == 0 { - return checker.CreateMinScoreResult(name, "project is not fuzzed") - } - fuzzers := []string{} - for i := range r.Fuzzers { - fuzzer := r.Fuzzers[i] - for _, f := range fuzzer.Files { - msg := checker.LogMessage{ - Path: f.Path, - Type: f.Type, - Offset: f.Offset, - } - if f.Snippet != "" { - msg.Text = f.Snippet - } - dl.Info(&msg) + // Compute the score. + for i := range findings { + f := &findings[i] + if f.Outcome == finding.OutcomePositive { + return checker.CreateMaxScoreResult(name, "project is fuzzed") } - fuzzers = append(fuzzers, fuzzer.Name) } - return checker.CreateMaxScoreResult(name, - fmt.Sprintf("project is fuzzed with %v", fuzzers)) + return checker.CreateMinScoreResult(name, "project is not fuzzed") } diff --git a/checks/evaluation/fuzzing_test.go b/checks/evaluation/fuzzing_test.go index 89311490..1300f1e2 100644 --- a/checks/evaluation/fuzzing_test.go +++ b/checks/evaluation/fuzzing_test.go @@ -20,15 +20,14 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/ossf/scorecard/v4/checker" - scut "github.com/ossf/scorecard/v4/utests" + "github.com/ossf/scorecard/v4/finding" ) func TestFuzzing(t *testing.T) { t.Parallel() type args struct { //nolint - name string - dl checker.DetailLogger - r *checker.FuzzingData + name string + findings []finding.Finding } tests := []struct { name string @@ -39,8 +38,36 @@ func TestFuzzing(t *testing.T) { name: "Fuzzing - no fuzzing", args: args{ name: "Fuzzing", - dl: &scut.TestDetailLogger{}, - r: &checker.FuzzingData{}, + findings: []finding.Finding{ + { + Probe: "fuzzedWithClusterFuzzLite", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithGoNative", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOneFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOSSFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedHaskell", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedJavascript", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedTypescript", + Outcome: finding.OutcomeNegative, + }, + }, }, want: checker.CheckResult{ Score: 0, @@ -50,23 +77,37 @@ func TestFuzzing(t *testing.T) { }, }, { - name: "Fuzzing - fuzzing", + name: "Fuzzing - fuzzing GoNative", args: args{ name: "Fuzzing", - dl: &scut.TestDetailLogger{}, - r: &checker.FuzzingData{ - Fuzzers: []checker.Tool{ - { - Name: "Fuzzing", - Files: []checker.File{ - { - Path: "Fuzzing", - Type: 0, - Offset: 1, - Snippet: "Fuzzing", - }, - }, - }, + findings: []finding.Finding{ + { + Probe: "fuzzedWithClusterFuzzLite", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithGoNative", + Outcome: finding.OutcomePositive, + }, + { + Probe: "fuzzedWithOneFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOSSFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedHaskell", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedJavascript", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedTypescript", + Outcome: finding.OutcomeNegative, }, }, }, @@ -74,21 +115,91 @@ func TestFuzzing(t *testing.T) { Score: 10, Name: "Fuzzing", Version: 2, - Reason: "project is fuzzed with [Fuzzing]", + Reason: "project is fuzzed", }, }, { - name: "Fuzzing - fuzzing data nil", + name: "Fuzzing - fuzzing missing GoNative finding", args: args{ name: "Fuzzing", - dl: &scut.TestDetailLogger{}, - r: nil, + findings: []finding.Finding{ + { + Probe: "fuzzedWithClusterFuzzLite", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOneFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOSSFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedHaskell", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedJavascript", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedTypescript", + Outcome: finding.OutcomeNegative, + }, + }, }, want: checker.CheckResult{ Score: -1, Name: "Fuzzing", Version: 2, - Reason: "internal error: empty raw data", + Reason: "internal error: invalid probe results", + }, + }, + { + name: "Fuzzing - fuzzing invalid probe name", + args: args{ + name: "Fuzzing", + findings: []finding.Finding{ + { + Probe: "fuzzedWithClusterFuzzLite", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithGoNative", + Outcome: finding.OutcomePositive, + }, + { + Probe: "fuzzedWithOneFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithOSSFuzz", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedHaskell", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedJavascript", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithPropertyBasedTypescript", + Outcome: finding.OutcomeNegative, + }, + { + Probe: "fuzzedWithInvalidProbeName", + Outcome: finding.OutcomePositive, + }, + }, + }, + want: checker.CheckResult{ + Score: -1, + Name: "Fuzzing", + Version: 2, + Reason: "internal error: invalid probe results", }, }, } @@ -96,7 +207,7 @@ func TestFuzzing(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - if got := Fuzzing(tt.args.name, tt.args.dl, tt.args.r); !cmp.Equal(got, tt.want, cmpopts.IgnoreFields(checker.CheckResult{}, "Error")) { //nolint:lll + if got := Fuzzing(tt.args.name, tt.args.findings); !cmp.Equal(got, tt.want, cmpopts.IgnoreFields(checker.CheckResult{}, "Error")) { //nolint:lll t.Errorf("Fuzzing() = %v, want %v", got, cmp.Diff(got, tt.want, cmpopts.IgnoreFields(checker.CheckResult{}, "Error"))) //nolint:lll } }) diff --git a/checks/fuzzing.go b/checks/fuzzing.go index 1e92d021..6d26df57 100644 --- a/checks/fuzzing.go +++ b/checks/fuzzing.go @@ -19,6 +19,7 @@ import ( "github.com/ossf/scorecard/v4/checks/evaluation" "github.com/ossf/scorecard/v4/checks/raw" sce "github.com/ossf/scorecard/v4/errors" + "github.com/ossf/scorecard/v4/probes" ) // CheckFuzzing is the registered name for Fuzzing. @@ -41,9 +42,16 @@ func Fuzzing(c *checker.CheckRequest) checker.CheckResult { } // Set the raw results. - if c.RawResults != nil { - c.RawResults.FuzzingResults = rawData + pRawResults := getRawResults(c) + pRawResults.FuzzingResults = rawData + + // Evaluate the probes. + findings, err := evaluateProbes(c, pRawResults, probes.Fuzzing) + if err != nil { + e := sce.WithMessage(sce.ErrScorecardInternal, err.Error()) + return checker.CreateRuntimeErrorResult(CheckFuzzing, e) } - return evaluation.Fuzzing(CheckFuzzing, c.Dlogger, &rawData) + // Return the score evaluation. + return evaluation.Fuzzing(CheckFuzzing, findings) } diff --git a/checks/fuzzing_test.go b/checks/fuzzing_test.go index 3e6e5159..5fd34bbd 100644 --- a/checks/fuzzing_test.go +++ b/checks/fuzzing_test.go @@ -33,7 +33,6 @@ func TestFuzzing(t *testing.T) { //nolint tests := []struct { name string - want checker.CheckResult langs []clients.Language response clients.SearchResponse wantErr bool @@ -52,6 +51,13 @@ func TestFuzzing(t *testing.T) { }, }, wantErr: false, + expected: scut.TestReturn{ + Error: nil, + NumberOfWarn: 7, + NumberOfDebug: 0, + NumberOfInfo: 0, + Score: 0, + }, }, { name: "hits 1", @@ -69,11 +75,10 @@ func TestFuzzing(t *testing.T) { }, }, wantErr: false, - want: checker.CheckResult{Score: 10}, expected: scut.TestReturn{ - NumberOfWarn: 0, + NumberOfWarn: 6, NumberOfDebug: 0, - NumberOfInfo: 0, + NumberOfInfo: 1, Score: 10, }, }, @@ -86,7 +91,6 @@ func TestFuzzing(t *testing.T) { }, }, wantErr: true, - want: checker.CheckResult{Score: -1}, expected: scut.TestReturn{ Error: sce.ErrScorecardInternal, NumberOfWarn: 0, @@ -104,12 +108,24 @@ func TestFuzzing(t *testing.T) { }, }, wantFuzzErr: false, - want: checker.CheckResult{Score: 0}, + expected: scut.TestReturn{ + Error: nil, + NumberOfWarn: 7, + NumberOfDebug: 0, + NumberOfInfo: 0, + Score: 0, + }, }, { name: "error", wantFuzzErr: true, - want: checker.CheckResult{}, + expected: scut.TestReturn{ + Error: nil, + NumberOfWarn: 7, + NumberOfDebug: 0, + NumberOfInfo: 0, + Score: 0, + }, }, } for _, tt := range tests { @@ -138,11 +154,14 @@ func TestFuzzing(t *testing.T) { return tt.fileContent, nil }).AnyTimes() dl := scut.TestDetailLogger{} + raw := checker.RawResults{} req := checker.CheckRequest{ RepoClient: mockFuzz, OssFuzzRepo: mockFuzz, Dlogger: &dl, + RawResults: &raw, } + if tt.wantFuzzErr { req.OssFuzzRepo = nil } diff --git a/e2e/fuzzing_test.go b/e2e/fuzzing_test.go index ad872ea3..6d33e84c 100644 --- a/e2e/fuzzing_test.go +++ b/e2e/fuzzing_test.go @@ -40,6 +40,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { Expect(err).Should(BeNil()) ossFuzzRepoClient, err := ossfuzz.CreateOSSFuzzClientEager(ossfuzz.StatusURL) Expect(err).Should(BeNil()) + req := checker.CheckRequest{ Ctx: context.Background(), RepoClient: repoClient, @@ -50,8 +51,8 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { expected := scut.TestReturn{ Error: nil, Score: checker.MaxResultScore, - NumberOfWarn: 0, - NumberOfInfo: 0, + NumberOfWarn: 6, + NumberOfInfo: 1, NumberOfDebug: 0, } result := checks.Fuzzing(&req) @@ -68,6 +69,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { Expect(err).Should(BeNil()) ossFuzzRepoClient, err := ossfuzz.CreateOSSFuzzClientEager(ossfuzz.StatusURL) Expect(err).Should(BeNil()) + req := checker.CheckRequest{ Ctx: context.Background(), RepoClient: repoClient, @@ -78,8 +80,8 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { expected := scut.TestReturn{ Error: nil, Score: checker.MaxResultScore, - NumberOfWarn: 0, - NumberOfInfo: 0, + NumberOfWarn: 6, + NumberOfInfo: 1, NumberOfDebug: 0, } result := checks.Fuzzing(&req) @@ -96,6 +98,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { Expect(err).Should(BeNil()) ossFuzzRepoClient, err := ossfuzz.CreateOSSFuzzClientEager(ossfuzz.StatusURL) Expect(err).Should(BeNil()) + req := checker.CheckRequest{ Ctx: context.Background(), RepoClient: repoClient, @@ -106,7 +109,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { expected := scut.TestReturn{ Error: nil, Score: checker.MaxResultScore, - NumberOfWarn: 0, + NumberOfWarn: 6, NumberOfInfo: 2, NumberOfDebug: 0, } @@ -124,6 +127,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { Expect(err).Should(BeNil()) ossFuzzRepoClient, err := ossfuzz.CreateOSSFuzzClientEager(ossfuzz.StatusURL) Expect(err).Should(BeNil()) + req := checker.CheckRequest{ Ctx: context.Background(), RepoClient: repoClient, @@ -144,6 +148,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { Expect(err).Should(BeNil()) ossFuzzRepoClient, err := ossfuzz.CreateOSSFuzzClientEager(ossfuzz.StatusURL) Expect(err).Should(BeNil()) + req := checker.CheckRequest{ Ctx: context.Background(), RepoClient: repoClient, @@ -154,7 +159,7 @@ var _ = Describe("E2E TEST:"+checks.CheckFuzzing, func() { expected := scut.TestReturn{ Error: nil, Score: checker.MinResultScore, - NumberOfWarn: 0, + NumberOfWarn: 7, NumberOfInfo: 0, NumberOfDebug: 0, } diff --git a/finding/finding.go b/finding/finding.go index 7dae0026..4d9b2ca8 100644 --- a/finding/finding.go +++ b/finding/finding.go @@ -149,19 +149,19 @@ func NewWith(efs embed.FS, probeID, text string, loc *Location, return f, nil } -// NewWith create a negative finding with the desried location. +// NewWith create a negative finding with the desired location. func NewNegative(efs embed.FS, probeID, text string, loc *Location, ) (*Finding, error) { return NewWith(efs, probeID, text, loc, OutcomeNegative) } -// NewNotAvailable create a finding with a NotAvailable outcome and the desried location. +// NewNotAvailable create a finding with a NotAvailable outcome and the desired location. func NewNotAvailable(efs embed.FS, probeID, text string, loc *Location, ) (*Finding, error) { return NewWith(efs, probeID, text, loc, OutcomeNotAvailable) } -// NewPositive create a positive finding with the desried location. +// NewPositive create a positive finding with the desired location. func NewPositive(efs embed.FS, probeID, text string, loc *Location, ) (*Finding, error) { return NewWith(efs, probeID, text, loc, OutcomePositive) diff --git a/probes/entries.go b/probes/entries.go index 1864db92..7e7dd729 100644 --- a/probes/entries.go +++ b/probes/entries.go @@ -17,6 +17,13 @@ package probes import ( "github.com/ossf/scorecard/v4/checker" "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/fuzzedWithClusterFuzzLite" + "github.com/ossf/scorecard/v4/probes/fuzzedWithGoNative" + "github.com/ossf/scorecard/v4/probes/fuzzedWithOSSFuzz" + "github.com/ossf/scorecard/v4/probes/fuzzedWithOneFuzz" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedHaskell" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedJavascript" + "github.com/ossf/scorecard/v4/probes/fuzzedWithPropertyBasedTypescript" "github.com/ossf/scorecard/v4/probes/securityPolicyContainsLinks" "github.com/ossf/scorecard/v4/probes/securityPolicyContainsText" "github.com/ossf/scorecard/v4/probes/securityPolicyContainsVulnerabilityDisclosure" @@ -49,6 +56,15 @@ var ( toolPyUpInstalled.Run, toolSonatypeLiftInstalled.Run, } + Fuzzing = []ProbeImpl{ + fuzzedWithOSSFuzz.Run, + fuzzedWithOneFuzz.Run, + fuzzedWithGoNative.Run, + fuzzedWithClusterFuzzLite.Run, + fuzzedWithPropertyBasedHaskell.Run, + fuzzedWithPropertyBasedTypescript.Run, + fuzzedWithPropertyBasedJavascript.Run, + } ) //nolint:gochecknoinits @@ -56,6 +72,7 @@ func init() { All = concatMultipleProbes([][]ProbeImpl{ DependencyToolUpdates, SecurityPolicy, + Fuzzing, }) } diff --git a/probes/fuzzedWithClusterFuzzLite/def.yml b/probes/fuzzedWithClusterFuzzLite/def.yml new file mode 100644 index 00000000..df398759 --- /dev/null +++ b/probes/fuzzedWithClusterFuzzLite/def.yml @@ -0,0 +1,32 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithClusterFuzzLite +short: Check that the project is fuzzed using ClusterFuzzLite +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementation looks for a file called ".clusterfuzzlite/Dockerfile". +outcome: + - If the file is found, one finding with OutcomePositive (1) is returned. + - If the file is not found, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - Follow the steps in https://github.com/google/clusterfuzzlite to integrate fuzzing as part of CI. + - Over time, try to add fuzzing for more functionalities of your project. + markdown: + - Follow the steps in [https://github.com/google/clusterfuzzlite](https://github.com/google/clusterfuzzlite) to integrate fuzzing as part of CI. + - Over time, try to add fuzzing for more functionalities of your project. \ No newline at end of file diff --git a/probes/fuzzedWithClusterFuzzLite/impl.go b/probes/fuzzedWithClusterFuzzLite/impl.go new file mode 100644 index 00000000..6837f108 --- /dev/null +++ b/probes/fuzzedWithClusterFuzzLite/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithClusterFuzzLite + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithClusterFuzzLite" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "ClusterFuzzLite") +} diff --git a/probes/fuzzedWithClusterFuzzLite/impl_test.go b/probes/fuzzedWithClusterFuzzLite/impl_test.go new file mode 100644 index 00000000..01db3089 --- /dev/null +++ b/probes/fuzzedWithClusterFuzzLite/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithClusterFuzzLite + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "ClusterFuzzLite", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "ClusterFuzzLite", + }, + { + Name: "ClusterFuzzLite", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "ClusterFuzzLite", + }, + { + Name: "not-ClusterFuzzLite", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-ClusterFuzzLite", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithGoNative/def.yml b/probes/fuzzedWithGoNative/def.yml new file mode 100644 index 00000000..c6b7806b --- /dev/null +++ b/probes/fuzzedWithGoNative/def.yml @@ -0,0 +1,32 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithGoNative +short: Check that the project is fuzzed using Go native fuzzing framework +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementation checks whether fo the presence of functions with the signature 'func FuzzSomeName(*testing.F)' in .go files. +outcome: + - If fuzzing functions are found, each finding is returned with OutcomePositive (1). + - If no fuzzing is detected, one finding with OutcomeNegative (0) is returned. +remediation: + effort: Medium + text: + - Follow the steps in https://go.dev/doc/fuzz/ to enable fuzzing on your project. + - Over time, try to add fuzzing for more functionalities of your project. + markdown: + - Follow the steps in [https://go.dev/doc/fuzz/](https://go.dev/doc/fuzz/) to enable fuzzing on your project. + - Over time, try to add fuzzing for more functionalities of your project. \ No newline at end of file diff --git a/probes/fuzzedWithGoNative/impl.go b/probes/fuzzedWithGoNative/impl.go new file mode 100644 index 00000000..5bdc74d5 --- /dev/null +++ b/probes/fuzzedWithGoNative/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithGoNative + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithGoNative" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "GoBuiltInFuzzer") +} diff --git a/probes/fuzzedWithGoNative/impl_test.go b/probes/fuzzedWithGoNative/impl_test.go new file mode 100644 index 00000000..67b93257 --- /dev/null +++ b/probes/fuzzedWithGoNative/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithGoNative + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "GoBuiltInFuzzer", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "GoBuiltInFuzzer", + }, + { + Name: "GoBuiltInFuzzer", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "GoBuiltInFuzzer", + }, + { + Name: "not-GoBuiltInFuzzer", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-GoBuiltInFuzzer", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithOSSFuzz/def.yml b/probes/fuzzedWithOSSFuzz/def.yml new file mode 100644 index 00000000..267cbd18 --- /dev/null +++ b/probes/fuzzedWithOSSFuzz/def.yml @@ -0,0 +1,32 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithOSSFuzz +short: Check that the project is fuzzed using OSS-Fuzz +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementation lists the projects integrated with OSS-Fuzz in https://github.com/google/oss-fuzz/tree/master/projects, and checks whether the project is in the list. +outcome: + - If an integration with OSS-Fuzz is found, one finding with OutcomePositive (1) is returned. + - If no integration with OSS-Fuzz is found, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - Follow the steps in https://github.com/google/oss-fuzz to integrate fuzzing for your project. + - Over time, try to add fuzzing for more functionalities of your project. + markdown: + - Follow the steps in [https://github.com/google/oss-fuzz](https://github.com/google/oss-fuzz) to integrate fuzzing for your project. + - Over time, try to add fuzzing for more functionalities of your project. \ No newline at end of file diff --git a/probes/fuzzedWithOSSFuzz/impl.go b/probes/fuzzedWithOSSFuzz/impl.go new file mode 100644 index 00000000..9f13669c --- /dev/null +++ b/probes/fuzzedWithOSSFuzz/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithOSSFuzz + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithOSSFuzz" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "OSSFuzz") +} diff --git a/probes/fuzzedWithOSSFuzz/impl_test.go b/probes/fuzzedWithOSSFuzz/impl_test.go new file mode 100644 index 00000000..8977da5c --- /dev/null +++ b/probes/fuzzedWithOSSFuzz/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithOSSFuzz + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OSSFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OSSFuzz", + }, + { + Name: "OSSFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OSSFuzz", + }, + { + Name: "not-OSSFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-OSSFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithOneFuzz/def.yml b/probes/fuzzedWithOneFuzz/def.yml new file mode 100644 index 00000000..8fb87a8f --- /dev/null +++ b/probes/fuzzedWithOneFuzz/def.yml @@ -0,0 +1,32 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithOneFuzz +short: Check that the project is fuzzed using OneFuzz +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementation checks if the file '.onefuzz' is present in the source code files. +outcome: + - If the file is found, one finding is returned with OutcomePositive (1). + - If the file is not found, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - Follow the steps in https://github.com/microsoft/onefuzz to start fuzzing for your project. + - Over time, try to add fuzzing for more functionalities of your project. + markdown: + - Follow the steps in [https://github.com/microsoft/onefuzz](https://github.com/microsoft/onefuzz) to start fuzzing for your project. + - Over time, try to add fuzzing for more functionalities of your project. \ No newline at end of file diff --git a/probes/fuzzedWithOneFuzz/impl.go b/probes/fuzzedWithOneFuzz/impl.go new file mode 100644 index 00000000..b8f5b877 --- /dev/null +++ b/probes/fuzzedWithOneFuzz/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithOneFuzz + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +var Probe = "fuzzedWithOneFuzz" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "OneFuzz") +} diff --git a/probes/fuzzedWithOneFuzz/impl_test.go b/probes/fuzzedWithOneFuzz/impl_test.go new file mode 100644 index 00000000..72838dd5 --- /dev/null +++ b/probes/fuzzedWithOneFuzz/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithOneFuzz + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OneFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OneFuzz", + }, + { + Name: "OneFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "OneFuzz", + }, + { + Name: "not-OneFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-OneFuzz", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithPropertyBasedHaskell/def.yml b/probes/fuzzedWithPropertyBasedHaskell/def.yml new file mode 100644 index 00000000..5d08c9bb --- /dev/null +++ b/probes/fuzzedWithPropertyBasedHaskell/def.yml @@ -0,0 +1,42 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithPropertyBasedHaskell +short: Check that the project is fuzzed using a property-based testing framework. +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementation looks for direct imports of QuickCheck, Hedgehog, validity and SmallCheck or their indirect imports through the higher-level Hspec or Tasty testing frameworks. +outcome: + - If imports are found, each finding is returned with OutcomePositive (1). + - If no import is detected, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - 'Use one of the following frameworks to fuzz your project:' + - 'QuickCheck: https://hackage.haskell.org/package/QuickCheck' + - 'hedgehog: https://hedgehog.qa/' + - 'validity: https://github.com/NorfairKing/validity' + - 'smallcheck: https://hackage.haskell.org/package/smallcheck' + - 'hspec: https://hspec.github.io/' + - 'tasty: https://hackage.haskell.org/package/tasty' + markdown: + - 'Use one of the following frameworks to fuzz your project:' + - '[QuickCheck](https://hackage.haskell.org/package/QuickCheck)' + - '[hedgehog]( https://hedgehog.qa/)' + - '[validity](https://github.com/NorfairKing/validity)' + - '[smallcheck](https://hackage.haskell.org/package/smallcheck)' + - '[hspec](https://hspec.github.io/)' + - '[tasty](https://hackage.haskell.org/package/tasty)' \ No newline at end of file diff --git a/probes/fuzzedWithPropertyBasedHaskell/impl.go b/probes/fuzzedWithPropertyBasedHaskell/impl.go new file mode 100644 index 00000000..34dba1ae --- /dev/null +++ b/probes/fuzzedWithPropertyBasedHaskell/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedHaskell + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithPropertyBasedHaskell" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "HaskellPropertyBasedTesting") +} diff --git a/probes/fuzzedWithPropertyBasedHaskell/impl_test.go b/probes/fuzzedWithPropertyBasedHaskell/impl_test.go new file mode 100644 index 00000000..a65e6808 --- /dev/null +++ b/probes/fuzzedWithPropertyBasedHaskell/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedHaskell + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "HaskellPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "HaskellPropertyBasedTesting", + }, + { + Name: "HaskellPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "HaskellPropertyBasedTesting", + }, + { + Name: "not-HaskellPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-HaskellPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithPropertyBasedJavascript/def.yml b/probes/fuzzedWithPropertyBasedJavascript/def.yml new file mode 100644 index 00000000..0858bc0a --- /dev/null +++ b/probes/fuzzedWithPropertyBasedJavascript/def.yml @@ -0,0 +1,30 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithPropertyBasedJavascript +short: Check that the javascript project is fuzzed using a property-based testing framework. +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementations looks for direct imports of fast-check https://github.com/dubzzz/fast-check in .js files. +outcome: + - If imports are found, each finding is returned with OutcomePositive (1). + - If no import is detected, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - 'Use fast-check: https://github.com/dubzzz/fast-check' + markdown: + - 'Use [fast-check](https://github.com/dubzzz/fast-check)' \ No newline at end of file diff --git a/probes/fuzzedWithPropertyBasedJavascript/impl.go b/probes/fuzzedWithPropertyBasedJavascript/impl.go new file mode 100644 index 00000000..692a95e1 --- /dev/null +++ b/probes/fuzzedWithPropertyBasedJavascript/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedJavascript + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithPropertyBasedJavascript" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "JavaScriptPropertyBasedTesting") +} diff --git a/probes/fuzzedWithPropertyBasedJavascript/impl_test.go b/probes/fuzzedWithPropertyBasedJavascript/impl_test.go new file mode 100644 index 00000000..debc74a0 --- /dev/null +++ b/probes/fuzzedWithPropertyBasedJavascript/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedJavascript + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "JavaScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "JavaScriptPropertyBasedTesting", + }, + { + Name: "JavaScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "JavaScriptPropertyBasedTesting", + }, + { + Name: "not-JavaScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-JavaScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/fuzzedWithPropertyBasedTypescript/def.yml b/probes/fuzzedWithPropertyBasedTypescript/def.yml new file mode 100644 index 00000000..f571b302 --- /dev/null +++ b/probes/fuzzedWithPropertyBasedTypescript/def.yml @@ -0,0 +1,30 @@ +# Copyright 2023 OpenSSF 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. + +id: fuzzedWithPropertyBasedTypescript +short: Check that the typescript project is fuzzed using a property-based testing framework. +motivation: > + Fuzzing, or fuzz testing, is the practice of feeding unexpected or random data into a program to expose bugs. + Regular fuzzing is important to detect vulnerabilities that may be exploited by others, especially since attackers can also use fuzzing to find the same flaws. +implementation: > + The implementations looks for direct imports of fast-check https://github.com/dubzzz/fast-check in .ts files. +outcome: + - If imports are found, each finding is returned with OutcomePositive (1). + - If no import is detected, one finding with OutcomeNegative (0) is returned. +remediation: + effort: High + text: + - 'Use fast-check: https://github.com/dubzzz/fast-check' + markdown: + - 'Use [fast-check](https://github.com/dubzzz/fast-check)' \ No newline at end of file diff --git a/probes/fuzzedWithPropertyBasedTypescript/impl.go b/probes/fuzzedWithPropertyBasedTypescript/impl.go new file mode 100644 index 00000000..01b4f5fc --- /dev/null +++ b/probes/fuzzedWithPropertyBasedTypescript/impl.go @@ -0,0 +1,38 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedTypescript + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +//go:embed *.yml +var fs embed.FS + +const Probe = "fuzzedWithPropertyBasedTypescript" + +func Run(raw *checker.RawResults) ([]finding.Finding, string, error) { + if raw == nil { + return nil, "", fmt.Errorf("%w: raw", utils.ErrNil) + } + //nolint:wrapcheck + return utils.FuzzerRun(raw, fs, Probe, "TypeScriptPropertyBasedTesting") +} diff --git a/probes/fuzzedWithPropertyBasedTypescript/impl_test.go b/probes/fuzzedWithPropertyBasedTypescript/impl_test.go new file mode 100644 index 00000000..a0fd7a95 --- /dev/null +++ b/probes/fuzzedWithPropertyBasedTypescript/impl_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 OpenSSF 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. + +// nolint:stylecheck +package fuzzedWithPropertyBasedTypescript + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" + "github.com/ossf/scorecard/v4/probes/internal/utils" +) + +func Test_Run(t *testing.T) { + t.Parallel() + // nolint:govet + tests := []struct { + name string + raw *checker.RawResults + outcomes []finding.Outcome + err error + }{ + { + name: "fuzzer present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "TypeScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present twice", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "TypeScriptPropertyBasedTesting", + }, + { + Name: "TypeScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + finding.OutcomePositive, + }, + }, + { + name: "fuzzer present and other present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "TypeScriptPropertyBasedTesting", + }, + { + Name: "not-TypeScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomePositive, + }, + }, + { + name: "fuzzer not present", + raw: &checker.RawResults{ + FuzzingResults: checker.FuzzingData{ + Fuzzers: []checker.Tool{ + { + Name: "not-TypeScriptPropertyBasedTesting", + }, + }, + }, + }, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "no fuzzer", + raw: &checker.RawResults{}, + outcomes: []finding.Outcome{ + finding.OutcomeNegative, + }, + }, + { + name: "nil raw", + err: utils.ErrNil, + }, + } + for _, tt := range tests { + tt := tt // Re-initializing variable so it is not changed while executing the closure below + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + findings, s, err := Run(tt.raw) + if !cmp.Equal(tt.err, err, cmpopts.EquateErrors()) { + t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(tt.err, err, cmpopts.EquateErrors())) + } + if err != nil { + return + } + if diff := cmp.Diff(Probe, s); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(len(tt.outcomes), len(findings)); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + for i := range tt.outcomes { + outcome := &tt.outcomes[i] + f := &findings[i] + if diff := cmp.Diff(*outcome, f.Outcome); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/probes/internal/utils/fuzzing.go b/probes/internal/utils/fuzzing.go new file mode 100644 index 00000000..a443b3c4 --- /dev/null +++ b/probes/internal/utils/fuzzing.go @@ -0,0 +1,71 @@ +// Copyright 2023 OpenSSF 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 utils + +import ( + "embed" + "fmt" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/finding" +) + +func FuzzerRun(raw *checker.RawResults, fs embed.FS, probeID, fuzzerName string) ([]finding.Finding, string, error) { + var findings []finding.Finding + fuzzers := raw.FuzzingResults.Fuzzers + + for i := range fuzzers { + fuzzer := &fuzzers[i] + if fuzzer.Name != fuzzerName { + continue + } + + // The current implementation does not provide file location + // for all fuzzers. Check this first. + if len(fuzzer.Files) == 0 { + f, err := finding.NewWith(fs, probeID, + fmt.Sprintf("%s integration found", fuzzerName), nil, + finding.OutcomePositive) + if err != nil { + return nil, probeID, fmt.Errorf("create finding: %w", err) + } + findings = append(findings, *f) + continue + } + + // Files are present. Create one results for each file location. + for j := range fuzzer.Files { + file := fuzzer.Files[j] + f, err := finding.NewWith(fs, probeID, + fmt.Sprintf("%s integration found", fuzzerName), file.Location(), + finding.OutcomePositive) + if err != nil { + return nil, probeID, fmt.Errorf("create finding: %w", err) + } + findings = append(findings, *f) + } + } + + if len(findings) == 0 { + f, err := finding.NewNegative(fs, probeID, + fmt.Sprintf("no %s integration found", fuzzerName), nil) + if err != nil { + return nil, probeID, fmt.Errorf("create finding: %w", err) + } + findings = append(findings, *f) + } + + return findings, probeID, nil +}