mirror of
https://github.com/ossf/scorecard.git
synced 2024-11-04 03:52:31 +03:00
✨ [experimental] Add probe code and support for Tool-Update-Dependency (#2944)
* update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> * update Signed-off-by: laurentsimon <laurentsimon@google.com> --------- Signed-off-by: laurentsimon <laurentsimon@google.com>
This commit is contained in:
parent
f8d33f8b3e
commit
1a336d8087
@ -193,3 +193,26 @@ func CreateRuntimeErrorResult(name string, e error) CheckResult {
|
|||||||
Reason: e.Error(), // Note: message already accessible by caller thru `Error`.
|
Reason: e.Error(), // Note: message already accessible by caller thru `Error`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogFindings logs the list of findings.
|
||||||
|
func LogFindings(findings []finding.Finding, dl DetailLogger) error {
|
||||||
|
for i := range findings {
|
||||||
|
f := &findings[i]
|
||||||
|
switch f.Outcome {
|
||||||
|
case finding.OutcomeNegative:
|
||||||
|
dl.Warn(&LogMessage{
|
||||||
|
Finding: f,
|
||||||
|
})
|
||||||
|
case finding.OutcomePositive:
|
||||||
|
dl.Info(&LogMessage{
|
||||||
|
Finding: f,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
dl.Debug(&LogMessage{
|
||||||
|
Finding: f,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -242,7 +242,6 @@ type SignedReleasesData struct {
|
|||||||
// for the Dependency-Update-Tool check.
|
// for the Dependency-Update-Tool check.
|
||||||
type DependencyUpdateToolData struct {
|
type DependencyUpdateToolData struct {
|
||||||
// Tools contains a list of tools.
|
// Tools contains a list of tools.
|
||||||
// Note: we only populate one entry at most.
|
|
||||||
Tools []Tool
|
Tools []Tool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,3 +374,24 @@ type TokenPermission struct {
|
|||||||
Msg *string
|
Msg *string
|
||||||
Type PermissionLevel
|
Type PermissionLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Location generates location from a file.
|
||||||
|
func (f *File) Location() *finding.Location {
|
||||||
|
// TODO(2626): merge location and path.
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
loc := &finding.Location{
|
||||||
|
Type: f.Type,
|
||||||
|
Path: f.Path,
|
||||||
|
LineStart: &f.Offset,
|
||||||
|
}
|
||||||
|
if f.EndOffset != 0 {
|
||||||
|
loc.LineEnd = &f.EndOffset
|
||||||
|
}
|
||||||
|
if f.Snippet != "" {
|
||||||
|
loc.Snippet = &f.Snippet
|
||||||
|
}
|
||||||
|
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
@ -19,12 +19,13 @@ import (
|
|||||||
"github.com/ossf/scorecard/v4/checks/evaluation"
|
"github.com/ossf/scorecard/v4/checks/evaluation"
|
||||||
"github.com/ossf/scorecard/v4/checks/raw"
|
"github.com/ossf/scorecard/v4/checks/raw"
|
||||||
sce "github.com/ossf/scorecard/v4/errors"
|
sce "github.com/ossf/scorecard/v4/errors"
|
||||||
|
"github.com/ossf/scorecard/v4/probes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckDependencyUpdateTool is the exported name for Automatic-Depdendency-Update.
|
// CheckDependencyUpdateTool is the exported name for Automatic-Depdendency-Update.
|
||||||
const CheckDependencyUpdateTool = "Dependency-Update-Tool"
|
const CheckDependencyUpdateTool = "Dependency-Update-Tool"
|
||||||
|
|
||||||
//nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
supportedRequestTypes := []checker.RequestType{
|
supportedRequestTypes := []checker.RequestType{
|
||||||
checker.FileBased,
|
checker.FileBased,
|
||||||
@ -48,6 +49,13 @@ func DependencyUpdateTool(c *checker.CheckRequest) checker.CheckResult {
|
|||||||
c.RawResults.DependencyUpdateToolResults = rawData
|
c.RawResults.DependencyUpdateToolResults = rawData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluate the probes.
|
||||||
|
findings, err := evaluateProbes(c, CheckDependencyUpdateTool, probes.DependencyToolUpdates)
|
||||||
|
if err != nil {
|
||||||
|
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||||
|
return checker.CreateRuntimeErrorResult(CheckDependencyUpdateTool, e)
|
||||||
|
}
|
||||||
|
|
||||||
// Return the score evaluation.
|
// Return the score evaluation.
|
||||||
return evaluation.DependencyUpdateTool(CheckDependencyUpdateTool, c.Dlogger, &rawData)
|
return evaluation.DependencyUpdateTool(CheckDependencyUpdateTool, findings)
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
CallSearchCommits: 0,
|
CallSearchCommits: 0,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
|
NumberOfWarn: 3,
|
||||||
Score: 10,
|
Score: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -63,6 +64,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
CallSearchCommits: 0,
|
CallSearchCommits: 0,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
|
NumberOfWarn: 3,
|
||||||
Score: 10,
|
Score: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -75,7 +77,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
SearchCommits: []clients.Commit{{Committer: clients.User{ID: 111111111}}},
|
SearchCommits: []clients.Commit{{Committer: clients.User{ID: 111111111}}},
|
||||||
CallSearchCommits: 1,
|
CallSearchCommits: 1,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfWarn: 1,
|
NumberOfWarn: 4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -87,7 +89,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
SearchCommits: []clients.Commit{},
|
SearchCommits: []clients.Commit{},
|
||||||
CallSearchCommits: 1,
|
CallSearchCommits: 1,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfWarn: 1,
|
NumberOfWarn: 4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -101,6 +103,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
CallSearchCommits: 1,
|
CallSearchCommits: 1,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
|
NumberOfWarn: 3,
|
||||||
Score: 10,
|
Score: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -108,13 +111,14 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
name: "found in commits 2",
|
name: "found in commits 2",
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
files: []string{},
|
files: []string{},
|
||||||
SearchCommits: []clients.Commit{{Committer: clients.User{ID: 111111111}},
|
SearchCommits: []clients.Commit{
|
||||||
|
{Committer: clients.User{ID: 111111111}},
|
||||||
{Committer: clients.User{ID: dependabotID}},
|
{Committer: clients.User{ID: dependabotID}},
|
||||||
},
|
},
|
||||||
|
|
||||||
CallSearchCommits: 1,
|
CallSearchCommits: 1,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
|
NumberOfWarn: 3,
|
||||||
Score: 10,
|
Score: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -125,12 +129,14 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
files: []string{
|
files: []string{
|
||||||
".github/foobar.yml",
|
".github/foobar.yml",
|
||||||
},
|
},
|
||||||
SearchCommits: []clients.Commit{{Committer: clients.User{ID: 111111111}},
|
SearchCommits: []clients.Commit{
|
||||||
|
{Committer: clients.User{ID: 111111111}},
|
||||||
{Committer: clients.User{ID: dependabotID}},
|
{Committer: clients.User{ID: dependabotID}},
|
||||||
},
|
},
|
||||||
CallSearchCommits: 1,
|
CallSearchCommits: 1,
|
||||||
expected: scut.TestReturn{
|
expected: scut.TestReturn{
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
|
NumberOfWarn: 3,
|
||||||
Score: 10,
|
Score: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -144,9 +150,11 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
mockRepo.EXPECT().ListFiles(gomock.Any()).Return(tt.files, nil)
|
mockRepo.EXPECT().ListFiles(gomock.Any()).Return(tt.files, nil)
|
||||||
mockRepo.EXPECT().SearchCommits(gomock.Any()).Return(tt.SearchCommits, nil).Times(tt.CallSearchCommits)
|
mockRepo.EXPECT().SearchCommits(gomock.Any()).Return(tt.SearchCommits, nil).Times(tt.CallSearchCommits)
|
||||||
dl := scut.TestDetailLogger{}
|
dl := scut.TestDetailLogger{}
|
||||||
|
raw := checker.RawResults{}
|
||||||
c := &checker.CheckRequest{
|
c := &checker.CheckRequest{
|
||||||
RepoClient: mockRepo,
|
RepoClient: mockRepo,
|
||||||
Dlogger: &dl,
|
Dlogger: &dl,
|
||||||
|
RawResults: &raw,
|
||||||
}
|
}
|
||||||
res := DependencyUpdateTool(c)
|
res := DependencyUpdateTool(c)
|
||||||
|
|
||||||
|
@ -15,51 +15,20 @@
|
|||||||
package evaluation
|
package evaluation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ossf/scorecard/v4/checker"
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
sce "github.com/ossf/scorecard/v4/errors"
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DependencyUpdateTool applies the score policy for the Dependency-Update-Tool check.
|
// DependencyUpdateTool applies the score policy for the Dependency-Update-Tool check.
|
||||||
func DependencyUpdateTool(name string, dl checker.DetailLogger,
|
func DependencyUpdateTool(name string,
|
||||||
r *checker.DependencyUpdateToolData,
|
findings []finding.Finding,
|
||||||
) checker.CheckResult {
|
) checker.CheckResult {
|
||||||
if r == nil {
|
for i := range findings {
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
|
f := &findings[i]
|
||||||
return checker.CreateRuntimeErrorResult(name, e)
|
if f.Outcome == finding.OutcomePositive {
|
||||||
|
return checker.CreateMaxScoreResult(name, "update tool detected")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the policy evaluation.
|
return checker.CreateMinScoreResult(name, "no update tool detected")
|
||||||
if r.Tools == nil || len(r.Tools) == 0 {
|
|
||||||
dl.Warn(&checker.LogMessage{
|
|
||||||
Text: `Config file not detected in source location for dependabot, renovatebot, Sonatype Lift, or
|
|
||||||
PyUp (Python). We recommend setting this configuration in code so it can be easily verified by others.`,
|
|
||||||
})
|
|
||||||
return checker.CreateMinScoreResult(name, "no update tool detected")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the input.
|
|
||||||
if len(r.Tools) != 1 {
|
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("found %d tools, expected 1", len(r.Tools)))
|
|
||||||
return checker.CreateRuntimeErrorResult(name, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Tools[0].Files == nil {
|
|
||||||
e := sce.WithMessage(sce.ErrScorecardInternal, "Files are nil")
|
|
||||||
return checker.CreateRuntimeErrorResult(name, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over all the files, since a Tool can contain multiple files.
|
|
||||||
for _, file := range r.Tools[0].Files {
|
|
||||||
dl.Info(&checker.LogMessage{
|
|
||||||
Path: file.Path,
|
|
||||||
Type: file.Type,
|
|
||||||
Offset: file.Offset,
|
|
||||||
Text: fmt.Sprintf("%s detected", r.Tools[0].Name),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// High score result.
|
|
||||||
return checker.CreateMaxScoreResult(name, "update tool detected")
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ossf/scorecard/v4/checker"
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
sce "github.com/ossf/scorecard/v4/errors"
|
|
||||||
"github.com/ossf/scorecard/v4/finding"
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
scut "github.com/ossf/scorecard/v4/utests"
|
scut "github.com/ossf/scorecard/v4/utests"
|
||||||
)
|
)
|
||||||
@ -26,135 +25,91 @@ import (
|
|||||||
func TestDependencyUpdateTool(t *testing.T) {
|
func TestDependencyUpdateTool(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//nolint
|
//nolint
|
||||||
type args struct {
|
|
||||||
name string
|
|
||||||
dl checker.DetailLogger
|
|
||||||
r *checker.DependencyUpdateToolData
|
|
||||||
}
|
|
||||||
//nolint
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
findings []finding.Finding
|
||||||
want checker.CheckResult
|
|
||||||
err bool
|
err bool
|
||||||
|
want checker.CheckResult
|
||||||
expected scut.TestReturn
|
expected scut.TestReturn
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "DependencyUpdateTool",
|
name: "dependabot",
|
||||||
args: args{
|
findings: []finding.Finding{
|
||||||
name: "DependencyUpdateTool",
|
{
|
||||||
dl: &scut.TestDetailLogger{},
|
Probe: "toolDependabotInstalled",
|
||||||
r: &checker.DependencyUpdateToolData{
|
Outcome: finding.OutcomePositive,
|
||||||
Tools: []checker.Tool{
|
|
||||||
{
|
|
||||||
Name: "DependencyUpdateTool",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: checker.CheckResult{
|
|
||||||
Score: -1,
|
|
||||||
},
|
|
||||||
err: false,
|
|
||||||
expected: scut.TestReturn{
|
|
||||||
Error: sce.ErrScorecardInternal,
|
|
||||||
Score: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty tool list",
|
|
||||||
args: args{
|
|
||||||
name: "DependencyUpdateTool",
|
|
||||||
dl: &scut.TestDetailLogger{},
|
|
||||||
r: &checker.DependencyUpdateToolData{
|
|
||||||
Tools: []checker.Tool{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: checker.CheckResult{
|
|
||||||
Score: 0,
|
|
||||||
Error: nil,
|
|
||||||
},
|
|
||||||
err: false,
|
|
||||||
expected: scut.TestReturn{
|
|
||||||
Score: 0,
|
|
||||||
NumberOfWarn: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid tool",
|
|
||||||
args: args{
|
|
||||||
name: "DependencyUpdateTool",
|
|
||||||
dl: &scut.TestDetailLogger{},
|
|
||||||
r: &checker.DependencyUpdateToolData{
|
|
||||||
Tools: []checker.Tool{
|
|
||||||
{
|
|
||||||
Name: "DependencyUpdateTool",
|
|
||||||
Files: []checker.File{
|
|
||||||
{
|
|
||||||
Path: "/etc/dependency-update-tool.conf",
|
|
||||||
Snippet: `
|
|
||||||
[dependency-update-tool]
|
|
||||||
enabled = true
|
|
||||||
`,
|
|
||||||
Type: finding.FileTypeSource,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: checker.CheckResult{
|
want: checker.CheckResult{
|
||||||
Score: 10,
|
Score: 10,
|
||||||
Error: nil,
|
|
||||||
},
|
},
|
||||||
expected: scut.TestReturn{
|
|
||||||
Error: nil,
|
|
||||||
Score: 10,
|
|
||||||
NumberOfInfo: 1,
|
|
||||||
},
|
|
||||||
err: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "more than one tool in the list",
|
name: "renovate",
|
||||||
args: args{
|
findings: []finding.Finding{
|
||||||
name: "DependencyUpdateTool",
|
{
|
||||||
dl: &scut.TestDetailLogger{},
|
Probe: "toolRenovateInstalled",
|
||||||
r: &checker.DependencyUpdateToolData{
|
Outcome: finding.OutcomePositive,
|
||||||
Tools: []checker.Tool{
|
|
||||||
{
|
|
||||||
Name: "DependencyUpdateTool",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "DependencyUpdateTool",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: checker.CheckResult{
|
want: checker.CheckResult{
|
||||||
Score: -1,
|
Score: 10,
|
||||||
Error: nil,
|
|
||||||
},
|
},
|
||||||
expected: scut.TestReturn{
|
|
||||||
Error: sce.ErrScorecardInternal,
|
|
||||||
Score: -1,
|
|
||||||
},
|
|
||||||
err: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nil r",
|
name: "pyup",
|
||||||
args: args{
|
findings: []finding.Finding{
|
||||||
name: "nil r",
|
{
|
||||||
dl: &scut.TestDetailLogger{},
|
Probe: "toolPyUpInstalled",
|
||||||
|
Outcome: finding.OutcomePositive,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
want: checker.CheckResult{
|
want: checker.CheckResult{
|
||||||
Score: -1,
|
Score: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sonatype",
|
||||||
|
findings: []finding.Finding{
|
||||||
|
{
|
||||||
|
Probe: "toolSonatypeInstalled",
|
||||||
|
Outcome: finding.OutcomePositive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: checker.CheckResult{
|
||||||
|
Score: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "none",
|
||||||
|
findings: []finding.Finding{
|
||||||
|
{
|
||||||
|
Probe: "toolDependabotInstalled",
|
||||||
|
Outcome: finding.OutcomeNegative,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Probe: "toolRenovateInstalled",
|
||||||
|
Outcome: finding.OutcomeNegative,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Probe: "toolPyUpInstalled",
|
||||||
|
Outcome: finding.OutcomeNegative,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Probe: "toolSonatypeInstalled",
|
||||||
|
Outcome: finding.OutcomeNegative,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: checker.CheckResult{
|
||||||
|
Score: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty tool list",
|
||||||
|
want: checker.CheckResult{
|
||||||
|
Score: 0,
|
||||||
Error: nil,
|
Error: nil,
|
||||||
},
|
},
|
||||||
expected: scut.TestReturn{
|
|
||||||
Error: sce.ErrScorecardInternal,
|
|
||||||
Score: -1,
|
|
||||||
},
|
|
||||||
err: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -162,8 +117,7 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dl := scut.TestDetailLogger{}
|
got := DependencyUpdateTool(tt.name, tt.findings)
|
||||||
got := DependencyUpdateTool(tt.args.name, &dl, tt.args.r)
|
|
||||||
if tt.want.Score != got.Score {
|
if tt.want.Score != got.Score {
|
||||||
t.Errorf("DependencyUpdateTool() got Score = %v, want %v for %v", got.Score, tt.want.Score, tt.name)
|
t.Errorf("DependencyUpdateTool() got Score = %v, want %v for %v", got.Score, tt.want.Score, tt.name)
|
||||||
}
|
}
|
||||||
@ -171,10 +125,6 @@ func TestDependencyUpdateTool(t *testing.T) {
|
|||||||
t.Errorf("DependencyUpdateTool() error = %v, want %v for %v", got.Error, tt.want.Error, tt.name)
|
t.Errorf("DependencyUpdateTool() error = %v, want %v for %v", got.Error, tt.want.Error, tt.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !scut.ValidateTestReturn(t, tt.name, &tt.expected, &got, &dl) {
|
|
||||||
t.Fatalf(tt.name)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,13 +126,11 @@ var checkDependencyFileExists fileparser.DoWhileTrueOnFilename = func(name strin
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
|
||||||
// Continue iterating.
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We found a file, no need to continue iterating.
|
// Continue iterating, even if we have found a tool.
|
||||||
return false, nil
|
// It's needed for all probes results to be populated.
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func asPointer(s string) *string {
|
func asPointer(s string) *string {
|
||||||
|
@ -26,115 +26,84 @@ import (
|
|||||||
|
|
||||||
func Test_checkDependencyFileExists(t *testing.T) {
|
func Test_checkDependencyFileExists(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
//nolint
|
|
||||||
type args struct {
|
|
||||||
name string
|
|
||||||
data *[]checker.Tool
|
|
||||||
}
|
|
||||||
//nolint
|
//nolint
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
path string
|
||||||
want bool
|
want bool
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "check dependency file exists",
|
name: ".github/dependabot.yml",
|
||||||
args: args{
|
path: ".github/dependabot.yml",
|
||||||
name: ".github/dependabot.yml",
|
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: ".other",
|
|
||||||
args: args{
|
|
||||||
name: ".other",
|
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: true,
|
want: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".github/renovate.json",
|
name: ".github/dependabot.yaml",
|
||||||
args: args{
|
path: ".github/dependabot.yaml",
|
||||||
name: ".github/renovate.json",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: ".other",
|
||||||
|
path: ".other",
|
||||||
want: false,
|
want: false,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".github/renovate.json5",
|
name: ".github/renovate.json",
|
||||||
args: args{
|
path: ".github/renovate.json",
|
||||||
name: ".github/renovate.json5",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".renovaterc.json",
|
name: ".github/renovate.json5",
|
||||||
args: args{
|
path: ".github/renovate.json5",
|
||||||
name: ".renovaterc.json",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "renovate.json",
|
name: ".renovaterc.json",
|
||||||
args: args{
|
path: ".renovaterc.json",
|
||||||
name: "renovate.json",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "renovate.json5",
|
name: "renovate.json",
|
||||||
args: args{
|
path: "renovate.json",
|
||||||
name: "renovate.json5",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".renovaterc",
|
name: "renovate.json5",
|
||||||
args: args{
|
path: "renovate.json5",
|
||||||
name: ".renovaterc",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".pyup.yml",
|
name: ".renovaterc",
|
||||||
args: args{
|
path: ".renovaterc",
|
||||||
name: ".pyup.yml",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".lift.toml",
|
name: ".pyup.yml",
|
||||||
args: args{
|
path: ".pyup.yml",
|
||||||
name: ".lift.toml",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ".lift/config.toml",
|
name: ".lift.toml",
|
||||||
args: args{
|
path: ".lift.toml",
|
||||||
name: ".lift/config.toml",
|
want: true,
|
||||||
data: &[]checker.Tool{},
|
wantErr: false,
|
||||||
},
|
},
|
||||||
want: false,
|
{
|
||||||
|
name: ".lift/config.toml",
|
||||||
|
path: ".lift/config.toml",
|
||||||
|
want: true,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -142,13 +111,17 @@ func Test_checkDependencyFileExists(t *testing.T) {
|
|||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
got, err := checkDependencyFileExists(tt.args.name, tt.args.data)
|
results := []checker.Tool{}
|
||||||
|
cont, err := checkDependencyFileExists(tt.path, &results)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("checkDependencyFileExists() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("checkDependencyFileExists() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if got != tt.want {
|
if !cont {
|
||||||
t.Errorf("checkDependencyFileExists() = %v, want %v for test %v", got, tt.want, tt.name)
|
t.Errorf("continue is false for %v", tt.name)
|
||||||
|
}
|
||||||
|
if tt.want != (len(results) == 1) {
|
||||||
|
t.Errorf("checkDependencyFileExists() = %v, want %v for test %v", len(results), tt.want, tt.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
41
checks/run_probes.go
Normal file
41
checks/run_probes.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 checks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/zrunner"
|
||||||
|
)
|
||||||
|
|
||||||
|
// evaluateProbes runs the probes in probesToRun and logs its findings.
|
||||||
|
func evaluateProbes(c *checker.CheckRequest, checkName string,
|
||||||
|
probesToRun []probes.ProbeImpl,
|
||||||
|
) ([]finding.Finding, error) {
|
||||||
|
// Run the probes.
|
||||||
|
findings, err := zrunner.Run(c.RawResults, probesToRun)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("zrunner.Run: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the findings.
|
||||||
|
if err := checker.LogFindings(findings, c.Dlogger); err != nil {
|
||||||
|
return nil, fmt.Errorf("LogFindings: %w", err)
|
||||||
|
}
|
||||||
|
return findings, nil
|
||||||
|
}
|
@ -39,16 +39,18 @@ var _ = Describe("E2E TEST:"+checks.CheckDependencyUpdateTool, func() {
|
|||||||
err = repoClient.InitRepo(repo, clients.HeadSHA, 0)
|
err = repoClient.InitRepo(repo, clients.HeadSHA, 0)
|
||||||
Expect(err).Should(BeNil())
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
|
raw := checker.RawResults{}
|
||||||
req := checker.CheckRequest{
|
req := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
RepoClient: repoClient,
|
RepoClient: repoClient,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Dlogger: &dl,
|
Dlogger: &dl,
|
||||||
|
RawResults: &raw,
|
||||||
}
|
}
|
||||||
expected := scut.TestReturn{
|
expected := scut.TestReturn{
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Score: checker.MaxResultScore,
|
Score: checker.MaxResultScore,
|
||||||
NumberOfWarn: 0,
|
NumberOfWarn: 3,
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
NumberOfDebug: 0,
|
NumberOfDebug: 0,
|
||||||
}
|
}
|
||||||
@ -66,16 +68,18 @@ var _ = Describe("E2E TEST:"+checks.CheckDependencyUpdateTool, func() {
|
|||||||
err = repoClient.InitRepo(repo, clients.HeadSHA, 0)
|
err = repoClient.InitRepo(repo, clients.HeadSHA, 0)
|
||||||
Expect(err).Should(BeNil())
|
Expect(err).Should(BeNil())
|
||||||
|
|
||||||
|
raw := checker.RawResults{}
|
||||||
req := checker.CheckRequest{
|
req := checker.CheckRequest{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
RepoClient: repoClient,
|
RepoClient: repoClient,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Dlogger: &dl,
|
Dlogger: &dl,
|
||||||
|
RawResults: &raw,
|
||||||
}
|
}
|
||||||
expected := scut.TestReturn{
|
expected := scut.TestReturn{
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Score: checker.MaxResultScore,
|
Score: checker.MaxResultScore,
|
||||||
NumberOfWarn: 0,
|
NumberOfWarn: 3,
|
||||||
NumberOfInfo: 1,
|
NumberOfInfo: 1,
|
||||||
NumberOfDebug: 0,
|
NumberOfDebug: 0,
|
||||||
}
|
}
|
||||||
|
59
probes/entries.go
Normal file
59
probes/entries.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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 probes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/toolDependabotInstalled"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/toolPyUpInstalled"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/toolRenovateInstalled"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/toolSonatypeLiftInstalled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProbeImpl is the implementation of a probe.
|
||||||
|
type ProbeImpl func(*checker.RawResults) ([]finding.Finding, string, error)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// All represents all the probes.
|
||||||
|
All []ProbeImpl
|
||||||
|
// DependencyToolUpdates is all the probes for the
|
||||||
|
// DpendencyUpdateTool check.
|
||||||
|
DependencyToolUpdates = []ProbeImpl{
|
||||||
|
toolRenovateInstalled.Run,
|
||||||
|
toolDependabotInstalled.Run,
|
||||||
|
toolPyUpInstalled.Run,
|
||||||
|
toolSonatypeLiftInstalled.Run,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoinits
|
||||||
|
func init() {
|
||||||
|
All = concatMultipleProbes([][]ProbeImpl{
|
||||||
|
DependencyToolUpdates,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func concatMultipleProbes(slices [][]ProbeImpl) []ProbeImpl {
|
||||||
|
var totalLen int
|
||||||
|
for _, s := range slices {
|
||||||
|
totalLen += len(s)
|
||||||
|
}
|
||||||
|
tmp := make([]ProbeImpl, 0, totalLen)
|
||||||
|
for _, s := range slices {
|
||||||
|
tmp = append(tmp, s...)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
32
probes/toolDependabotInstalled/def.yml
Normal file
32
probes/toolDependabotInstalled/def.yml
Normal file
@ -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: toolDependabotInstalled
|
||||||
|
short: Check that Dependabot is enabled
|
||||||
|
motivation: >
|
||||||
|
Out-of-date dependencies make a project vulnerable to known flaws and prone to attacks.
|
||||||
|
Dependabot automates the process of updating dependencies by scanning for outdated or insecure requirements, and opening a pull request to update them if found.
|
||||||
|
implementation: >
|
||||||
|
The implemtation looks for the presence of files named ".github/dependabot.yml" or ".github/dependabot.yaml". If none of these files are found,
|
||||||
|
the implementation checks whether commits are authored by Dependabot. If none of these succeed, Dependabot is not installed.
|
||||||
|
NOTE: if the configuration files are found, the probe does not ensure that the Dependabot is run or that the Dependabot's pull requests are merged.
|
||||||
|
outcome:
|
||||||
|
- If dependendabot is installed, the probe returns OutcomePositive (1)
|
||||||
|
- If dependendabot is not installed, the probe returns OutcomeNegative (0)
|
||||||
|
remediation:
|
||||||
|
effort: Low
|
||||||
|
text:
|
||||||
|
- Follow the instructions from https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates.
|
||||||
|
markdown:
|
||||||
|
- Follow the instructions from [the official documentation](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates).
|
53
probes/toolDependabotInstalled/impl.go
Normal file
53
probes/toolDependabotInstalled/impl.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2022 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 toolDependabotInstalled
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.yml
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
const probe = "toolDependabotInstalled"
|
||||||
|
|
||||||
|
type dependabot struct{}
|
||||||
|
|
||||||
|
func (t dependabot) Name() string {
|
||||||
|
return "Dependabot"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t dependabot) Matches(tool *checker.Tool) bool {
|
||||||
|
return t.Name() == tool.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(raw *checker.RawResults) ([]finding.Finding, string, error) {
|
||||||
|
tools := raw.DependencyUpdateToolResults.Tools
|
||||||
|
var matcher dependabot
|
||||||
|
// Check whether Dependabot tool is installed on the repo,
|
||||||
|
// and create the corresponding findings.
|
||||||
|
//nolint:wrapcheck
|
||||||
|
return utils.ToolsRun(tools, fs, probe,
|
||||||
|
// Tool found will generate a positive result.
|
||||||
|
finding.OutcomePositive,
|
||||||
|
// Tool not found will generate a negative result.
|
||||||
|
finding.OutcomeNegative,
|
||||||
|
matcher)
|
||||||
|
}
|
32
probes/toolPyUpInstalled/def.yml
Normal file
32
probes/toolPyUpInstalled/def.yml
Normal file
@ -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: toolPyUpInstalled
|
||||||
|
short: Check that PyUp is installed.
|
||||||
|
motivation: >
|
||||||
|
Out-of-date dependencies make a project vulnerable to known flaws and prone to attacks.
|
||||||
|
PyUp automates the process of updating dependencies by scanning for outdated or insecure requirements, and opening a pull request to update them if found.
|
||||||
|
implementation: >
|
||||||
|
The implementation looks for the presence of a file named ".pyup.yml".
|
||||||
|
If the file is not found, PyUp is not installed.
|
||||||
|
NOTE: the implementation does not ensure that PyUp is run or that PyUp's pull requests are merged.
|
||||||
|
outcome:
|
||||||
|
- If PyUp is installed, the probe returns OutcomePositive (1)
|
||||||
|
- If PyUp is not installed, the probe returns OutcomeNegative (0)
|
||||||
|
remediation:
|
||||||
|
effort: Low
|
||||||
|
text:
|
||||||
|
- Follow the instructions from https://docs.pyup.io/docs.
|
||||||
|
markdown:
|
||||||
|
- Follow the instructions from [the official documentation](https://docs.pyup.io/docs).
|
53
probes/toolPyUpInstalled/impl.go
Normal file
53
probes/toolPyUpInstalled/impl.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2022 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 toolPyUpInstalled
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.yml
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
const probe = "toolPyUpInstalled"
|
||||||
|
|
||||||
|
type pyup struct{}
|
||||||
|
|
||||||
|
func (t pyup) Name() string {
|
||||||
|
return "PyUp"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t pyup) Matches(tool *checker.Tool) bool {
|
||||||
|
return t.Name() == tool.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(raw *checker.RawResults) ([]finding.Finding, string, error) {
|
||||||
|
tools := raw.DependencyUpdateToolResults.Tools
|
||||||
|
var matcher pyup
|
||||||
|
// Check whether PyUp tool is installed on the repo,
|
||||||
|
// and create the corresponding findings.
|
||||||
|
//nolint:wrapcheck
|
||||||
|
return utils.ToolsRun(tools, fs, probe,
|
||||||
|
// Tool found will generate a positive result.
|
||||||
|
finding.OutcomePositive,
|
||||||
|
// Tool not found will generate a negative result.
|
||||||
|
finding.OutcomeNegative,
|
||||||
|
matcher)
|
||||||
|
}
|
32
probes/toolRenovateInstalled/def.yml
Normal file
32
probes/toolRenovateInstalled/def.yml
Normal file
@ -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: toolRenovateInstalled
|
||||||
|
short: Check that Renovate bot is installed.
|
||||||
|
motivation: >
|
||||||
|
Out-of-date dependencies make a project vulnerable to known flaws and prone to attacks.
|
||||||
|
Renovate automates the process of updating dependencies by scanning for outdated or insecure requirements, and opening a pull request to update them if found.
|
||||||
|
implementation: >
|
||||||
|
The implementation looks for the presence of files named ".github/renovate.json", ".github/renovate.json5", ".renovaterc.json" or. "renovate.json".
|
||||||
|
If none of these files are found, Renovate is not installed.
|
||||||
|
NOTE: the implementation does not ensure that Renovate is run or that Renovate's pull requests are merged.
|
||||||
|
outcome:
|
||||||
|
- If Renovate is installed, the probe returns OutcomePositive (1)
|
||||||
|
- If Renovate is not installed, the probe returns OutcomeNegative (0)
|
||||||
|
remediation:
|
||||||
|
effort: Low
|
||||||
|
text:
|
||||||
|
- Follow the instructions from https://docs.renovatebot.com/configuration-options/.
|
||||||
|
markdown:
|
||||||
|
- Follow the instructions from [the official documentation](https://docs.renovatebot.com/configuration-options/).
|
53
probes/toolRenovateInstalled/impl.go
Normal file
53
probes/toolRenovateInstalled/impl.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2022 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 toolRenovateInstalled
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.yml
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
const probe = "toolRenovateInstalled"
|
||||||
|
|
||||||
|
type renovate struct{}
|
||||||
|
|
||||||
|
func (t renovate) Name() string {
|
||||||
|
return "RenovateBot"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t renovate) Matches(tool *checker.Tool) bool {
|
||||||
|
return t.Name() == tool.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(raw *checker.RawResults) ([]finding.Finding, string, error) {
|
||||||
|
tools := raw.DependencyUpdateToolResults.Tools
|
||||||
|
var matcher renovate
|
||||||
|
// Check whether Renovate tool is installed on the repo,
|
||||||
|
// and create the corresponding findings.
|
||||||
|
//nolint:wrapcheck
|
||||||
|
return utils.ToolsRun(tools, fs, probe,
|
||||||
|
// Tool found will generate a positive result.
|
||||||
|
finding.OutcomePositive,
|
||||||
|
// Tool not found will generate a negative result.
|
||||||
|
finding.OutcomeNegative,
|
||||||
|
matcher)
|
||||||
|
}
|
32
probes/toolSonatypeLiftInstalled/def.yml
Normal file
32
probes/toolSonatypeLiftInstalled/def.yml
Normal file
@ -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: toolSonatypeLiftInstalled
|
||||||
|
short: Check that Sonatype Lyft is installed.
|
||||||
|
motivation: >
|
||||||
|
Out-of-date dependencies make a project vulnerable to known flaws and prone to attacks.
|
||||||
|
Sonatype Lyft automates the process of updating dependencies by scanning for outdated or insecure requirements, and opening a pull request to update them if found.
|
||||||
|
implementation: >
|
||||||
|
The implementation looks for the presence of files named ".lift.toml" or ".lift/config.toml".
|
||||||
|
If none of these files are found, Sonatype Lyft is not installed.
|
||||||
|
NOTE: the implementation does not ensure that Sonatype Lyft is run or that Sonatype Lyft's pull requests are merged.
|
||||||
|
outcome:
|
||||||
|
- If Sonatype Lyft is installed, the probe returns OutcomePositive (1)
|
||||||
|
- If Sonatype Lyft is not installed, the probe returns OutcomeNegative (0)
|
||||||
|
remediation:
|
||||||
|
effort: Low
|
||||||
|
text:
|
||||||
|
- Follow the instructions from https://help.sonatype.com/lift/getting-started.
|
||||||
|
markdown:
|
||||||
|
- Follow the instructions from [the official documentation](https://help.sonatype.com/lift/getting-started).
|
53
probes/toolSonatypeLiftInstalled/impl.go
Normal file
53
probes/toolSonatypeLiftInstalled/impl.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2022 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 toolSonatypeLiftInstalled
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.yml
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
const probe = "toolSonatypeLiftInstalled"
|
||||||
|
|
||||||
|
type sonatypeLyft struct{}
|
||||||
|
|
||||||
|
func (t sonatypeLyft) Name() string {
|
||||||
|
return "Sonatype Lift"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t sonatypeLyft) Matches(tool *checker.Tool) bool {
|
||||||
|
return t.Name() == tool.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(raw *checker.RawResults) ([]finding.Finding, string, error) {
|
||||||
|
tools := raw.DependencyUpdateToolResults.Tools
|
||||||
|
var matcher sonatypeLyft
|
||||||
|
// Check whether Sona Lyft tool is installed on the repo,
|
||||||
|
// and create the corresponding findings.
|
||||||
|
//nolint:wrapcheck
|
||||||
|
return utils.ToolsRun(tools, fs, probe,
|
||||||
|
// Tool found will generate a positive result.
|
||||||
|
finding.OutcomePositive,
|
||||||
|
// Tool not found will generate a negative result.
|
||||||
|
finding.OutcomeNegative,
|
||||||
|
matcher)
|
||||||
|
}
|
69
probes/utils/tools.go
Normal file
69
probes/utils/tools.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type toolMatcher interface {
|
||||||
|
Name() string
|
||||||
|
Matches(*checker.Tool) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToolsRun runs the probe for a tool.
|
||||||
|
// The function iterates thru the raw results and searches for a tool of interest that is used on a repository.
|
||||||
|
// The function uses 'matcher' to identify the tool of interest.
|
||||||
|
// If a tool is used in the repository, it creates a finding with the 'foundOutcome'.
|
||||||
|
// If not, it returns a finding with outcome 'notFoundOutcome'.
|
||||||
|
func ToolsRun(tools []checker.Tool, fs embed.FS, probeID string,
|
||||||
|
foundOutcome, notFoundOutcome finding.Outcome, matcher toolMatcher,
|
||||||
|
) ([]finding.Finding, string, error) {
|
||||||
|
var findings []finding.Finding
|
||||||
|
for i := range tools {
|
||||||
|
tool := &tools[i]
|
||||||
|
if !matcher.Matches(tool) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var loc *finding.Location
|
||||||
|
if len(tool.Files) > 0 {
|
||||||
|
loc = tool.Files[0].Location()
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := finding.NewWith(fs, probeID, fmt.Sprintf("tool '%s' is used", tool.Name),
|
||||||
|
loc, foundOutcome)
|
||||||
|
if err != nil {
|
||||||
|
return nil, probeID, fmt.Errorf("create finding: %w", err)
|
||||||
|
}
|
||||||
|
findings = append(findings, *f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No tools found.
|
||||||
|
if len(findings) == 0 {
|
||||||
|
f, err := finding.NewWith(fs, probeID, fmt.Sprintf("tool '%s' is not used", matcher.Name()),
|
||||||
|
nil, notFoundOutcome)
|
||||||
|
if err != nil {
|
||||||
|
return nil, probeID, fmt.Errorf("create finding: %w", err)
|
||||||
|
}
|
||||||
|
findings = append(findings, *f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return findings, probeID, nil
|
||||||
|
}
|
51
probes/zrunner/runner.go
Normal file
51
probes/zrunner/runner.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 zrunner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ossf/scorecard/v4/checker"
|
||||||
|
serrors "github.com/ossf/scorecard/v4/errors"
|
||||||
|
"github.com/ossf/scorecard/v4/finding"
|
||||||
|
"github.com/ossf/scorecard/v4/probes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errProbeRun = errors.New("probe run failure")
|
||||||
|
|
||||||
|
// Run runs the probes in probesToRun.
|
||||||
|
func Run(raw *checker.RawResults, probesToRun []probes.ProbeImpl) ([]finding.Finding, error) {
|
||||||
|
var results []finding.Finding
|
||||||
|
var errs []error
|
||||||
|
for _, probeFunc := range probesToRun {
|
||||||
|
findings, probeID, err := probeFunc(raw)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
results = append(results,
|
||||||
|
finding.Finding{
|
||||||
|
Probe: probeID,
|
||||||
|
Outcome: finding.OutcomeError,
|
||||||
|
Message: serrors.WithMessage(serrors.ErrScorecardInternal, err.Error()).Error(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, findings...)
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return results, fmt.Errorf("%w: %v", errProbeRun, errs)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user