Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion checks/evaluation/fuzzing.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
// Fuzzing applies the score policy for the Fuzzing check.
func Fuzzing(name string,
findings []finding.Finding, dl checker.DetailLogger,
usesMemoryUnsafeLanguage bool,
) checker.CheckResult {
expectedProbes := []string{
fuzzed.Probe,
Expand Down Expand Up @@ -56,5 +57,10 @@ func Fuzzing(name string,
if fuzzerDetected {
return checker.CreateMaxScoreResult(name, "project is fuzzed")
}
return checker.CreateMinScoreResult(name, "project is not fuzzed")

if usesMemoryUnsafeLanguage {
return checker.CreateMinScoreResult(name, "project is not fuzzed")
}

return checker.CreateInconclusiveResult(name, "fuzzing not required for memory-safe languages")
}
28 changes: 17 additions & 11 deletions checks/evaluation/fuzzing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,29 @@ import (
func TestFuzzing(t *testing.T) {
t.Parallel()
tests := []struct {
name string
findings []finding.Finding
result scut.TestReturn
name string
findings []finding.Finding
usesMemoryUnsafeLanguage bool
result scut.TestReturn
}{
{
name: "no fuzzers",
findings: []finding.Finding{
{
Probe: fuzzed.Probe,
Outcome: finding.OutcomeFalse,
},
},
name: "no fuzzers - unsafe language",
findings: []finding.Finding{{Probe: fuzzed.Probe, Outcome: finding.OutcomeFalse}},
usesMemoryUnsafeLanguage: true,
result: scut.TestReturn{
Score: checker.MinResultScore,
NumberOfWarn: 1,
},
},
{
name: "no fuzzers - safe language",
findings: []finding.Finding{{Probe: fuzzed.Probe, Outcome: finding.OutcomeFalse}},
usesMemoryUnsafeLanguage: false,
result: scut.TestReturn{
Score: checker.InconclusiveResultScore,
NumberOfWarn: 1,
},
},
{
name: "single fuzzer gives max score",
findings: []finding.Finding{
Expand Down Expand Up @@ -85,7 +91,7 @@ func TestFuzzing(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dl := scut.TestDetailLogger{}
got := Fuzzing(tt.name, tt.findings, &dl)
got := Fuzzing(tt.name, tt.findings, &dl, tt.usesMemoryUnsafeLanguage)
scut.ValidateTestReturn(t, tt.name, &tt.result, &got, &dl)
})
}
Expand Down
11 changes: 10 additions & 1 deletion checks/fuzzing.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
package checks

import (
"fmt"

"github.com/ossf/scorecard/v5/checker"
"github.com/ossf/scorecard/v5/checks/evaluation"
"github.com/ossf/scorecard/v5/checks/raw"
"github.com/ossf/scorecard/v5/clients"
sce "github.com/ossf/scorecard/v5/errors"
"github.com/ossf/scorecard/v5/probes"
"github.com/ossf/scorecard/v5/probes/zrunner"
Expand Down Expand Up @@ -57,7 +60,13 @@ func Fuzzing(c *checker.CheckRequest) checker.CheckResult {
}

// Return the score evaluation.
ret := evaluation.Fuzzing(CheckFuzzing, findings, c.Dlogger)
langs, err := c.RepoClient.ListProgrammingLanguages()
if err != nil {
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("ListProgrammingLanguages: %v", err))
return checker.CreateRuntimeErrorResult(CheckFuzzing, e)
}

ret := evaluation.Fuzzing(CheckFuzzing, findings, c.Dlogger, clients.HasMemoryUnsafeLanguage(langs))
ret.Findings = findings
return ret
}
6 changes: 3 additions & 3 deletions checks/fuzzing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestFuzzing(t *testing.T) {
NumberOfWarn: 1,
NumberOfDebug: 0,
NumberOfInfo: 0,
Score: 0,
Score: -1,
},
},
{
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestFuzzing(t *testing.T) {
NumberOfWarn: 1,
NumberOfDebug: 0,
NumberOfInfo: 0,
Score: 0,
Score: -1,
},
},
{
Expand All @@ -125,7 +125,7 @@ func TestFuzzing(t *testing.T) {
NumberOfWarn: 1,
NumberOfDebug: 0,
NumberOfInfo: 0,
Score: 0,
Score: -1,
},
},
}
Expand Down
26 changes: 26 additions & 0 deletions clients/languages.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package clients

import "strings"

// LanguageName is the name of a language, a customized type of string.
type LanguageName string

Expand Down Expand Up @@ -47,6 +49,9 @@ const (
// ObjectiveC: the objective c language.
ObjectiveC LanguageName = "objectivec"

// ObjectiveCpp: the objective c++ language.
ObjectiveCpp LanguageName = "objective-c++"

// Ruby: https://www.ruby-lang.org/
Ruby LanguageName = "ruby"

Expand Down Expand Up @@ -111,3 +116,24 @@ type Language struct {

// TODO: add more properties for Language.
}

// HasMemoryUnsafeLanguage returns true if any of the languages in the list are memory-unsafe.
// Based on OpenSSF best practices:
// https://github.com/ossf/scorecard/blob/main/docs/checks.md#fuzzing
func HasMemoryUnsafeLanguage(langs []Language) bool {
memoryUnsafeLanguages := map[LanguageName]bool{
C: true,
Cpp: true,
ObjectiveC: true,
ObjectiveCpp: true,
"objective-c": true, // Robustness for hyphenated Objective-C
}

for _, lang := range langs {
normalizedName := LanguageName(strings.ToLower(string(lang.Name)))
if memoryUnsafeLanguages[normalizedName] {
return true
}
}
return false
}
120 changes: 120 additions & 0 deletions clients/languages_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2021 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 clients

import "testing"

func TestHasMemoryUnsafeLanguage(t *testing.T) {
t.Parallel()
tests := []struct {
name string
langs []Language
want bool
}{
{
name: "empty",
langs: []Language{},
want: false,
},
{
name: "safe",
langs: []Language{
{Name: Go, NumLines: 100},
{Name: Python, NumLines: 100},
},
want: false,
},
{
name: "contains C",
langs: []Language{
{Name: C, NumLines: 100},
},
want: true,
},
{
name: "contains C++",
langs: []Language{
{Name: Cpp, NumLines: 100},
},
want: true,
},
{
name: "contains Objective-C",
langs: []Language{
{Name: ObjectiveC, NumLines: 100},
},
want: true,
},
{
name: "contains Objective-C++",
langs: []Language{
{Name: ObjectiveCpp, NumLines: 100},
},
want: true,
},
{
name: "mixed safe and unsafe",
langs: []Language{
{Name: Go, NumLines: 100},
{Name: C, NumLines: 100},
},
want: true,
},
{
name: "cased variants",
langs: []Language{
{Name: LanguageName("C"), NumLines: 100},
},
want: true,
},
{
name: "cased variants C++",
langs: []Language{
{Name: LanguageName("C++"), NumLines: 100},
},
want: true,
},
{
name: "lowercase variant",
langs: []Language{
{Name: LanguageName("c"), NumLines: 100},
},
want: true,
},
{
name: "mixed case variant",
langs: []Language{
{Name: LanguageName("C++"), NumLines: 100},
},
want: true,
},
{
name: "lowercase objective-c",
langs: []Language{
{Name: LanguageName("objective-c"), NumLines: 100},
},
want: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := HasMemoryUnsafeLanguage(tt.langs); got != tt.want {
t.Errorf("HasMemoryUnsafeLanguage() = %v, want %v", got, tt.want)
}
})
}
}
4 changes: 4 additions & 0 deletions docs/checks/internal/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ checks:
a low score on this test. There are many ways to implement fuzzing, and it is
challenging for an automated tool like Scorecard to detect them all. A low score
is therefore not a definitive indication that the project is at risk.

Projects that exclusively use memory-safe languages (e.g., Go, Rust, Java, Python)
and have no detected fuzzers will receive an "Inconclusive" (N/A) result, as
fuzzing is primarily recommended for memory-unsafe languages (C, C++, Objective-C).
remediation:
- >-
Integrate the project with OSS-Fuzz by following the instructions
Expand Down