Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌱 convert packaging check to probe #3486

Merged
merged 9 commits into from
Oct 24, 2023
Merged
11 changes: 11 additions & 0 deletions checks/evaluation/finding.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ func nonNegativeFindings(findings []finding.Finding) []finding.Finding {
}
return ff
}

func negativeFindings(findings []finding.Finding) []finding.Finding {
var ff []finding.Finding
for i := range findings {
f := &findings[i]
if f.Outcome == finding.OutcomeNegative {
ff = append(ff, *f)
}
}
return ff
}
85 changes: 28 additions & 57 deletions checks/evaluation/packaging.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,75 +15,46 @@
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/packagedWithAutomatedWorkflow"
)

// Packaging applies the score policy for the Packaging check.
func Packaging(name string, dl checker.DetailLogger, r *checker.PackagingData) checker.CheckResult {
if r == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
func Packaging(name string,
findings []finding.Finding,
dl checker.DetailLogger,
) checker.CheckResult {
expectedProbes := []string{
packagedWithAutomatedWorkflow.Probe,
}

if !finding.UniqueProbesEqual(findings, expectedProbes) {
e := sce.WithMessage(sce.ErrScorecardInternal, "invalid probe results")
return checker.CreateRuntimeErrorResult(name, e)
}

pass := false
for _, p := range r.Packages {
if p.Msg != nil {
// This is a debug message. Let's just replay the message.
dl.Debug(&checker.LogMessage{
Text: *p.Msg,
// Currently there is only a single packaging probe that returns
// a single positive or negative outcome. As such, in this evaluation,
// we return max score if the outcome is positive and lowest score if
// the outcome is negative.
maxScore := false
for _, f := range findings {
f := f
if f.Outcome == finding.OutcomePositive {
maxScore = true
// Log all findings except the negative ones.
dl.Info(&checker.LogMessage{
Finding: &f,
})
continue
}

// Presence of a single non-debug message means the
// check passes.
pass = true

msg, err := createLogMessage(p)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
dl.Info(&msg)
}

if pass {
return checker.CreateMaxScoreResult(name,
"publishing workflow detected")
if maxScore {
return checker.CreateMaxScoreResult(name, "packaging workflow detected")
}

dl.Warn(&checker.LogMessage{
Text: "no GitHub/GitLab publishing workflow detected",
})

checker.LogFindings(negativeFindings(findings), dl)
return checker.CreateInconclusiveResult(name,
"no published package detected")
}

func createLogMessage(p checker.Package) (checker.LogMessage, error) {
var msg checker.LogMessage

if p.Msg != nil {
return msg, sce.WithMessage(sce.ErrScorecardInternal, "Msg should be nil")
}

if p.File == nil {
return msg, sce.WithMessage(sce.ErrScorecardInternal, "File field is nil")
}

if p.File != nil {
msg.Path = p.File.Path
msg.Type = p.File.Type
msg.Offset = p.File.Offset
}

if len(p.Runs) == 0 {
return msg, sce.WithMessage(sce.ErrScorecardInternal, "no run data")
}

msg.Text = fmt.Sprintf("GitHub/GitLab publishing workflow used in run %s", p.Runs[0].URL)

return msg, nil
"packaging workflow not detected")
}
170 changes: 44 additions & 126 deletions checks/evaluation/packaging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,162 +16,80 @@ package evaluation
import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/finding"
scut "github.com/ossf/scorecard/v4/utests"
)

func Test_createLogMessage(t *testing.T) {
msg := "msg"
func TestPackaging(t *testing.T) {
t.Parallel()
tests := []struct { //nolint:govet
name string
args checker.Package
want checker.LogMessage
wantErr bool
tests := []struct {
name string
findings []finding.Finding
result scut.TestReturn
}{
{
name: "nil package",
args: checker.Package{},
want: checker.LogMessage{},
wantErr: true,
},
{
name: "nil file",
args: checker.Package{
File: nil,
},
want: checker.LogMessage{},
wantErr: true,
},
{
name: "msg is not nil",
args: checker.Package{
File: &checker.File{},
Msg: &msg,
},
want: checker.LogMessage{
Text: "",
},
wantErr: true,
},
{
name: "file is not nil",
args: checker.Package{
File: &checker.File{
Path: "path",
name: "test positive outcome",
findings: []finding.Finding{
{
Probe: "packagedWithAutomatedWorkflow",
Outcome: finding.OutcomePositive,
},
},
want: checker.LogMessage{
Path: "path",
result: scut.TestReturn{
Score: checker.MaxResultScore,
NumberOfInfo: 1,
},
wantErr: true,
},
{
name: "runs are not zero",
args: checker.Package{
File: &checker.File{
Path: "path",
},
Runs: []checker.Run{
{},
name: "test positive outcome with wrong probes",
findings: []finding.Finding{
{
Probe: "wrongProbe",
Outcome: finding.OutcomePositive,
},
},
want: checker.LogMessage{
Text: "GitHub/GitLab publishing workflow used in run ",
Path: "path",
},
},
}
for _, tt := range tests {
tt := tt // Parallel testing
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := createLogMessage(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("createLogMessage() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !cmp.Equal(got, tt.want) {
t.Errorf("createLogMessage() got = %v, want %v", got, cmp.Diff(got, tt.want))
}
})
}
}

func TestPackaging(t *testing.T) {
t.Parallel()
type args struct { //nolint:govet
name string
dl checker.DetailLogger
r *checker.PackagingData
}
tests := []struct {
name string
args args
want checker.CheckResult
}{
{
name: "nil packaging data",
args: args{
name: "name",
dl: nil,
r: nil,
},
want: checker.CheckResult{
Name: "name",
Version: 2,
Score: -1,
Reason: "internal error: empty raw data",
result: scut.TestReturn{
Score: -1,
Error: sce.ErrScorecardInternal,
},
},
{
name: "empty packaging data",
args: args{
name: "name",
dl: &scut.TestDetailLogger{},
r: &checker.PackagingData{},
name: "test inconclusive outcome",
findings: []finding.Finding{
{
Probe: "packagedWithAutomatedWorkflow",
Outcome: finding.OutcomeNegative,
},
},
want: checker.CheckResult{
Name: "name",
Version: 2,
Score: -1,
Reason: "no published package detected",
result: scut.TestReturn{
Score: checker.InconclusiveResultScore,
NumberOfWarn: 1,
spencerschrock marked this conversation as resolved.
Show resolved Hide resolved
},
},
{
name: "runs are not zero",
args: args{
dl: &scut.TestDetailLogger{},
r: &checker.PackagingData{
Packages: []checker.Package{
{
File: &checker.File{
Path: "path",
},
Runs: []checker.Run{
{},
},
},
},
name: "test negative outcome with wrong probes",
findings: []finding.Finding{
{
Probe: "wrongProbe",
Outcome: finding.OutcomeNegative,
},
},
want: checker.CheckResult{
Name: "",
Version: 2,
Score: 10,
Reason: "publishing workflow detected",
result: scut.TestReturn{
Score: -1,
Error: sce.ErrScorecardInternal,
},
},
}
for _, tt := range tests {
tt := tt // Parallel testing
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := Packaging(tt.args.name, tt.args.dl, tt.args.r); !cmp.Equal(got, tt.want, cmpopts.IgnoreFields(checker.CheckResult{}, "Error")) { //nolint:lll
t.Errorf("Packaging() = %v, want %v", got, cmp.Diff(got, tt.want, cmpopts.IgnoreFields(checker.CheckResult{}, "Error"))) //nolint:lll
dl := scut.TestDetailLogger{}
got := Packaging(tt.name, tt.findings, &dl)
if !scut.ValidateTestReturn(t, tt.name, &tt.result, &got, &dl) {
t.Errorf("got %v, expected %v", got, tt.result)
}
})
}
Expand Down
14 changes: 10 additions & 4 deletions checks/packaging.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"github.com/ossf/scorecard/v4/clients/githubrepo"
"github.com/ossf/scorecard/v4/clients/gitlabrepo"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/probes"
"github.com/ossf/scorecard/v4/probes/zrunner"
)

// CheckPackaging is the registered name for Packaging.
Expand Down Expand Up @@ -54,10 +56,14 @@
return checker.CreateRuntimeErrorResult(CheckPackaging, e)
}

// Set the raw results.
if c.RawResults != nil {
c.RawResults.PackagingResults = rawData
pRawResults := getRawResults(c)
pRawResults.PackagingResults = rawData

findings, err := zrunner.Run(pRawResults, probes.Packaging)
if err != nil {
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckPackaging, e)

Check warning on line 65 in checks/packaging.go

View check run for this annotation

Codecov / codecov/patch

checks/packaging.go#L59-L65

Added lines #L59 - L65 were not covered by tests
}

return evaluation.Packaging(CheckPackaging, c.Dlogger, &rawData)
return evaluation.Packaging(CheckPackaging, findings, c.Dlogger)

Check warning on line 68 in checks/packaging.go

View check run for this annotation

Codecov / codecov/patch

checks/packaging.go#L68

Added line #L68 was not covered by tests
}
2 changes: 1 addition & 1 deletion e2e/packaging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var _ = Describe("E2E TEST:"+checks.CheckPackaging, func() {
Score: checker.InconclusiveResultScore,
NumberOfWarn: 1,
NumberOfInfo: 0,
NumberOfDebug: 4,
NumberOfDebug: 0,
AdamKorcz marked this conversation as resolved.
Show resolved Hide resolved
}
result := checks.Packaging(&req)
Expect(scut.ValidateTestReturn(nil, "use packaging", &expected, &result, &dl)).Should(BeTrue())
Expand Down
4 changes: 4 additions & 0 deletions probes/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/ossf/scorecard/v4/probes/hasFSFOrOSIApprovedLicense"
"github.com/ossf/scorecard/v4/probes/hasLicenseFile"
"github.com/ossf/scorecard/v4/probes/hasLicenseFileAtTopDir"
"github.com/ossf/scorecard/v4/probes/packagedWithAutomatedWorkflow"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsLinks"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsText"
"github.com/ossf/scorecard/v4/probes/securityPolicyContainsVulnerabilityDisclosure"
Expand Down Expand Up @@ -80,6 +81,9 @@ var (
fuzzedWithPropertyBasedTypescript.Run,
fuzzedWithPropertyBasedJavascript.Run,
}
Packaging = []ProbeImpl{
packagedWithAutomatedWorkflow.Run,
}
License = []ProbeImpl{
hasLicenseFile.Run,
hasFSFOrOSIApprovedLicense.Run,
Expand Down
Loading
Loading