mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-11 00:45:36 +03:00
✨ [RAW]: dependency update tool (#1391)
* dependency update tool * rename * missing files * add fields * rm field
This commit is contained in:
parent
cef72f0f7d
commit
f2cee41ca9
@ -139,6 +139,38 @@ type SecurityPolicyData struct {
|
||||
Files []File
|
||||
}
|
||||
|
||||
// Run represents a run.
|
||||
type Run struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., Result=["success", "failure"]
|
||||
}
|
||||
|
||||
// Issue represents an issue.
|
||||
type Issue struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., state=[opened|closed]
|
||||
}
|
||||
|
||||
// MergeRequest represents a merge request.
|
||||
type MergeRequest struct {
|
||||
URL string
|
||||
// TODO: add fields, e.g., State=["merged"|"closed"]
|
||||
}
|
||||
|
||||
// Tool represents a tool.
|
||||
type Tool struct {
|
||||
// Runs of the tool.
|
||||
Runs []Run
|
||||
// Issues created by the tool.
|
||||
Issues []Issue
|
||||
// Merges requests created by the tool.
|
||||
MergeRequests []MergeRequest
|
||||
Name string
|
||||
URL string
|
||||
Desc string
|
||||
ConfigFiles []File
|
||||
}
|
||||
|
||||
// BinaryArtifactData contains the raw results
|
||||
// for the Binary-Artifact check.
|
||||
type BinaryArtifactData struct {
|
||||
@ -146,11 +178,20 @@ type BinaryArtifactData struct {
|
||||
Files []File
|
||||
}
|
||||
|
||||
// DependencyUpdateToolData contains the raw results
|
||||
// for the Dependency-Update-Tool check.
|
||||
type DependencyUpdateToolData struct {
|
||||
// Tools contains a list of tools.
|
||||
// Note: we only populate one entry at most.
|
||||
Tools []Tool
|
||||
}
|
||||
|
||||
// RawResults contains results before a policy
|
||||
// is applied.
|
||||
type RawResults struct {
|
||||
BinaryArtifactResults BinaryArtifactData
|
||||
SecurityPolicyResults SecurityPolicyData
|
||||
BinaryArtifactResults BinaryArtifactData
|
||||
SecurityPolicyResults SecurityPolicyData
|
||||
DependencyUpdateToolResults DependencyUpdateToolData
|
||||
}
|
||||
|
||||
// CreateProportionalScore creates a proportional score.
|
||||
|
@ -15,10 +15,9 @@
|
||||
package checks
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ossf/scorecard/v3/checker"
|
||||
"github.com/ossf/scorecard/v3/checks/fileparser"
|
||||
"github.com/ossf/scorecard/v3/checks/evaluation"
|
||||
"github.com/ossf/scorecard/v3/checks/raw"
|
||||
sce "github.com/ossf/scorecard/v3/errors"
|
||||
)
|
||||
|
||||
@ -27,60 +26,23 @@ const CheckDependencyUpdateTool = "Dependency-Update-Tool"
|
||||
|
||||
//nolint
|
||||
func init() {
|
||||
registerCheck(CheckDependencyUpdateTool, UsesDependencyUpdateTool)
|
||||
registerCheck(CheckDependencyUpdateTool, DependencyUpdateTool)
|
||||
}
|
||||
|
||||
// UsesDependencyUpdateTool will check the repository uses a dependency update tool.
|
||||
func UsesDependencyUpdateTool(c *checker.CheckRequest) checker.CheckResult {
|
||||
var r bool
|
||||
err := fileparser.CheckIfFileExists(c, fileExists, &r)
|
||||
// DependencyUpdateTool checks if the repository uses a dependency update tool.
|
||||
func DependencyUpdateTool(c *checker.CheckRequest) checker.CheckResult {
|
||||
rawData, err := raw.DependencyUpdateTool(c.RepoClient)
|
||||
if err != nil {
|
||||
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||
return checker.CreateRuntimeErrorResult(CheckDependencyUpdateTool, e)
|
||||
}
|
||||
if !r {
|
||||
c.Dlogger.Warn3(&checker.LogMessage{
|
||||
Text: `dependabot config file not detected in source location.
|
||||
We recommend setting this configuration in code so it can be easily verified by others.`,
|
||||
})
|
||||
c.Dlogger.Warn3(&checker.LogMessage{
|
||||
Text: `renovatebot config file not detected in source location.
|
||||
We recommend setting this configuration in code so it can be easily verified by others.`,
|
||||
})
|
||||
return checker.CreateMinScoreResult(CheckDependencyUpdateTool, "no update tool detected")
|
||||
|
||||
// Return raw results.
|
||||
if c.RawResults != nil {
|
||||
c.RawResults.DependencyUpdateToolResults = rawData
|
||||
return checker.CheckResult{}
|
||||
}
|
||||
|
||||
// High score result.
|
||||
return checker.CreateMaxScoreResult(CheckDependencyUpdateTool, "update tool detected")
|
||||
}
|
||||
|
||||
// fileExists will validate the if frozen dependencies file name exists.
|
||||
func fileExists(name string, dl checker.DetailLogger, data fileparser.FileCbData) (bool, error) {
|
||||
pdata := fileparser.FileGetCbDataAsBoolPointer(data)
|
||||
|
||||
switch strings.ToLower(name) {
|
||||
case ".github/dependabot.yml":
|
||||
dl.Info3(&checker.LogMessage{
|
||||
Path: name,
|
||||
Type: checker.FileTypeSource,
|
||||
Offset: checker.OffsetDefault,
|
||||
Text: "dependabot detected",
|
||||
})
|
||||
// https://docs.renovatebot.com/configuration-options/
|
||||
case ".github/renovate.json", ".github/renovate.json5", ".renovaterc.json", "renovate.json",
|
||||
"renovate.json5", ".renovaterc":
|
||||
dl.Info3(&checker.LogMessage{
|
||||
Path: name,
|
||||
Type: checker.FileTypeSource,
|
||||
Offset: checker.OffsetDefault,
|
||||
Text: "renovate detected",
|
||||
})
|
||||
default:
|
||||
// Continue iterating.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
*pdata = true
|
||||
// We found the file, no need to continue iterating.
|
||||
return false, nil
|
||||
// Return the score evaluation.
|
||||
return evaluation.DependencyUpdateTool(CheckDependencyUpdateTool, c.Dlogger, &rawData)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
sce "github.com/ossf/scorecard/v3/errors"
|
||||
)
|
||||
|
||||
// BinaryArtifacts applies the score policy for the Binary-Artiacts check.
|
||||
// BinaryArtifacts applies the score policy for the Binary-Artifacts check.
|
||||
func BinaryArtifacts(name string, dl checker.DetailLogger,
|
||||
r *checker.BinaryArtifactData) checker.CheckResult {
|
||||
if r == nil {
|
||||
|
68
checks/evaluation/dependency_update_tool.go
Normal file
68
checks/evaluation/dependency_update_tool.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2020 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package evaluation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ossf/scorecard/v3/checker"
|
||||
sce "github.com/ossf/scorecard/v3/errors"
|
||||
)
|
||||
|
||||
// DependencyUpdateTool applies the score policy for the Dependency-Update-Tool check.
|
||||
func DependencyUpdateTool(name string, dl checker.DetailLogger,
|
||||
r *checker.DependencyUpdateToolData) checker.CheckResult {
|
||||
if r == nil {
|
||||
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
|
||||
return checker.CreateRuntimeErrorResult(name, e)
|
||||
}
|
||||
|
||||
// Apply the policy evaluation.
|
||||
if r.Tools == nil || len(r.Tools) == 0 {
|
||||
dl.Warn3(&checker.LogMessage{
|
||||
Text: `dependabot config file not detected in source location.
|
||||
We recommend setting this configuration in code so it can be easily verified by others.`,
|
||||
})
|
||||
dl.Warn3(&checker.LogMessage{
|
||||
Text: `renovatebot config file not detected in source location.
|
||||
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 len(r.Tools[0].ConfigFiles) != 1 {
|
||||
e := sce.WithMessage(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("found %d config files, expected 1", len(r.Tools[0].ConfigFiles)))
|
||||
return checker.CreateRuntimeErrorResult(name, e)
|
||||
}
|
||||
|
||||
// Note: only one file per tool is present,
|
||||
// so we do not iterate thru all entries.
|
||||
dl.Info3(&checker.LogMessage{
|
||||
Path: r.Tools[0].ConfigFiles[0].Path,
|
||||
Type: r.Tools[0].ConfigFiles[0].Type,
|
||||
Offset: r.Tools[0].ConfigFiles[0].Offset,
|
||||
Text: fmt.Sprintf("%s detected", r.Tools[0].Name),
|
||||
})
|
||||
|
||||
// High score result.
|
||||
return checker.CreateMaxScoreResult(name, "update tool detected")
|
||||
}
|
@ -168,6 +168,34 @@ func CheckFilesContentV6(shellPathFnPattern string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileCbV6 is the callback.
|
||||
// The bool returned indicates whether the FileCbData
|
||||
// should continue iterating over files or not.
|
||||
type FileCbV6 func(path string, data FileCbData) (bool, error)
|
||||
|
||||
// CheckIfFileExistsV6 downloads the tar of the repository and calls the onFile() to check
|
||||
// for the occurrence.
|
||||
func CheckIfFileExistsV6(repoClient clients.RepoClient,
|
||||
onFile FileCbV6, data FileCbData) error {
|
||||
matchedFiles, err := repoClient.ListFiles(func(string) (bool, error) { return true, nil })
|
||||
if err != nil {
|
||||
// nolint: wrapcheck
|
||||
return err
|
||||
}
|
||||
for _, filename := range matchedFiles {
|
||||
continueIter, err := onFile(filename, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !continueIter {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileCb represents a callback fn.
|
||||
type FileCb func(path string,
|
||||
dl checker.DetailLogger, data FileCbData) (bool, error)
|
||||
|
82
checks/raw/dependency_update_tool.go
Normal file
82
checks/raw/dependency_update_tool.go
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2020 Security Scorecard Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package raw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ossf/scorecard/v3/checker"
|
||||
"github.com/ossf/scorecard/v3/checks/fileparser"
|
||||
"github.com/ossf/scorecard/v3/clients"
|
||||
)
|
||||
|
||||
// DependencyUpdateTool is the exported name for Depdendency-Update-Tool.
|
||||
func DependencyUpdateTool(c clients.RepoClient) (checker.DependencyUpdateToolData, error) {
|
||||
var tools []checker.Tool
|
||||
err := fileparser.CheckIfFileExistsV6(c, checkDependencyFileExists, &tools)
|
||||
if err != nil {
|
||||
return checker.DependencyUpdateToolData{}, fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
// No error, return the files.
|
||||
return checker.DependencyUpdateToolData{Tools: tools}, nil
|
||||
}
|
||||
|
||||
func checkDependencyFileExists(name string, data fileparser.FileCbData) (bool, error) {
|
||||
ptools, ok := data.(*[]checker.Tool)
|
||||
if !ok {
|
||||
// This never happens.
|
||||
panic("invalid type")
|
||||
}
|
||||
|
||||
switch strings.ToLower(name) {
|
||||
case ".github/dependabot.yml":
|
||||
*ptools = append(*ptools, checker.Tool{
|
||||
Name: "Dependabot",
|
||||
URL: "https://github.com/dependabot",
|
||||
Desc: "Automated dependency updates built into GitHub",
|
||||
ConfigFiles: []checker.File{
|
||||
{
|
||||
Path: name,
|
||||
Type: checker.FileTypeSource,
|
||||
Offset: checker.OffsetDefault,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// https://docs.renovatebot.com/configuration-options/
|
||||
case ".github/renovate.json", ".github/renovate.json5", ".renovaterc.json", "renovate.json",
|
||||
"renovate.json5", ".renovaterc":
|
||||
*ptools = append(*ptools, checker.Tool{
|
||||
Name: "Renovabot",
|
||||
URL: "https://github.com/renovatebot/renovate",
|
||||
Desc: "Automated dependency updates. Multi-platform and multi-language.",
|
||||
ConfigFiles: []checker.File{
|
||||
{
|
||||
Path: name,
|
||||
Type: checker.FileTypeSource,
|
||||
Offset: checker.OffsetDefault,
|
||||
},
|
||||
},
|
||||
})
|
||||
default:
|
||||
// Continue iterating.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We found a file, no need to continue iterating.
|
||||
return false, nil
|
||||
}
|
@ -52,7 +52,7 @@ var _ = Describe("E2E TEST:"+checks.CheckDependencyUpdateTool, func() {
|
||||
NumberOfDebug: 0,
|
||||
}
|
||||
|
||||
result := checks.UsesDependencyUpdateTool(&req)
|
||||
result := checks.DependencyUpdateTool(&req)
|
||||
// UPGRADEv2: to remove.
|
||||
// Old version.
|
||||
Expect(result.Error).Should(BeNil())
|
||||
@ -82,7 +82,7 @@ var _ = Describe("E2E TEST:"+checks.CheckDependencyUpdateTool, func() {
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
}
|
||||
result := checks.UsesDependencyUpdateTool(&req)
|
||||
result := checks.DependencyUpdateTool(&req)
|
||||
// UPGRADEv2: to remove.
|
||||
// Old version.
|
||||
Expect(result.Error).Should(BeNil())
|
||||
|
@ -33,24 +33,35 @@ type jsonScorecardRawResult struct {
|
||||
}
|
||||
|
||||
// TODO: separate each check extraction into its own file.
|
||||
type jsonFiles struct {
|
||||
type jsonFile struct {
|
||||
Path string `json:"path"`
|
||||
Offset int `json:"offset,omitempty"`
|
||||
}
|
||||
|
||||
type jsonTool struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Desc string `json:"desc"`
|
||||
ConfigFiles []jsonFile `json:"files"`
|
||||
// TODO: Runs, Issues, Merge requests.
|
||||
}
|
||||
|
||||
type jsonRawResults struct {
|
||||
// List of binaries found in the repo.
|
||||
Binaries []jsonFiles `json:"binaries"`
|
||||
Binaries []jsonFile `json:"binaries"`
|
||||
// List of security policy files found in the repo.
|
||||
// Note: we return one at most.
|
||||
SecurityPolicies []jsonFiles `json:"security-policies"`
|
||||
SecurityPolicies []jsonFile `json:"security-policies"`
|
||||
// List of update tools.
|
||||
// Note: we return one at most.
|
||||
DependencyUpdateTools []jsonTool `json:"dependency-update-tools"`
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (r *jsonScorecardRawResult) addBinaryArtifactRawResults(ba *checker.BinaryArtifactData) error {
|
||||
r.Results.Binaries = []jsonFiles{}
|
||||
r.Results.Binaries = []jsonFile{}
|
||||
for _, v := range ba.Files {
|
||||
r.Results.Binaries = append(r.Results.Binaries, jsonFiles{
|
||||
r.Results.Binaries = append(r.Results.Binaries, jsonFile{
|
||||
Path: v.Path,
|
||||
})
|
||||
}
|
||||
@ -58,16 +69,38 @@ func (r *jsonScorecardRawResult) addBinaryArtifactRawResults(ba *checker.BinaryA
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (r *jsonScorecardRawResult) addSecurityPolicyRawResults(ba *checker.SecurityPolicyData) error {
|
||||
r.Results.SecurityPolicies = []jsonFiles{}
|
||||
for _, v := range ba.Files {
|
||||
r.Results.SecurityPolicies = append(r.Results.SecurityPolicies, jsonFiles{
|
||||
func (r *jsonScorecardRawResult) addSecurityPolicyRawResults(sp *checker.SecurityPolicyData) error {
|
||||
r.Results.SecurityPolicies = []jsonFile{}
|
||||
for _, v := range sp.Files {
|
||||
r.Results.SecurityPolicies = append(r.Results.SecurityPolicies, jsonFile{
|
||||
Path: v.Path,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (r *jsonScorecardRawResult) addDependencyUpdateToolRawResults(dut *checker.DependencyUpdateToolData) error {
|
||||
r.Results.DependencyUpdateTools = []jsonTool{}
|
||||
for i := range dut.Tools {
|
||||
t := dut.Tools[i]
|
||||
offset := len(r.Results.DependencyUpdateTools)
|
||||
r.Results.DependencyUpdateTools = append(r.Results.DependencyUpdateTools, jsonTool{
|
||||
Name: t.Name,
|
||||
URL: t.URL,
|
||||
Desc: t.Desc,
|
||||
})
|
||||
for _, f := range t.ConfigFiles {
|
||||
r.Results.DependencyUpdateTools[offset].ConfigFiles =
|
||||
append(r.Results.DependencyUpdateTools[offset].ConfigFiles, jsonFile{
|
||||
Path: f.Path,
|
||||
Offset: f.Offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) error {
|
||||
// Binary-Artifacts.
|
||||
if err := r.addBinaryArtifactRawResults(&raw.BinaryArtifactResults); err != nil {
|
||||
@ -78,6 +111,12 @@ func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) err
|
||||
if err := r.addSecurityPolicyRawResults(&raw.SecurityPolicyResults); err != nil {
|
||||
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||
}
|
||||
|
||||
// Dependecy-Update-Tool.
|
||||
if err := r.addDependencyUpdateToolRawResults(&raw.DependencyUpdateToolResults); err != nil {
|
||||
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user