mirror of
https://github.com/ossf/scorecard.git
synced 2024-09-17 11:57:12 +03:00
✨ cleanup Frozen-Deps MakeResultAnd
(#742)
* draft * fixes * commi 1 * delete file * clean * clean 2 * linter * fix score * handle err * in-proress score * fixes
This commit is contained in:
parent
8128f9fe68
commit
a004ffb107
@ -98,24 +98,24 @@ func CreateProportionalScore(b, t int) int {
|
||||
// and normalizes the result.
|
||||
// Each score contributes equally.
|
||||
func AggregateScores(scores ...int) int {
|
||||
n := len(scores)
|
||||
r := float32(0)
|
||||
n := float64(len(scores))
|
||||
r := 0
|
||||
for _, s := range scores {
|
||||
r += float32(s) / float32(MaxResultScore) * float32(n)
|
||||
r += s
|
||||
}
|
||||
return int(r)
|
||||
return int(math.Floor(float64(r) / n))
|
||||
}
|
||||
|
||||
// AggregateScoresWithWeight adds up all scores
|
||||
// and normalizes the result.
|
||||
// The caller is responsible for ensuring the sum of
|
||||
// weights is 10.
|
||||
func AggregateScoresWithWeight(scores map[int]int) int {
|
||||
r := float32(0)
|
||||
r := 0
|
||||
ws := 0
|
||||
for s, w := range scores {
|
||||
r += float32(s) / float32(MaxResultScore) * float32(w)
|
||||
r += s * w
|
||||
ws += w
|
||||
}
|
||||
return int(r)
|
||||
return int(math.Floor(float64(r) / float64(ws)))
|
||||
}
|
||||
|
||||
func NormalizeReason(reason string, score int) string {
|
||||
|
@ -132,17 +132,6 @@ func MultiCheckOr2(fns ...CheckFn) CheckFn {
|
||||
}
|
||||
}
|
||||
|
||||
func MultiCheckAnd2(fns ...CheckFn) CheckFn {
|
||||
return func(c *CheckRequest) CheckResult {
|
||||
var checks []CheckResult
|
||||
for _, fn := range fns {
|
||||
res := fn(c)
|
||||
checks = append(checks, res)
|
||||
}
|
||||
return MakeAndResult2(checks...)
|
||||
}
|
||||
}
|
||||
|
||||
// UPGRADEv2: will be removed.
|
||||
// MultiCheckOr returns the best check result out of several ones performed.
|
||||
func MultiCheckOr(fns ...CheckFn) CheckFn {
|
||||
@ -164,16 +153,3 @@ func MultiCheckOr(fns ...CheckFn) CheckFn {
|
||||
return maxResult
|
||||
}
|
||||
}
|
||||
|
||||
// MultiCheckAnd means all checks must succeed. This returns a conservative result
|
||||
// where the worst result is returned.
|
||||
func MultiCheckAnd(fns ...CheckFn) CheckFn {
|
||||
return func(c *CheckRequest) CheckResult {
|
||||
var checks []CheckResult
|
||||
for _, fn := range fns {
|
||||
res := fn(c)
|
||||
checks = append(checks, res)
|
||||
}
|
||||
return MakeAndResult(checks...)
|
||||
}
|
||||
}
|
||||
|
@ -1,151 +0,0 @@
|
||||
// Copyright 2021 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 checker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const checkTest = "Check-Test"
|
||||
|
||||
var errorTest = errors.New("test error")
|
||||
|
||||
func TestMakeCheckAnd(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
want CheckResult
|
||||
checks []CheckResult
|
||||
}{
|
||||
{
|
||||
name: "Multiple passing",
|
||||
checks: []CheckResult{
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: true,
|
||||
Details: nil,
|
||||
Confidence: 5,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: true,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
},
|
||||
want: CheckResult{
|
||||
Name: checkTest,
|
||||
Pass: true,
|
||||
Details: nil,
|
||||
Confidence: 5,
|
||||
ShouldRetry: false,
|
||||
Error: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple failing",
|
||||
checks: []CheckResult{
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 5,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
},
|
||||
want: CheckResult{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Passing and failing",
|
||||
checks: []CheckResult{
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: true,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: nil,
|
||||
},
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 5,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
},
|
||||
want: CheckResult{
|
||||
Name: checkTest,
|
||||
Pass: false,
|
||||
Details: nil,
|
||||
Confidence: 10,
|
||||
ShouldRetry: false,
|
||||
Error: errorTest,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := MakeAndResult(tt.checks...)
|
||||
if result.Pass != tt.want.Pass || result.Confidence != tt.want.Confidence {
|
||||
t.Errorf("MakeAndResult failed (%s): got %v, expected %v", tt.name, result, tt.want)
|
||||
}
|
||||
|
||||
// Also test CheckFn variant
|
||||
var fns []CheckFn
|
||||
for _, c := range tt.checks {
|
||||
check := c
|
||||
fns = append(fns, func(*CheckRequest) CheckResult { return check })
|
||||
}
|
||||
c := CheckRequest{}
|
||||
resultfn := MultiCheckAnd(fns...)(&c)
|
||||
if resultfn.Pass != tt.want.Pass || resultfn.Confidence != tt.want.Confidence {
|
||||
t.Errorf("MultiCheckAnd failed (%s): got %v, expected %v", tt.name, resultfn, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -75,10 +75,13 @@ func CIIBestPractices(c *checker.CheckRequest) checker.CheckResult {
|
||||
// https://bestpractices.coreinfrastructure.org/en/criteria.
|
||||
const silverScore = 7
|
||||
const passingScore = 5
|
||||
const inProgressScore = 2
|
||||
switch {
|
||||
default:
|
||||
e := sce.Create(sce.ErrScorecardInternal, "unsupported badge")
|
||||
e := sce.Create(sce.ErrScorecardInternal, fmt.Sprintf("unsupported badge: %v", result.BadgeLevel))
|
||||
return checker.CreateRuntimeErrorResult(CheckCIIBestPractices, e)
|
||||
case strings.Contains(result.BadgeLevel, "in_progress"):
|
||||
return checker.CreateResultWithScore(CheckCIIBestPractices, "badge detected: in_progress", inProgressScore)
|
||||
case strings.Contains(result.BadgeLevel, "silver"):
|
||||
return checker.CreateResultWithScore(CheckCIIBestPractices, "badge detected: silver", silverScore)
|
||||
case strings.Contains(result.BadgeLevel, "gold"):
|
||||
|
@ -59,40 +59,94 @@ func init() {
|
||||
|
||||
// FrozenDeps will check the repository if it contains frozen dependecies.
|
||||
func FrozenDeps(c *checker.CheckRequest) checker.CheckResult {
|
||||
return checker.MultiCheckAnd2(
|
||||
isPackageManagerLockFilePresent,
|
||||
isGitHubActionsWorkflowPinned,
|
||||
isDockerfilePinned,
|
||||
isDockerfileFreeOfInsecureDownloads,
|
||||
isShellScriptFreeOfInsecureDownloads,
|
||||
isGitHubWorkflowScriptFreeOfInsecureDownloads,
|
||||
)(c)
|
||||
// Lock file.
|
||||
lockScore, lockErr := isPackageManagerLockFilePresent(c)
|
||||
if lockErr != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, lockErr)
|
||||
}
|
||||
|
||||
// GitHub actions.
|
||||
actionScore, actionErr := isGitHubActionsWorkflowPinned(c)
|
||||
if actionErr != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, actionErr)
|
||||
}
|
||||
|
||||
// Docker files.
|
||||
dockerFromScore, dockerFromErr := isDockerfilePinned(c)
|
||||
if dockerFromErr != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, dockerFromErr)
|
||||
}
|
||||
|
||||
// Docker downloads.
|
||||
dockerDownloadScore, dockerDownloadErr := isDockerfileFreeOfInsecureDownloads(c)
|
||||
if dockerDownloadErr != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, dockerDownloadErr)
|
||||
}
|
||||
|
||||
// Script downloads.
|
||||
scriptScore, scriptError := isShellScriptFreeOfInsecureDownloads(c)
|
||||
if scriptError != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, scriptError)
|
||||
}
|
||||
|
||||
// Action script downloads.
|
||||
actionScriptScore, actionScriptError := isGitHubWorkflowScriptFreeOfInsecureDownloads(c)
|
||||
if actionScriptError != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, actionScriptError)
|
||||
}
|
||||
|
||||
// Scores may be inconclusive.
|
||||
lockScore = maxScore(0, lockScore)
|
||||
actionScore = maxScore(0, actionScore)
|
||||
dockerFromScore = maxScore(0, dockerFromScore)
|
||||
dockerDownloadScore = maxScore(0, dockerDownloadScore)
|
||||
scriptScore = maxScore(0, scriptScore)
|
||||
actionScriptScore = maxScore(0, actionScriptScore)
|
||||
score := checker.AggregateScores(lockScore, actionScore, dockerFromScore,
|
||||
dockerDownloadScore, scriptScore, actionScriptScore)
|
||||
if score == checker.MaxResultScore {
|
||||
checker.CreateMaxScoreResult(CheckFrozenDeps, "all dependencies are pinned")
|
||||
}
|
||||
return checker.CreateProportionalScoreResult(CheckFrozenDeps,
|
||||
"unpinned dependencies detected", score, checker.MaxResultScore)
|
||||
}
|
||||
|
||||
// TODO(laurent): need to support GCB pinning.
|
||||
|
||||
func isShellScriptFreeOfInsecureDownloads(c *checker.CheckRequest) checker.CheckResult {
|
||||
r, err := CheckFilesContent("*", false, c, validateShellScriptIsFreeOfInsecureDownloads)
|
||||
return createResultForIsShellScriptFreeOfInsecureDownloads(r, err)
|
||||
//nolint
|
||||
func maxScore(s1, s2 int) int {
|
||||
if s1 > s2 {
|
||||
return s1
|
||||
}
|
||||
return s2
|
||||
}
|
||||
|
||||
func createResultForIsShellScriptFreeOfInsecureDownloads(r bool, err error) checker.CheckResult {
|
||||
func createReturnValues(r bool, infoMsg string, dl checker.DetailLogger, err error) (int, error) {
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
return checker.InconclusiveResultScore, err
|
||||
}
|
||||
if !r {
|
||||
return checker.CreateMinScoreResult(CheckFrozenDeps,
|
||||
"insecure (unpinned) dependency downloads found in shell scripts")
|
||||
return checker.MinResultScore, nil
|
||||
}
|
||||
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps,
|
||||
"no insecure (unpinned) dependency downloads found in shell scripts")
|
||||
dl.Info(infoMsg)
|
||||
return checker.MaxResultScore, nil
|
||||
}
|
||||
|
||||
func isShellScriptFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckFilesContent("*", false, c, validateShellScriptIsFreeOfInsecureDownloads)
|
||||
return createReturnForIsShellScriptFreeOfInsecureDownloads(r, c.Dlogger, err)
|
||||
}
|
||||
|
||||
func createReturnForIsShellScriptFreeOfInsecureDownloads(r bool, dl checker.DetailLogger, err error) (int, error) {
|
||||
return createReturnValues(r,
|
||||
"no insecure (unpinned) dependency downloads found in shell scripts",
|
||||
dl, err)
|
||||
}
|
||||
|
||||
func testValidateShellScriptIsFreeOfInsecureDownloads(pathfn string,
|
||||
content []byte, dl checker.DetailLogger) checker.CheckResult {
|
||||
content []byte, dl checker.DetailLogger) (int, error) {
|
||||
r, err := validateShellScriptIsFreeOfInsecureDownloads(pathfn, content, dl)
|
||||
return createResultForIsShellScriptFreeOfInsecureDownloads(r, err)
|
||||
return createReturnForIsShellScriptFreeOfInsecureDownloads(r, dl, err)
|
||||
}
|
||||
|
||||
func validateShellScriptIsFreeOfInsecureDownloads(pathfn string, content []byte,
|
||||
@ -104,33 +158,30 @@ func validateShellScriptIsFreeOfInsecureDownloads(pathfn string, content []byte,
|
||||
return validateShellFile(pathfn, content, dl)
|
||||
}
|
||||
|
||||
func isDockerfileFreeOfInsecureDownloads(c *checker.CheckRequest) checker.CheckResult {
|
||||
func isDockerfileFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckFilesContent("*Dockerfile*", false, c, validateDockerfileIsFreeOfInsecureDownloads)
|
||||
return createResultForIsDockerfileFreeOfInsecureDownloads(r, err)
|
||||
return createReturnForIsDockerfileFreeOfInsecureDownloads(r, c.Dlogger, err)
|
||||
}
|
||||
|
||||
// Create the result.
|
||||
func createResultForIsDockerfileFreeOfInsecureDownloads(r bool, err error) checker.CheckResult {
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
}
|
||||
if !r {
|
||||
return checker.CreateMinScoreResult(CheckFrozenDeps,
|
||||
"insecure (unpinned) dependency downloads found in Dockerfiles")
|
||||
}
|
||||
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps,
|
||||
"no insecure (unpinned) dependency downloads found in Dockerfiles")
|
||||
func createReturnForIsDockerfileFreeOfInsecureDownloads(r bool, dl checker.DetailLogger, err error) (int, error) {
|
||||
return createReturnValues(r,
|
||||
"no insecure (unpinned) dependency downloads found in Dockerfiles",
|
||||
dl, err)
|
||||
}
|
||||
|
||||
func testValidateDockerfileIsFreeOfInsecureDownloads(pathfn string,
|
||||
content []byte, dl checker.DetailLogger) checker.CheckResult {
|
||||
content []byte, dl checker.DetailLogger) (int, error) {
|
||||
r, err := validateDockerfileIsFreeOfInsecureDownloads(pathfn, content, dl)
|
||||
return createResultForIsDockerfileFreeOfInsecureDownloads(r, err)
|
||||
return createReturnForIsDockerfileFreeOfInsecureDownloads(r, dl, err)
|
||||
}
|
||||
|
||||
func validateDockerfileIsFreeOfInsecureDownloads(pathfn string, content []byte,
|
||||
dl checker.DetailLogger) (bool, error) {
|
||||
// Return early if this is a script, e.g. script_dockerfile_something.sh
|
||||
if isShellScriptFile(pathfn, content) {
|
||||
return true, nil
|
||||
}
|
||||
contentReader := strings.NewReader(string(content))
|
||||
res, err := parser.Parse(contentReader)
|
||||
if err != nil {
|
||||
@ -167,26 +218,21 @@ func validateDockerfileIsFreeOfInsecureDownloads(pathfn string, content []byte,
|
||||
return validateShellFile(pathfn, bytes, dl)
|
||||
}
|
||||
|
||||
func isDockerfilePinned(c *checker.CheckRequest) checker.CheckResult {
|
||||
func isDockerfilePinned(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckFilesContent("*Dockerfile*", false, c, validateDockerfileIsPinned)
|
||||
return createResultForIsDockerfilePinned(r, err)
|
||||
return createReturnForIsDockerfilePinned(r, c.Dlogger, err)
|
||||
}
|
||||
|
||||
// Create the result.
|
||||
func createResultForIsDockerfilePinned(r bool, err error) checker.CheckResult {
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
}
|
||||
if r {
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps, "Dockerfile dependencies are pinned")
|
||||
}
|
||||
|
||||
return checker.CreateMinScoreResult(CheckFrozenDeps, "unpinned dependencies found Dockerfiles")
|
||||
func createReturnForIsDockerfilePinned(r bool, dl checker.DetailLogger, err error) (int, error) {
|
||||
return createReturnValues(r,
|
||||
"Dockerfile dependencies are pinned",
|
||||
dl, err)
|
||||
}
|
||||
|
||||
func testValidateDockerfileIsPinned(pathfn string, content []byte, dl checker.DetailLogger) checker.CheckResult {
|
||||
func testValidateDockerfileIsPinned(pathfn string, content []byte, dl checker.DetailLogger) (int, error) {
|
||||
r, err := validateDockerfileIsPinned(pathfn, content, dl)
|
||||
return createResultForIsDockerfilePinned(r, err)
|
||||
return createReturnForIsDockerfilePinned(r, dl, err)
|
||||
}
|
||||
|
||||
func validateDockerfileIsPinned(pathfn string, content []byte,
|
||||
@ -195,13 +241,17 @@ func validateDockerfileIsPinned(pathfn string, content []byte,
|
||||
// Dockerfile.aarch64, Dockerfile.template, Dockerfile_template, dockerfile, Dockerfile-name.template
|
||||
// Templates may trigger false positives, e.g. FROM { NAME }.
|
||||
|
||||
// Return early if this is a script, e.g. script_dockerfile_something.sh
|
||||
if isShellScriptFile(pathfn, content) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We have what looks like a docker file.
|
||||
// Let's interpret the content as utf8-encoded strings.
|
||||
contentReader := strings.NewReader(string(content))
|
||||
regex := regexp.MustCompile(`.*@sha256:[a-f\d]{64}`)
|
||||
|
||||
ret := true
|
||||
fromFound := false
|
||||
pinnedAsNames := make(map[string]bool)
|
||||
res, err := parser.Parse(contentReader)
|
||||
if err != nil {
|
||||
@ -215,9 +265,6 @@ func validateDockerfileIsPinned(pathfn string, content []byte,
|
||||
continue
|
||||
}
|
||||
|
||||
// New 'FROM' line found.
|
||||
fromFound = true
|
||||
|
||||
var valueList []string
|
||||
for n := child.Next; n != nil; n = n.Next {
|
||||
valueList = append(valueList, n.Value)
|
||||
@ -261,38 +308,30 @@ func validateDockerfileIsPinned(pathfn string, content []byte,
|
||||
}
|
||||
}
|
||||
|
||||
// The file should have at least one FROM statement.
|
||||
if !fromFound {
|
||||
//nolint
|
||||
return false, sce.Create(sce.ErrScorecardInternal, errInternalInvalidDockerFile.Error())
|
||||
}
|
||||
//nolint
|
||||
// The file need not have a FROM statement,
|
||||
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/dockerfiles/partials/jupyter.partial.Dockerfile.
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func isGitHubWorkflowScriptFreeOfInsecureDownloads(c *checker.CheckRequest) checker.CheckResult {
|
||||
func isGitHubWorkflowScriptFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckFilesContent(".github/workflows/*", false, c, validateGitHubWorkflowIsFreeOfInsecureDownloads)
|
||||
return createResultForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r, err)
|
||||
return createReturnForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r, c.Dlogger, err)
|
||||
}
|
||||
|
||||
// Create the result.
|
||||
func createResultForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r bool, err error) checker.CheckResult {
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
}
|
||||
if !r {
|
||||
return checker.CreateMinScoreResult(CheckFrozenDeps,
|
||||
"insecure (unpinned) dependency downloads found in GitHub workflows")
|
||||
}
|
||||
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps,
|
||||
"no insecure (unpinned) dependency downloads found in GitHub workflows")
|
||||
func createReturnForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r bool,
|
||||
dl checker.DetailLogger, err error) (int, error) {
|
||||
return createReturnValues(r,
|
||||
"no insecure (unpinned) dependency downloads found in GitHub workflows",
|
||||
dl, err)
|
||||
}
|
||||
|
||||
func testValidateGitHubWorkflowScriptFreeOfInsecureDownloads(pathfn string,
|
||||
content []byte, dl checker.DetailLogger) checker.CheckResult {
|
||||
content []byte, dl checker.DetailLogger) (int, error) {
|
||||
r, err := validateGitHubWorkflowIsFreeOfInsecureDownloads(pathfn, content, dl)
|
||||
return createResultForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r, err)
|
||||
return createReturnForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r, dl, err)
|
||||
}
|
||||
|
||||
func validateGitHubWorkflowIsFreeOfInsecureDownloads(pathfn string, content []byte,
|
||||
@ -354,26 +393,21 @@ func validateGitHubWorkflowIsFreeOfInsecureDownloads(pathfn string, content []by
|
||||
}
|
||||
|
||||
// Check pinning of github actions in workflows.
|
||||
func isGitHubActionsWorkflowPinned(c *checker.CheckRequest) checker.CheckResult {
|
||||
func isGitHubActionsWorkflowPinned(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckFilesContent(".github/workflows/*", true, c, validateGitHubActionWorkflow)
|
||||
return createResultForIsGitHubActionsWorkflowPinned(r, err)
|
||||
return createReturnForIsGitHubActionsWorkflowPinned(r, c.Dlogger, err)
|
||||
}
|
||||
|
||||
// Create the result.
|
||||
func createResultForIsGitHubActionsWorkflowPinned(r bool, err error) checker.CheckResult {
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
}
|
||||
if r {
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps, "GitHub actions are pinned")
|
||||
}
|
||||
|
||||
return checker.CreateMinScoreResult(CheckFrozenDeps, "GitHub actions are not pinned")
|
||||
func createReturnForIsGitHubActionsWorkflowPinned(r bool, dl checker.DetailLogger, err error) (int, error) {
|
||||
return createReturnValues(r,
|
||||
"GitHub actions are pinned",
|
||||
dl, err)
|
||||
}
|
||||
|
||||
func testIsGitHubActionsWorkflowPinned(pathfn string, content []byte, dl checker.DetailLogger) checker.CheckResult {
|
||||
func testIsGitHubActionsWorkflowPinned(pathfn string, content []byte, dl checker.DetailLogger) (int, error) {
|
||||
r, err := validateGitHubActionWorkflow(pathfn, content, dl)
|
||||
return createResultForIsGitHubActionsWorkflowPinned(r, err)
|
||||
return createReturnForIsGitHubActionsWorkflowPinned(r, dl, err)
|
||||
}
|
||||
|
||||
// Check file content.
|
||||
@ -414,16 +448,17 @@ func validateGitHubActionWorkflow(pathfn string, content []byte, dl checker.Deta
|
||||
}
|
||||
|
||||
// Check presence of lock files thru validatePackageManagerFile().
|
||||
func isPackageManagerLockFilePresent(c *checker.CheckRequest) checker.CheckResult {
|
||||
func isPackageManagerLockFilePresent(c *checker.CheckRequest) (int, error) {
|
||||
r, err := CheckIfFileExists(CheckFrozenDeps, c, validatePackageManagerFile)
|
||||
if err != nil {
|
||||
return checker.CreateRuntimeErrorResult(CheckFrozenDeps, err)
|
||||
return checker.InconclusiveResultScore, err
|
||||
}
|
||||
if !r {
|
||||
return checker.CreateInconclusiveResult(CheckFrozenDeps, "no lock files detected for a package manager")
|
||||
c.Dlogger.Warn("no lock files detected for a package manager")
|
||||
return checker.InconclusiveResultScore, nil
|
||||
}
|
||||
|
||||
return checker.CreateMaxScoreResult(CheckFrozenDeps, "lock file detected for a package manager")
|
||||
return checker.MaxResultScore, nil
|
||||
}
|
||||
|
||||
// validatePackageManagerFile will validate the if frozen dependecies file name exists.
|
||||
|
@ -50,7 +50,7 @@ func TestGithubWorkflowPinning(t *testing.T) {
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
@ -81,8 +81,8 @@ func TestGithubWorkflowPinning(t *testing.T) {
|
||||
}
|
||||
}
|
||||
dl := scut.TestDetailLogger{}
|
||||
r := testIsGitHubActionsWorkflowPinned(tt.filename, content, &dl)
|
||||
scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl)
|
||||
s, e := testIsGitHubActionsWorkflowPinned(tt.filename, content, &dl)
|
||||
scut.ValidateTestValues(t, tt.name, &tt.expected, s, e, &dl)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -98,10 +98,10 @@ func TestDockerfilePinning(t *testing.T) {
|
||||
name: "Invalid dockerfile",
|
||||
filename: "./testdata/Dockerfile-invalid",
|
||||
expected: scut.TestReturn{
|
||||
Errors: []error{sce.ErrScorecardInternal},
|
||||
Score: checker.InconclusiveResultScore,
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
@ -112,7 +112,7 @@ func TestDockerfilePinning(t *testing.T) {
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
@ -123,7 +123,7 @@ func TestDockerfilePinning(t *testing.T) {
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
@ -165,8 +165,8 @@ func TestDockerfilePinning(t *testing.T) {
|
||||
}
|
||||
}
|
||||
dl := scut.TestDetailLogger{}
|
||||
r := testValidateDockerfileIsPinned(tt.filename, content, &dl)
|
||||
scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl)
|
||||
s, e := testValidateDockerfileIsPinned(tt.filename, content, &dl)
|
||||
scut.ValidateTestValues(t, tt.name, &tt.expected, s, e, &dl)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -207,7 +207,7 @@ func TestDockerfileScriptDownload(t *testing.T) {
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
@ -277,6 +277,17 @@ func TestDockerfileScriptDownload(t *testing.T) {
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "download with some python",
|
||||
filename: "testdata/Dockerfile-some-python",
|
||||
expected: scut.TestReturn{
|
||||
Errors: nil,
|
||||
Score: checker.MinResultScore,
|
||||
NumberOfWarn: 1,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfDebug: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt // Re-initializing variable so it is not changed while executing the closure below
|
||||
@ -293,8 +304,8 @@ func TestDockerfileScriptDownload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
dl := scut.TestDetailLogger{}
|
||||
r := testValidateDockerfileIsFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl)
|
||||
s, e := testValidateDockerfileIsFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestValues(t, tt.name, &tt.expected, s, e, &dl)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -366,8 +377,8 @@ func TestShellScriptDownload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
dl := scut.TestDetailLogger{}
|
||||
r := testValidateShellScriptIsFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl)
|
||||
s, e := testValidateShellScriptIsFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestValues(t, tt.name, &tt.expected, s, e, &dl)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -428,8 +439,8 @@ func TestGitHubWorflowRunDownload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
dl := scut.TestDetailLogger{}
|
||||
r := testValidateGitHubWorkflowScriptFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl)
|
||||
s, e := testValidateGitHubWorkflowScriptFreeOfInsecureDownloads(tt.filename, content, &dl)
|
||||
scut.ValidateTestValues(t, tt.name, &tt.expected, s, e, &dl)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package checks
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
@ -30,14 +31,16 @@ import (
|
||||
sce "github.com/ossf/scorecard/v2/errors"
|
||||
)
|
||||
|
||||
// List of interpreters.
|
||||
var pythonInterpreters = []string{"python", "python3", "python2.7"}
|
||||
|
||||
var interpreters = append([]string{
|
||||
"sh", "bash", "dash", "ksh", "mksh", "python",
|
||||
"perl", "ruby", "php", "node", "nodejs", "java",
|
||||
"exec", "su",
|
||||
}, pythonInterpreters...)
|
||||
var (
|
||||
shellNames = []string{
|
||||
"sh", "bash", "dash", "ksh", "mksh",
|
||||
}
|
||||
pythonInterpreters = []string{"python", "python3", "python2.7"}
|
||||
shellInterpreters = append([]string{"exec", "su"}, shellNames...)
|
||||
otherInterpreters = []string{"perl", "ruby", "php", "node", "nodejs", "java"}
|
||||
interpreters = append(otherInterpreters,
|
||||
append(shellInterpreters, append(shellNames, pythonInterpreters...)...)...)
|
||||
)
|
||||
|
||||
// Note: aws is handled separately because it uses different
|
||||
// cli options.
|
||||
@ -45,10 +48,6 @@ var downloadUtils = []string{
|
||||
"curl", "wget", "gsutil",
|
||||
}
|
||||
|
||||
var shellNames = []string{
|
||||
"sh", "bash", "dash", "ksh", "mksh",
|
||||
}
|
||||
|
||||
func isBinaryName(expected, name string) bool {
|
||||
return strings.EqualFold(path.Base(name), expected)
|
||||
}
|
||||
@ -200,17 +199,34 @@ func isInterpreter(cmd []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isInterpreterWithCommand(cmd []string) bool {
|
||||
func isShellInterpreterOrCommand(cmd []string) bool {
|
||||
if len(cmd) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, b := range interpreters {
|
||||
if isCommand(cmd, b) {
|
||||
return true
|
||||
if isPythonCommand(cmd) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, b := range otherInterpreters {
|
||||
if isBinaryName(b, cmd[0]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func extractInterpreterAndCommand(cmd []string) (string, bool) {
|
||||
if len(cmd) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
for _, b := range interpreters {
|
||||
if isCommand(cmd, b) {
|
||||
return b, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func isInterpreterWithFile(cmd []string, fn string) bool {
|
||||
@ -620,27 +636,28 @@ func extractInterpreterCommandFromArgs(args []*syntax.Word) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
func extractInterpreterCommandFromNode(node syntax.Node) (string, bool) {
|
||||
func extractInterpreterAndCommandFromNode(node syntax.Node) (interpreter, command string, yes bool) {
|
||||
ce, ok := node.(*syntax.CallExpr)
|
||||
if !ok {
|
||||
return "", false
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
c, ok := extractCommand(ce)
|
||||
if !ok {
|
||||
return "", false
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
if !isInterpreterWithCommand(c) {
|
||||
return "", false
|
||||
i, ok := extractInterpreterAndCommand(c)
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
cs, ok := extractInterpreterCommandFromArgs(ce.Args)
|
||||
if !ok {
|
||||
return "", false
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
return cs, true
|
||||
return i, cs, true
|
||||
}
|
||||
|
||||
func nodeToString(p *syntax.Printer, node syntax.Node) (string, error) {
|
||||
@ -660,9 +677,11 @@ func validateShellFileAndRecord(pathfn string, content []byte, files map[string]
|
||||
in := strings.NewReader(string(content))
|
||||
f, err := syntax.NewParser().Parse(in, "")
|
||||
if err != nil {
|
||||
// Note: this is caught by internal caller and only printed
|
||||
// to avoid failing on shell scripts that our parser does not understand.
|
||||
// Example: https://github.com/openssl/openssl/blob/master/util/shlib_wrap.sh.in
|
||||
//nolint
|
||||
return false, sce.Create(sce.ErrScorecardInternal,
|
||||
fmt.Sprintf("%v: %v", errInternalInvalidShellCode, err))
|
||||
return false, sce.CreateInternal(errInternalInvalidShellCode, err.Error())
|
||||
}
|
||||
|
||||
printer := syntax.NewPrinter()
|
||||
@ -675,10 +694,13 @@ func validateShellFileAndRecord(pathfn string, content []byte, files map[string]
|
||||
return false
|
||||
}
|
||||
|
||||
// sh -c "CMD".
|
||||
c, ok := extractInterpreterCommandFromNode(node)
|
||||
// interpreter -c "CMD".
|
||||
i, c, ok := extractInterpreterAndCommandFromNode(node)
|
||||
// TODO: support other interpreters.
|
||||
// Example: https://github.com/apache/airflow/blob/main/scripts/ci/kubernetes/ci_run_kubernetes_tests.sh#L75
|
||||
// HOST_PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info[0]}.{sys.version_info[1]}")')``
|
||||
// nolinter
|
||||
if ok {
|
||||
if ok && isShellInterpreterOrCommand([]string{i}) {
|
||||
ok, e := validateShellFileAndRecord(pathfn, []byte(c), files, dl)
|
||||
validated = ok
|
||||
if e != nil {
|
||||
@ -752,28 +774,33 @@ func isShellScriptFile(pathfn string, content []byte) bool {
|
||||
// Look at file content.
|
||||
r := strings.NewReader(string(content))
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// TODO: support perl scripts with embedded shell scripts:
|
||||
// https://github.com/openssl/openssl/blob/master/test/recipes/15-test_dsaparam.t.
|
||||
|
||||
// #!/bin/XXX, #!XXX, #!/usr/bin/env XXX, #!env XXX
|
||||
if !strings.HasPrefix(line, "#!") {
|
||||
continue
|
||||
// Only look at first line.
|
||||
if !scanner.Scan() {
|
||||
return false
|
||||
}
|
||||
line := scanner.Text()
|
||||
|
||||
// #!/bin/XXX, #!XXX, #!/usr/bin/env XXX, #!env XXX
|
||||
if !strings.HasPrefix(line, "#!") {
|
||||
return false
|
||||
}
|
||||
|
||||
line = line[2:]
|
||||
for _, name := range shellNames {
|
||||
parts := strings.Split(line, " ")
|
||||
// #!/bin/bash, #!bash -e
|
||||
if len(parts) >= 1 && isBinaryName(name, parts[0]) {
|
||||
return true
|
||||
}
|
||||
|
||||
line = line[2:]
|
||||
for _, name := range shellNames {
|
||||
parts := strings.Split(line, " ")
|
||||
// #!/bin/bash, #!bash -e
|
||||
if len(parts) >= 1 && isBinaryName(name, parts[0]) {
|
||||
return true
|
||||
}
|
||||
|
||||
// #!/bin/env bash
|
||||
if len(parts) >= 2 &&
|
||||
isBinaryName("env", parts[0]) &&
|
||||
isBinaryName(name, parts[1]) {
|
||||
return true
|
||||
}
|
||||
// #!/bin/env bash
|
||||
if len(parts) >= 2 &&
|
||||
isBinaryName("env", parts[0]) &&
|
||||
isBinaryName(name, parts[1]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,5 +809,14 @@ func isShellScriptFile(pathfn string, content []byte) bool {
|
||||
|
||||
func validateShellFile(pathfn string, content []byte, dl checker.DetailLogger) (bool, error) {
|
||||
files := make(map[string]bool)
|
||||
return validateShellFileAndRecord(pathfn, content, files, dl)
|
||||
r, err := validateShellFileAndRecord(pathfn, content, files, dl)
|
||||
if err != nil {
|
||||
if errors.Is(err, errInternalInvalidShellCode) {
|
||||
// Discard and print this particular error for now.
|
||||
dl.Debug(err.Error())
|
||||
} else {
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
17
checks/testdata/Dockerfile-proc-subs
vendored
17
checks/testdata/Dockerfile-proc-subs
vendored
@ -16,12 +16,17 @@
|
||||
FROM python:3.7@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
|
||||
|
||||
RUN bash <(wget -qO- http://website.com/my-script.sh)
|
||||
RUN bash <(curl -s https://codecov.io/bash)
|
||||
RUN ["bash", "<(curl -s https://codecov.io/bash)"]
|
||||
RUN bash <(curl -s https://codecov.io/bash1)
|
||||
RUN ["bash", "<(curl -s https://codecov.io/bash2)"]
|
||||
|
||||
RUN sudo su -c "bash <(wget -qO- http://website.com/my-script.sh)" root
|
||||
RUN sudo su -c "bash <(curl -s https://codecov.io/bash)" root
|
||||
RUN ["su", "-c", "\"bash <(curl -s https://codecov.io/bash)\""]
|
||||
RUN sudo su -c "bash <(wget -qO- http://website.com/my-script2.sh)" root
|
||||
RUN sudo su -c "bash <(curl -s https://codecov.io/bash3)" root
|
||||
RUN ["su", "-c", "\"bash <(curl -s https://codecov.io/bash4)\""]
|
||||
|
||||
FROM scratch
|
||||
FROM python@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
|
||||
FROM python@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
|
||||
|
||||
[{1 insecure (unpinned) download detected in testdata/Dockerfile-proc-subs: 'bash <(wget -qO- http://website.com/my-script.sh)'}
|
||||
{1 insecure (unpinned) download detected in testdata/Dockerfile-proc-subs: 'bash <(curl -s https://codecov.io/bash)'}
|
||||
{1 insecure (unpinned) download detected in testdata/Dockerfile-proc-subs: 'bash <(curl -s https://codecov.io/bash)'}]
|
||||
FAIL
|
31
checks/testdata/Dockerfile-some-python
vendored
Normal file
31
checks/testdata/Dockerfile-some-python
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2021 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.
|
||||
|
||||
# Taken from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/dockerfiles/dockerfiles/ppc64le/cpu-ppc64le-jupyter.Dockerfile
|
||||
|
||||
ARG CACHE_STOP=1
|
||||
RUN if [ ${TF_PACKAGE} = tensorflow-gpu ]; then \
|
||||
BASE=https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Release_Build/lastSuccessfulBuild/; \
|
||||
elif [ ${TF_PACKAGE} = tf-nightly-gpu ]; then \
|
||||
BASE=https://powerci.osuosl.org/job/TensorFlow_PPC64LE_GPU_Nightly_Artifact/lastSuccessfulBuild/; \
|
||||
elif [ ${TF_PACKAGE} = tensorflow ]; then \
|
||||
BASE=https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Release_Build/lastSuccessfulBuild/; \
|
||||
elif [ ${TF_PACKAGE} = tf-nightly ]; then \
|
||||
BASE=https://powerci.osuosl.org/job/TensorFlow_PPC64LE_CPU_Nightly_Artifact/lastSuccessfulBuild/; \
|
||||
fi; \
|
||||
MAJOR=`python3 -c 'import sys; print(sys.version_info[0])'`; \
|
||||
MINOR=`python3 -c 'import sys; print(sys.version_info[1])'`; \
|
||||
PACKAGE=$(wget -qO- ${BASE}"api/xml?xpath=//fileName&wrapper=artifacts" | grep -o "[^<>]*cp${MAJOR}${MINOR}[^<>]*.whl"); \
|
||||
wget ${BASE}"artifact/tensorflow_pkg/"${PACKAGE}; \
|
||||
python3 -m pip install --no-cache-dir ${PACKAGE}
|
@ -48,10 +48,10 @@ var _ = Describe("E2E TEST:FrozenDeps", func() {
|
||||
}
|
||||
expected := scut.TestReturn{
|
||||
Errors: nil,
|
||||
Score: checker.InconclusiveResultScore,
|
||||
NumberOfWarn: 222,
|
||||
Score: checker.MinResultScore,
|
||||
NumberOfWarn: 374,
|
||||
NumberOfInfo: 0,
|
||||
NumberOfDebug: 0,
|
||||
NumberOfDebug: 4,
|
||||
}
|
||||
result := checks.FrozenDeps(&req)
|
||||
// UPGRADEv2: to remove.
|
||||
@ -81,7 +81,7 @@ var _ = Describe("E2E TEST:FrozenDeps", func() {
|
||||
Errors: nil,
|
||||
Score: checker.MaxResultScore,
|
||||
NumberOfWarn: 0,
|
||||
NumberOfInfo: 1,
|
||||
NumberOfInfo: 6,
|
||||
NumberOfDebug: 0,
|
||||
}
|
||||
result := checks.FrozenDeps(&req)
|
||||
|
@ -36,3 +36,9 @@ func Create(e error, msg string) error {
|
||||
// We still need to use %w to prevent callers from using e == ErrInvalidDockerFile.
|
||||
return fmt.Errorf("%w", e)
|
||||
}
|
||||
|
||||
// Create an internal error, not using
|
||||
// any of the errors listed above.
|
||||
func CreateInternal(e error, msg string) error {
|
||||
return Create(e, msg)
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ func validateDetailTypes(messages []checker.CheckDetail, nw, ni, nd int) bool {
|
||||
enw++
|
||||
}
|
||||
}
|
||||
|
||||
return enw == nw &&
|
||||
eni == ni &&
|
||||
end == nd
|
||||
@ -71,26 +72,35 @@ func (l *TestDetailLogger) Debug(desc string, args ...interface{}) {
|
||||
}
|
||||
|
||||
//nolint
|
||||
func ValidateTestReturn(t *testing.T, name string, te *TestReturn,
|
||||
tr *checker.CheckResult, dl *TestDetailLogger) bool {
|
||||
func ValidateTestValues(t *testing.T, name string, te *TestReturn,
|
||||
score int, err error, dl *TestDetailLogger) bool {
|
||||
for _, we := range te.Errors {
|
||||
if !errors.Is(tr.Error2, we) {
|
||||
if !errors.Is(err, we) {
|
||||
if t != nil {
|
||||
t.Errorf("%v: invalid error returned: %v is not of type %v",
|
||||
name, tr.Error, we)
|
||||
name, err, we)
|
||||
}
|
||||
fmt.Printf("%v: invalid error returned: %v is not of type %v",
|
||||
name, err, we)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// UPGRADEv2: update name.
|
||||
if tr.Score != te.Score ||
|
||||
if score != te.Score ||
|
||||
!validateDetailTypes(dl.messages, te.NumberOfWarn,
|
||||
te.NumberOfInfo, te.NumberOfDebug) {
|
||||
if t != nil {
|
||||
t.Errorf("%v: Got (score=%v) expected (%v)\n%v",
|
||||
name, tr.Score, te.Score, dl.messages)
|
||||
name, score, te.Score, dl.messages)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//nolint
|
||||
func ValidateTestReturn(t *testing.T, name string, te *TestReturn,
|
||||
tr *checker.CheckResult, dl *TestDetailLogger) bool {
|
||||
return ValidateTestValues(t, name, te, tr.Score, tr.Error2, dl)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user