Raw results for Pinned-Dependencies (#1932)

* backup

* update

* update

* draft

* updates

* updates

* updates

* updates

* fix

* linter

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* linter

* comments

* linter

* linter

* tests

* updates

* updates

* tests
This commit is contained in:
laurentsimon 2022-06-06 14:31:22 -07:00 committed by GitHub
parent 23523f6d09
commit 4bd3391a36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 2679 additions and 2583 deletions

View File

@ -879,5 +879,5 @@ jobs:
run: |
go env -w GOFLAGS=-mod=mod
go install github.com/google/addlicense@2fe3ee94479d08be985a84861de4e6b06a1c7208
addlicense -ignore "**/script-empty.sh" -ignore "pkg/testdata/**" -ignore "checks/testdata/**" -l apache -c 'Security Scorecard Authors' -v *
addlicense -ignore "**/script-empty.sh" -ignore "testdata/**" -ignore "**/testdata/**" -l apache -c 'Security Scorecard Authors' -v *
git diff --exit-code

View File

@ -31,6 +31,7 @@ type RawResults struct {
DependencyUpdateToolResults DependencyUpdateToolData
BranchProtectionResults BranchProtectionsData
CodeReviewResults CodeReviewData
PinningDependenciesResults PinningDependenciesData
WebhookResults WebhooksData
ContributorsResults ContributorsData
MaintainedResults MaintainedData
@ -64,6 +65,42 @@ type Package struct {
Runs []Run
}
// DependencyUseType reprensets a type of dependency use.
type DependencyUseType string
const (
// DependencyUseTypeGHAction is an action.
DependencyUseTypeGHAction DependencyUseType = "GitHubAction"
// DependencyUseTypeDockerfileContainerImage a container image used via FROM.
DependencyUseTypeDockerfileContainerImage DependencyUseType = "containerImage"
// DependencyUseTypeDownloadThenRun is a download followed by a run.
DependencyUseTypeDownloadThenRun DependencyUseType = "downloadThenRun"
// DependencyUseTypeGoCommand is a go command.
DependencyUseTypeGoCommand DependencyUseType = "goCommand"
// DependencyUseTypeChocoCommand is a choco command.
DependencyUseTypeChocoCommand DependencyUseType = "chocoCommand"
// DependencyUseTypeNpmCommand is an npm command.
DependencyUseTypeNpmCommand DependencyUseType = "npmCommand"
// DependencyUseTypePipCommand is a pipp command.
DependencyUseTypePipCommand DependencyUseType = "pipCommand"
)
// PinningDependenciesData represents pinned dependency data.
type PinningDependenciesData struct {
Dependencies []Dependency
}
// Dependency represents a dependency.
type Dependency struct {
// TODO: unique dependency name.
// TODO: Job *WorkflowJob
Name *string
PinnedAt *string
Location *File
Msg *string // Only for debug messages.
Type DependencyUseType
}
// MaintainedData contains the raw results
// for the Maintained check.
type MaintainedData struct {
@ -165,10 +202,11 @@ type ArchivedStatus struct {
// File represents a file.
type File struct {
Path string
Snippet string // Snippet of code
Offset uint // Offset in the file of Path (line for source/text files).
Type FileType // Type of file.
Path string
Snippet string // Snippet of code
Offset uint // Offset in the file of Path (line for source/text files).
EndOffset uint // End of offset in the file, e.g. if the command spans multiple lines.
Type FileType // Type of file.
// TODO: add hash.
}

View File

@ -19,7 +19,6 @@ import (
)
var (
errInternalInvalidDockerFile = errors.New("invalid Dockerfile")
errInvalidGitHubWorkflow = errors.New("invalid GitHub workflow")
errInternalNameCannotBeEmpty = errors.New("name cannot be empty")
errInternalCheckFuncCannotBeNil = errors.New("checkFunc cannot be nil")

View File

@ -0,0 +1,292 @@
// 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 evaluation
import (
"errors"
"fmt"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/fileparser"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/remediation"
)
var errInvalidValue = errors.New("invalid value")
type pinnedResult int
const (
pinnedUndefined pinnedResult = iota
pinned
notPinned
)
// Structure to host information about pinned github
// or third party dependencies.
type worklowPinningResult struct {
thirdParties pinnedResult
gitHubOwned pinnedResult
}
// PinningDependencies applies the score policy for the Pinned-Dependencies check.
func PinningDependencies(name string, dl checker.DetailLogger,
r *checker.PinningDependenciesData,
) checker.CheckResult {
if r == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
return checker.CreateRuntimeErrorResult(name, e)
}
var wp worklowPinningResult
pr := make(map[checker.DependencyUseType]pinnedResult)
for i := range r.Dependencies {
rr := r.Dependencies[i]
if rr.Location == nil {
if rr.Msg == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty File field")
return checker.CreateRuntimeErrorResult(name, e)
}
dl.Debug(&checker.LogMessage{
Text: *rr.Msg,
})
continue
}
if rr.Msg != nil {
dl.Debug(&checker.LogMessage{
Path: rr.Location.Path,
Type: rr.Location.Type,
Offset: rr.Location.Offset,
EndOffset: rr.Location.EndOffset,
Text: *rr.Msg,
Snippet: rr.Location.Snippet,
})
} else {
dl.Warn(&checker.LogMessage{
Path: rr.Location.Path,
Type: rr.Location.Type,
Offset: rr.Location.Offset,
EndOffset: rr.Location.EndOffset,
Text: generateText(&rr),
Snippet: rr.Location.Snippet,
Remediation: generateRemediation(&rr),
})
// Update the pinning status.
updatePinningResults(&rr, &wp, pr)
}
}
// Generate scores and Info results.
// GitHub actions.
actionScore, err := createReturnForIsGitHubActionsWorkflowPinned(wp, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
// Docker files.
dockerFromScore, err := createReturnForIsDockerfilePinned(pr, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
// Docker downloads.
dockerDownloadScore, err := createReturnForIsDockerfileFreeOfInsecureDownloads(pr, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
// Script downloads.
scriptScore, err := createReturnForIsShellScriptFreeOfInsecureDownloads(pr, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
// Scores may be inconclusive.
actionScore = maxScore(0, actionScore)
dockerFromScore = maxScore(0, dockerFromScore)
dockerDownloadScore = maxScore(0, dockerDownloadScore)
scriptScore = maxScore(0, scriptScore)
score := checker.AggregateScores(actionScore, dockerFromScore,
dockerDownloadScore, scriptScore)
if score == checker.MaxResultScore {
return checker.CreateMaxScoreResult(name, "all dependencies are pinned")
}
return checker.CreateProportionalScoreResult(name,
"dependency not pinned by hash detected", score, checker.MaxResultScore)
}
func generateRemediation(rr *checker.Dependency) *checker.Remediation {
if rr.Type == checker.DependencyUseTypeGHAction {
return remediation.CreateWorkflowPinningRemediation(rr.Location.Path)
}
return nil
}
func updatePinningResults(rr *checker.Dependency,
wp *worklowPinningResult, pr map[checker.DependencyUseType]pinnedResult,
) {
if rr.Type == checker.DependencyUseTypeGHAction {
// Note: `Snippet` contains `action/name@xxx`, so we cna use it to infer
// if it's a GitHub-owned action or not.
gitHubOwned := fileparser.IsGitHubOwnedAction(rr.Location.Snippet)
addWorkflowPinnedResult(wp, false, gitHubOwned)
return
}
// Update other result types.
var p pinnedResult
addPinnedResult(&p, false)
pr[rr.Type] = p
}
func generateText(rr *checker.Dependency) string {
if rr.Type == checker.DependencyUseTypeGHAction {
// Check if we are dealing with a GitHub action or a third-party one.
gitHubOwned := fileparser.IsGitHubOwnedAction(rr.Location.Snippet)
owner := generateOwnerToDisplay(gitHubOwned)
return fmt.Sprintf("%s %s not pinned by hash", owner, rr.Type)
}
return fmt.Sprintf("%s not pinned by hash", rr.Type)
}
func generateOwnerToDisplay(gitHubOwned bool) string {
if gitHubOwned {
return "GitHub-owned"
}
return "third-party"
}
// TODO(laurent): need to support GCB pinning.
//nolint
func maxScore(s1, s2 int) int {
if s1 > s2 {
return s1
}
return s2
}
// For the 'to' param, true means the file is pinning dependencies (or there are no dependencies),
// false means there are unpinned dependencies.
func addPinnedResult(r *pinnedResult, to bool) {
// If the result is `notPinned`, we keep it.
// In other cases, we always update the result.
if *r == notPinned {
return
}
switch to {
case true:
*r = pinned
case false:
*r = notPinned
}
}
func addWorkflowPinnedResult(w *worklowPinningResult, to, isGitHub bool) {
if isGitHub {
addPinnedResult(&w.gitHubOwned, to)
} else {
addPinnedResult(&w.thirdParties, to)
}
}
// Create the result for scripts.
func createReturnForIsShellScriptFreeOfInsecureDownloads(pr map[checker.DependencyUseType]pinnedResult,
dl checker.DetailLogger,
) (int, error) {
return createReturnValues(pr, checker.DependencyUseTypeDownloadThenRun,
"no insecure (not pinned by hash) dependency downloads found in shell scripts",
dl)
}
// Create the result for docker containers.
func createReturnForIsDockerfilePinned(pr map[checker.DependencyUseType]pinnedResult,
dl checker.DetailLogger,
) (int, error) {
return createReturnValues(pr, checker.DependencyUseTypeDockerfileContainerImage,
"Dockerfile dependencies are pinned",
dl)
}
// Create the result for docker commands.
func createReturnForIsDockerfileFreeOfInsecureDownloads(pr map[checker.DependencyUseType]pinnedResult,
dl checker.DetailLogger,
) (int, error) {
return createReturnValues(pr, checker.DependencyUseTypeDownloadThenRun,
"no insecure (not pinned by hash) dependency downloads found in Dockerfiles",
dl)
}
func createReturnValues(pr map[checker.DependencyUseType]pinnedResult,
t checker.DependencyUseType, infoMsg string,
dl checker.DetailLogger,
) (int, error) {
// Note: we don't check if the entry exists,
// as it will have the default value which is handled in the switch statement.
//nolint
r, _ := pr[t]
switch r {
default:
return checker.InconclusiveResultScore, fmt.Errorf("%w: %v", errInvalidValue, r)
case pinned, pinnedUndefined:
dl.Info(&checker.LogMessage{
Text: infoMsg,
})
return checker.MaxResultScore, nil
case notPinned:
// No logging needed as it's done by the checks.
return checker.MinResultScore, nil
}
}
// Create the result.
func createReturnForIsGitHubActionsWorkflowPinned(wp worklowPinningResult, dl checker.DetailLogger) (int, error) {
return createReturnValuesForGitHubActionsWorkflowPinned(wp,
fmt.Sprintf("%ss are pinned", checker.DependencyUseTypeGHAction),
dl)
}
func createReturnValuesForGitHubActionsWorkflowPinned(r worklowPinningResult, infoMsg string,
dl checker.DetailLogger,
) (int, error) {
score := checker.MinResultScore
if r.gitHubOwned != notPinned {
score += 2
dl.Info(&checker.LogMessage{
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("%s %s", "GitHub-owned", infoMsg),
})
}
if r.thirdParties != notPinned {
score += 8
dl.Info(&checker.LogMessage{
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("%s %s", "Third-party", infoMsg),
})
}
return score, nil
}

View File

@ -0,0 +1,312 @@
// 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 (
"testing"
"github.com/ossf/scorecard/v4/checker"
scut "github.com/ossf/scorecard/v4/utests"
)
func Test_createReturnValuesForGitHubActionsWorkflowPinned(t *testing.T) {
t.Parallel()
//nolint
type args struct {
r worklowPinningResult
infoMsg string
dl checker.DetailLogger
}
//nolint
tests := []struct {
name string
args args
want int
}{
{
name: "both actions workflow pinned",
args: args{
r: worklowPinningResult{
thirdParties: 1,
gitHubOwned: 1,
},
dl: &scut.TestDetailLogger{},
},
want: 10,
},
{
name: "github actions workflow pinned",
args: args{
r: worklowPinningResult{
thirdParties: 2,
gitHubOwned: 2,
},
dl: &scut.TestDetailLogger{},
},
want: 0,
},
{
name: "error in github actions workflow pinned",
args: args{
r: worklowPinningResult{
thirdParties: 2,
gitHubOwned: 2,
},
dl: &scut.TestDetailLogger{},
},
want: 0,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := createReturnValuesForGitHubActionsWorkflowPinned(tt.args.r, tt.args.infoMsg, tt.args.dl)
if err != nil {
t.Errorf("error during createReturnValuesForGitHubActionsWorkflowPinned: %v", err)
}
if got != tt.want {
t.Errorf("createReturnValuesForGitHubActionsWorkflowPinned() = %v, want %v", got, tt.want)
}
})
}
}
func asPointer(s string) *string {
return &s
}
func Test_PinningDependencies(t *testing.T) {
t.Parallel()
tests := []struct {
name string
dependencies []checker.Dependency
expected scut.TestReturn
}{
{
name: "download then run pinned debug",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Msg: asPointer("some message"),
Type: checker.DependencyUseTypeDownloadThenRun,
},
},
expected: scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 5,
NumberOfDebug: 1,
},
},
{
name: "download then run pinned debug and warn",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Msg: asPointer("some message"),
Type: checker.DependencyUseTypeDownloadThenRun,
},
{
Location: &checker.File{},
Type: checker.DependencyUseTypeDownloadThenRun,
},
},
expected: scut.TestReturn{
Error: nil,
Score: 5,
NumberOfWarn: 1,
NumberOfInfo: 3,
NumberOfDebug: 1,
},
},
{
name: "various wanrings",
dependencies: []checker.Dependency{
{
Location: &checker.File{},
Type: checker.DependencyUseTypePipCommand,
},
{
Location: &checker.File{},
Type: checker.DependencyUseTypeDownloadThenRun,
},
{
Location: &checker.File{},
Type: checker.DependencyUseTypeDockerfileContainerImage,
},
{
Location: &checker.File{},
Msg: asPointer("debug message"),
},
},
expected: scut.TestReturn{
Error: nil,
Score: 2,
NumberOfWarn: 3,
NumberOfInfo: 2,
NumberOfDebug: 1,
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dl := scut.TestDetailLogger{}
actual := PinningDependencies("checkname", &dl,
&checker.PinningDependenciesData{
Dependencies: tt.dependencies,
})
if !scut.ValidateTestReturn(t, tt.name, &tt.expected, &actual, &dl) {
t.Fail()
}
})
}
}
func Test_createReturnValues(t *testing.T) {
t.Parallel()
type args struct {
pr map[checker.DependencyUseType]pinnedResult
dl *scut.TestDetailLogger
t checker.DependencyUseType
}
tests := []struct {
name string
args args
want int
}{
{
name: "returns 10 if no error and no pinnedResult",
args: args{
t: checker.DependencyUseTypeDownloadThenRun,
dl: &scut.TestDetailLogger{},
},
want: 10,
},
{
name: "returns 10 if pinned undefined",
args: args{
t: checker.DependencyUseTypeDownloadThenRun,
pr: map[checker.DependencyUseType]pinnedResult{
checker.DependencyUseTypeDownloadThenRun: pinnedUndefined,
},
dl: &scut.TestDetailLogger{},
},
want: 10,
},
{
name: "returns 10 if pinned",
args: args{
t: checker.DependencyUseTypeDownloadThenRun,
pr: map[checker.DependencyUseType]pinnedResult{
checker.DependencyUseTypeDownloadThenRun: pinned,
},
dl: &scut.TestDetailLogger{},
},
want: 10,
},
{
name: "returns 0 if unpinned",
args: args{
t: checker.DependencyUseTypeDownloadThenRun,
pr: map[checker.DependencyUseType]pinnedResult{
checker.DependencyUseTypeDownloadThenRun: notPinned,
},
dl: &scut.TestDetailLogger{},
},
want: 0,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := createReturnValues(tt.args.pr, tt.args.t, "some message", tt.args.dl)
if err != nil {
t.Errorf("error during createReturnValues: %v", err)
}
if got != tt.want {
t.Errorf("createReturnValues() = %v, want %v", got, tt.want)
}
if tt.want < 10 {
return
}
isExpectedLog := func(logMessage checker.LogMessage, logType checker.DetailType) bool {
return logMessage.Text == "some message" && logType == checker.DetailInfo
}
if !scut.ValidateLogMessage(isExpectedLog, tt.args.dl) {
t.Errorf("test failed: log message not present: %+v", "some message")
}
})
}
}
func Test_maxScore(t *testing.T) {
t.Parallel()
type args struct {
s1 int
s2 int
}
tests := []struct {
name string
args args
want int
}{
{
name: "returns s1 if s1 is greater than s2",
args: args{
s1: 10,
s2: 5,
},
want: 10,
},
{
name: "returns s2 if s2 is greater than s1",
args: args{
s1: 5,
s2: 10,
},
want: 10,
},
{
name: "returns s1 if s1 is equal to s2",
args: args{
s1: 10,
s2: 10,
},
want: 10,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := maxScore(tt.args.s1, tt.args.s2); got != tt.want {
t.Errorf("maxScore() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -15,716 +15,47 @@
package checks
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/rhysd/actionlint"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/fileparser"
"github.com/ossf/scorecard/v4/checks/evaluation"
"github.com/ossf/scorecard/v4/checks/raw"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/remediation"
)
// CheckPinnedDependencies is the registered name for FrozenDeps.
const CheckPinnedDependencies = "Pinned-Dependencies"
// Structure to host information about pinned github
// or third party dependencies.
type worklowPinningResult struct {
thirdParties pinnedResult
gitHubOwned pinnedResult
}
//nolint:gochecknoinits
func init() {
supportedRequestTypes := []checker.RequestType{
checker.FileBased,
checker.CommitBased,
}
if err := registerCheck(CheckPinnedDependencies, PinnedDependencies, supportedRequestTypes); err != nil {
if err := registerCheck(CheckPinnedDependencies, PinningDependencies, supportedRequestTypes); err != nil {
// This should never happen.
panic(err)
}
}
// PinnedDependencies will check the repository if it contains frozen dependecies.
func PinnedDependencies(c *checker.CheckRequest) checker.CheckResult {
// Lock file.
/* WARNING: this code is inherently incorrect:
- does not differentiate between libs and main
- only looks at root folder.
=> disabling to avoid false positives.
lockScore, lockErr := isPackageManagerLockFilePresent(c)
if lockErr != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, lockErr)
}
*/
if remErr := remdiationSetup(c); remErr != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, remErr)
// PinningDependencies will check the repository for its use of dependencies.
func PinningDependencies(c *checker.CheckRequest) checker.CheckResult {
if err := remediation.Setup(c); err != nil {
e := sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("remdiationSetup: %v", err))
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, e)
}
// GitHub actions.
actionScore, actionErr := isGitHubActionsWorkflowPinned(c)
if actionErr != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, actionErr)
}
// Docker files.
dockerFromScore, dockerFromErr := isDockerfilePinned(c)
if dockerFromErr != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, dockerFromErr)
}
// Docker downloads.
dockerDownloadScore, dockerDownloadErr := isDockerfileFreeOfInsecureDownloads(c)
if dockerDownloadErr != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, dockerDownloadErr)
}
// Script downloads.
scriptScore, scriptError := isShellScriptFreeOfInsecureDownloads(c)
if scriptError != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, scriptError)
}
// Action script downloads.
actionScriptScore, actionScriptError := isGitHubWorkflowScriptFreeOfInsecureDownloads(c)
if actionScriptError != nil {
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, actionScriptError)
}
// Scores may be inconclusive.
actionScore = maxScore(0, actionScore)
dockerFromScore = maxScore(0, dockerFromScore)
dockerDownloadScore = maxScore(0, dockerDownloadScore)
scriptScore = maxScore(0, scriptScore)
actionScriptScore = maxScore(0, actionScriptScore)
score := checker.AggregateScores(actionScore, dockerFromScore,
dockerDownloadScore, scriptScore, actionScriptScore)
if score == checker.MaxResultScore {
return checker.CreateMaxScoreResult(CheckPinnedDependencies, "all dependencies are pinned")
}
return checker.CreateProportionalScoreResult(CheckPinnedDependencies,
"dependency not pinned by hash detected", score, checker.MaxResultScore)
}
// TODO(laurent): need to support GCB pinning.
//nolint
func maxScore(s1, s2 int) int {
if s1 > s2 {
return s1
}
return s2
}
type pinnedResult int
const (
pinnedUndefined pinnedResult = iota
pinned
notPinned
)
// For the 'to' param, true means the file is pinning dependencies (or there are no dependencies),
// false means there are unpinned dependencies.
func addPinnedResult(r *pinnedResult, to bool) {
// If the result is `notPinned`, we keep it.
// In other cases, we always update the result.
if *r == notPinned {
return
}
switch to {
case true:
*r = pinned
case false:
*r = notPinned
}
}
func dataAsWorkflowResultPointer(data interface{}) *worklowPinningResult {
pdata, ok := data.(*worklowPinningResult)
if !ok {
// panic if it is not correct type
panic("type need to be of worklowPinningResult")
}
return pdata
}
func dataAsResultPointer(data interface{}) *pinnedResult {
pdata, ok := data.(*pinnedResult)
if !ok {
// This never happens.
panic("invalid type")
}
return pdata
}
func dataAsDetailLogger(data interface{}) checker.DetailLogger {
pdata, ok := data.(checker.DetailLogger)
if !ok {
// This never happens.
panic("invalid type")
}
return pdata
}
func createReturnValuesForGitHubActionsWorkflowPinned(r worklowPinningResult, infoMsg string,
dl checker.DetailLogger, err error,
) (int, error) {
rawData, err := raw.PinningDependencies(c)
if err != nil {
return checker.InconclusiveResultScore, err
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, e)
}
score := checker.MinResultScore
if r.gitHubOwned != notPinned {
score += 2
dl.Info(&checker.LogMessage{
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("%s %s", "GitHub-owned", infoMsg),
})
// Set the raw results.
if c.RawResults != nil {
c.RawResults.PinningDependenciesResults = rawData
}
if r.thirdParties != notPinned {
score += 8
dl.Info(&checker.LogMessage{
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("%s %s", "Third-party", infoMsg),
})
}
return score, nil
}
func createReturnValues(r pinnedResult, infoMsg string, dl checker.DetailLogger, err error) (int, error) {
if err != nil {
return checker.InconclusiveResultScore, err
}
switch r {
default:
panic("invalid value")
case pinned, pinnedUndefined:
dl.Info(&checker.LogMessage{
Text: infoMsg,
})
return checker.MaxResultScore, nil
case notPinned:
// No logging needed as it's done by the checks.
return checker.MinResultScore, nil
}
}
func isShellScriptFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
var r pinnedResult
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*",
CaseSensitive: false,
}, validateShellScriptIsFreeOfInsecureDownloads, c.Dlogger, &r)
return createReturnForIsShellScriptFreeOfInsecureDownloads(r, c.Dlogger, err)
}
func createReturnForIsShellScriptFreeOfInsecureDownloads(r pinnedResult,
dl checker.DetailLogger, err error,
) (int, error) {
return createReturnValues(r,
"no insecure (not pinned by hash) dependency downloads found in shell scripts",
dl, err)
}
var validateShellScriptIsFreeOfInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if len(args) != 2 {
return false, fmt.Errorf(
"validateShellScriptIsFreeOfInsecureDownloads requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata := dataAsResultPointer(args[1])
dl := dataAsDetailLogger(args[0])
// Validate the file type.
if !isSupportedShellScriptFile(pathfn, content) {
addPinnedResult(pdata, true)
return true, nil
}
r, err := validateShellFile(pathfn, 0, 0 /*unknown*/, content, map[string]bool{}, dl)
if err != nil {
// Ignore parsing errors.
if errors.Is(err, sce.ErrorShellParsing) {
addPinnedResult(pdata, true)
}
return false, nil
}
addPinnedResult(pdata, r)
return true, nil
}
func isDockerfileFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
var r pinnedResult
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*Dockerfile*",
CaseSensitive: false,
}, validateDockerfileIsFreeOfInsecureDownloads, c.Dlogger, &r)
return createReturnForIsDockerfileFreeOfInsecureDownloads(r, c.Dlogger, err)
}
// Create the result.
func createReturnForIsDockerfileFreeOfInsecureDownloads(r pinnedResult,
dl checker.DetailLogger, err error,
) (int, error) {
return createReturnValues(r,
"no insecure (not pinned by hash) dependency downloads found in Dockerfiles",
dl, err)
}
func isDockerfile(pathfn string, content []byte) bool {
if strings.HasSuffix(pathfn, ".go") ||
strings.HasSuffix(pathfn, ".c") ||
strings.HasSuffix(pathfn, ".cpp") ||
strings.HasSuffix(pathfn, ".rs") ||
strings.HasSuffix(pathfn, ".js") ||
strings.HasSuffix(pathfn, ".py") ||
strings.HasSuffix(pathfn, ".pyc") ||
strings.HasSuffix(pathfn, ".java") ||
isShellScriptFile(pathfn, content) {
return false
}
return true
}
var validateDockerfileIsFreeOfInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if len(args) != 2 {
return false, fmt.Errorf(
"validateDockerfileIsFreeOfInsecureDownloads requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata := dataAsResultPointer(args[1])
dl := dataAsDetailLogger(args[0])
// Return early if this is not a docker file.
if !isDockerfile(pathfn, content) {
addPinnedResult(pdata, true)
return true, nil
}
if !fileparser.CheckFileContainsCommands(content, "#") {
addPinnedResult(pdata, true)
return true, nil
}
contentReader := strings.NewReader(string(content))
res, err := parser.Parse(contentReader)
if err != nil {
return false, sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("%v: %v", errInternalInvalidDockerFile, err))
}
// Walk the Dockerfile's AST.
taintedFiles := make(map[string]bool)
for i := range res.AST.Children {
var bytes []byte
child := res.AST.Children[i]
cmdType := child.Value
// Only look for the 'RUN' command.
if cmdType != "RUN" {
continue
}
var valueList []string
for n := child.Next; n != nil; n = n.Next {
valueList = append(valueList, n.Value)
}
if len(valueList) == 0 {
return false, sce.WithMessage(sce.ErrScorecardInternal, errInternalInvalidDockerFile.Error())
}
// Build a file content.
cmd := strings.Join(valueList, " ")
bytes = append(bytes, cmd...)
r, err := validateShellFile(pathfn, uint(child.StartLine)-1, uint(child.EndLine)-1,
bytes, taintedFiles, dl)
if err != nil {
// Ignore parsing errors.
if errors.Is(err, sce.ErrorShellParsing) {
addPinnedResult(pdata, true)
}
return false, err
}
addPinnedResult(pdata, r)
}
return true, nil
}
func isDockerfilePinned(c *checker.CheckRequest) (int, error) {
var r pinnedResult
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*Dockerfile*",
CaseSensitive: false,
}, validateDockerfileIsPinned, c.Dlogger, &r)
return createReturnForIsDockerfilePinned(r, c.Dlogger, err)
}
// Create the result.
func createReturnForIsDockerfilePinned(r pinnedResult, dl checker.DetailLogger, err error) (int, error) {
return createReturnValues(r,
"Dockerfile dependencies are pinned",
dl, err)
}
var validateDockerfileIsPinned fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
// Users may use various names, e.g.,
// Dockerfile.aarch64, Dockerfile.template, Dockerfile_template, dockerfile, Dockerfile-name.template
if len(args) != 2 {
return false, fmt.Errorf(
"validateDockerfileIsPinned requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata := dataAsResultPointer(args[1])
dl := dataAsDetailLogger(args[0])
// Return early if this is not a dockerfile.
if !isDockerfile(pathfn, content) {
addPinnedResult(pdata, true)
return true, nil
}
if !fileparser.CheckFileContainsCommands(content, "#") {
addPinnedResult(pdata, true)
return true, nil
}
if fileparser.IsTemplateFile(pathfn) {
addPinnedResult(pdata, true)
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))
// The dependency must be pinned by sha256 hash, e.g.,
// FROM something@sha256:${ARG},
// FROM something:@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
regex := regexp.MustCompile(`.*@sha256:([a-f\d]{64}|\${.*})`)
ret := true
pinnedAsNames := make(map[string]bool)
res, err := parser.Parse(contentReader)
if err != nil {
return false, sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("%v: %v", errInternalInvalidDockerFile, err))
}
for _, child := range res.AST.Children {
cmdType := child.Value
if cmdType != "FROM" {
continue
}
var valueList []string
for n := child.Next; n != nil; n = n.Next {
valueList = append(valueList, n.Value)
}
switch {
// scratch is no-op.
case len(valueList) > 0 && strings.EqualFold(valueList[0], "scratch"):
continue
// FROM name AS newname.
case len(valueList) == 3 && strings.EqualFold(valueList[1], "as"):
name := valueList[0]
asName := valueList[2]
// Check if the name is pinned.
// (1): name = <>@sha245:hash
// (2): name = XXX where XXX was pinned
pinned := pinnedAsNames[name]
if pinned || regex.MatchString(name) {
// Record the asName.
pinnedAsNames[asName] = true
continue
}
// Not pinned.
ret = false
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: uint(child.StartLine),
EndOffset: uint(child.EndLine),
Text: "docker image not pinned by hash",
Snippet: child.Original,
})
// FROM name.
case len(valueList) == 1:
name := valueList[0]
pinned := pinnedAsNames[name]
if !pinned && !regex.MatchString(name) {
ret = false
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: uint(child.StartLine),
EndOffset: uint(child.EndLine),
Text: "docker image not pinned by hash",
Snippet: child.Original,
})
}
default:
// That should not happen.
return false, sce.WithMessage(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.
addPinnedResult(pdata, ret)
return true, nil
}
func isGitHubWorkflowScriptFreeOfInsecureDownloads(c *checker.CheckRequest) (int, error) {
var r pinnedResult
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: false,
}, validateGitHubWorkflowIsFreeOfInsecureDownloads, c.Dlogger, &r)
return createReturnForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r, c.Dlogger, err)
}
// Create the result.
func createReturnForIsGitHubWorkflowScriptFreeOfInsecureDownloads(r pinnedResult,
dl checker.DetailLogger, err error,
) (int, error) {
return createReturnValues(r,
"no insecure (not pinned by hash) dependency downloads found in GitHub workflows",
dl, err)
}
// validateGitHubWorkflowIsFreeOfInsecureDownloads checks if the workflow file downloads dependencies that are unpinned.
// Returns true if the check should continue executing after this file.
var validateGitHubWorkflowIsFreeOfInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(pathfn) {
return true, nil
}
if len(args) != 2 {
return false, fmt.Errorf(
"validateGitHubWorkflowIsFreeOfInsecureDownloads requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata := dataAsResultPointer(args[1])
dl := dataAsDetailLogger(args[0])
if !fileparser.CheckFileContainsCommands(content, "#") {
addPinnedResult(pdata, true)
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
// actionlint is a linter, so it will return errors when the yaml file does not meet its linting standards.
// Often we don't care about these errors.
return false, fileparser.FormatActionlintError(errs)
}
githubVarRegex := regexp.MustCompile(`{{[^{}]*}}`)
for jobName, job := range workflow.Jobs {
jobName := jobName
job := job
if len(fileparser.GetJobName(job)) > 0 {
jobName = fileparser.GetJobName(job)
}
taintedFiles := make(map[string]bool)
for _, step := range job.Steps {
step := step
if !fileparser.IsStepExecKind(step, actionlint.ExecKindRun) {
continue
}
execRun, ok := step.Exec.(*actionlint.ExecRun)
if !ok {
stepName := fileparser.GetStepName(step)
return false, sce.WithMessage(sce.ErrScorecardInternal,
fmt.Sprintf("unable to parse step '%v' for job '%v'", jobName, stepName))
}
if execRun == nil || execRun.Run == nil {
// Cannot check further, continue.
continue
}
run := execRun.Run.Value
// https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
shell, err := fileparser.GetShellForStep(step, job)
if err != nil {
return false, err
}
// Skip unsupported shells. We don't support Windows shells or some Unix shells.
if !isSupportedShell(shell) {
continue
}
// We replace the `${{ github.variable }}` to avoid shell parsing failures.
script := githubVarRegex.ReplaceAll([]byte(run), []byte("GITHUB_REDACTED_VAR"))
validated, err := validateShellFile(pathfn, uint(execRun.Run.Pos.Line), uint(execRun.Run.Pos.Line),
script, taintedFiles, dl)
if err != nil {
// Ignore parsing errors.
if errors.Is(err, sce.ErrorShellParsing) {
addPinnedResult(pdata, true)
}
return false, err
}
addPinnedResult(pdata, validated)
}
}
return true, nil
}
// Check pinning of github actions in workflows.
func isGitHubActionsWorkflowPinned(c *checker.CheckRequest) (int, error) {
var r worklowPinningResult
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: true,
}, validateGitHubActionWorkflow, c.Dlogger, &r)
return createReturnForIsGitHubActionsWorkflowPinned(r, c.Dlogger, err)
}
// Create the result.
func createReturnForIsGitHubActionsWorkflowPinned(r worklowPinningResult, dl checker.DetailLogger,
err error,
) (int, error) {
return createReturnValuesForGitHubActionsWorkflowPinned(r,
"actions are pinned",
dl, err)
}
func generateOwnerToDisplay(gitHubOwned bool) string {
if gitHubOwned {
return "GitHub-owned"
}
return "third-party"
}
// validateGitHubActionWorkflow checks if the workflow file contains unpinned actions. Returns true if the check
// should continue executing after this file.
var validateGitHubActionWorkflow fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(pathfn) {
return true, nil
}
if len(args) != 2 {
return false, fmt.Errorf(
"validateGitHubActionWorkflow requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata := dataAsWorkflowResultPointer(args[1])
dl := dataAsDetailLogger(args[0])
if !fileparser.CheckFileContainsCommands(content, "#") {
addWorkflowPinnedResult(pdata, true, true)
addWorkflowPinnedResult(pdata, true, true)
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
// actionlint is a linter, so it will return errors when the yaml file does not meet its linting standards.
// Often we don't care about these errors.
return false, fileparser.FormatActionlintError(errs)
}
hashRegex := regexp.MustCompile(`^.*@[a-f\d]{40,}`)
for jobName, job := range workflow.Jobs {
jobName := jobName
job := job
if len(fileparser.GetJobName(job)) > 0 {
jobName = fileparser.GetJobName(job)
}
for _, step := range job.Steps {
if !fileparser.IsStepExecKind(step, actionlint.ExecKindAction) {
continue
}
execAction, ok := step.Exec.(*actionlint.ExecAction)
if !ok {
stepName := fileparser.GetStepName(step)
return false, sce.WithMessage(sce.ErrScorecardInternal,
fmt.Sprintf("unable to parse step '%v' for job '%v'", jobName, stepName))
}
if execAction == nil || execAction.Uses == nil {
// Cannot check further, continue.
continue
}
// nolint:lll
// Check whether this is an action defined in the same repo,
// https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions#referencing-an-action-in-the-same-repository-where-a-workflow-file-uses-the-action.
if strings.HasPrefix(execAction.Uses.Value, "./") {
continue
}
// Check if we are dealing with a GitHub action or a third-party one.
gitHubOwned := fileparser.IsGitHubOwnedAction(execAction.Uses.Value)
owner := generateOwnerToDisplay(gitHubOwned)
// Ensure a hash at least as large as SHA1 is used (40 hex characters).
// Example: action-name@hash
match := hashRegex.MatchString(execAction.Uses.Value)
if !match {
dl.Warn(&checker.LogMessage{
Path: pathfn, Type: checker.FileTypeSource,
Offset: uint(execAction.Uses.Pos.Line),
EndOffset: uint(execAction.Uses.Pos.Line), // `Uses` always span a single line.
Snippet: execAction.Uses.Value,
Text: fmt.Sprintf("%s action not pinned by hash", owner),
Remediation: createWorkflowPinningRemediation(pathfn),
})
}
addWorkflowPinnedResult(pdata, match, gitHubOwned)
}
}
return true, nil
}
func addWorkflowPinnedResult(w *worklowPinningResult, to, isGitHub bool) {
if isGitHub {
addPinnedResult(&w.gitHubOwned, to)
} else {
addPinnedResult(&w.thirdParties, to)
}
return evaluation.PinningDependencies(CheckPinnedDependencies, c.Dlogger, &rawData)
}

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,9 @@ import (
)
var (
errInternalCommitishNil = errors.New("commitish is nil")
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
errInvalidGitHubWorkflow = errors.New("invalid GitHub workflow")
errInternalInvalidDockerFile = errors.New("invalid Dockerfile")
errInternalCommitishNil = errors.New("commitish is nil")
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
errInvalidGitHubWorkflow = errors.New("invalid GitHub workflow")
)

View File

@ -0,0 +1,499 @@
// Copyright 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"
"reflect"
"regexp"
"strings"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/rhysd/actionlint"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/fileparser"
sce "github.com/ossf/scorecard/v4/errors"
)
// PinningDependencies checks for (un)pinned dependencies.
func PinningDependencies(c *checker.CheckRequest) (checker.PinningDependenciesData, error) {
var results checker.PinningDependenciesData
// GitHub actions.
if err := collectGitHubActionsWorkflowPinning(c, &results); err != nil {
return checker.PinningDependenciesData{}, err
}
// // Docker files.
if err := collectDockerfilePinning(c, &results); err != nil {
return checker.PinningDependenciesData{}, err
}
// Docker downloads.
if err := collectDockerfileInsecureDownloads(c, &results); err != nil {
return checker.PinningDependenciesData{}, err
}
// Script downloads.
if err := collectShellScriptInsecureDownloads(c, &results); err != nil {
return checker.PinningDependenciesData{}, err
}
// Action script downloads.
if err := collectGitHubWorkflowScriptInsecureDownloads(c, &results); err != nil {
return checker.PinningDependenciesData{}, err
}
return results, nil
}
func dataAsPinnedDependenciesPointer(data interface{}) *checker.PinningDependenciesData {
pdata, ok := data.(*checker.PinningDependenciesData)
if !ok {
// panic if it is not correct type
panic(fmt.Sprintf("expected type PinningDependenciesData, got %v", reflect.TypeOf(data)))
}
return pdata
}
func collectShellScriptInsecureDownloads(c *checker.CheckRequest, r *checker.PinningDependenciesData) error {
return fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*",
CaseSensitive: false,
}, validateShellScriptIsFreeOfInsecureDownloads, r)
}
var validateShellScriptIsFreeOfInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if len(args) != 1 {
return false, fmt.Errorf(
"validateShellScriptIsFreeOfInsecureDownloads requires exactly 1 arguments: got %v: %w",
len(args), errInvalidArgLength)
}
pdata := dataAsPinnedDependenciesPointer(args[0])
// Validate the file type.
if !isSupportedShellScriptFile(pathfn, content) {
return true, nil
}
if err := validateShellFile(pathfn, 0, 0, content, map[string]bool{}, pdata); err != nil {
return false, nil
}
return true, nil
}
func collectDockerfileInsecureDownloads(c *checker.CheckRequest, r *checker.PinningDependenciesData) error {
return fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*Dockerfile*",
CaseSensitive: false,
}, validateDockerfileInsecureDownloads, r)
}
var validateDockerfileInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if len(args) != 1 {
return false, fmt.Errorf(
"validateDockerfileInsecureDownloads requires exactly 1 arguments: got %v: %w",
len(args), errInvalidArgLength)
}
pdata := dataAsPinnedDependenciesPointer(args[0])
// Return early if this is not a docker file.
if !isDockerfile(pathfn, content) {
return true, nil
}
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
contentReader := strings.NewReader(string(content))
res, err := parser.Parse(contentReader)
if err != nil {
return false, sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("%v: %v", errInternalInvalidDockerFile, err))
}
// Walk the Dockerfile's AST.
taintedFiles := make(map[string]bool)
for i := range res.AST.Children {
var bytes []byte
child := res.AST.Children[i]
cmdType := child.Value
// Only look for the 'RUN' command.
if cmdType != "RUN" {
continue
}
var valueList []string
for n := child.Next; n != nil; n = n.Next {
valueList = append(valueList, n.Value)
}
if len(valueList) == 0 {
return false, sce.WithMessage(sce.ErrScorecardInternal, errInternalInvalidDockerFile.Error())
}
// Build a file content.
cmd := strings.Join(valueList, " ")
bytes = append(bytes, cmd...)
if err := validateShellFile(pathfn, uint(child.StartLine)-1, uint(child.EndLine)-1,
bytes, taintedFiles, pdata); err != nil {
return false, err
}
}
return true, nil
}
func isDockerfile(pathfn string, content []byte) bool {
if strings.HasSuffix(pathfn, ".go") ||
strings.HasSuffix(pathfn, ".c") ||
strings.HasSuffix(pathfn, ".cpp") ||
strings.HasSuffix(pathfn, ".rs") ||
strings.HasSuffix(pathfn, ".js") ||
strings.HasSuffix(pathfn, ".py") ||
strings.HasSuffix(pathfn, ".pyc") ||
strings.HasSuffix(pathfn, ".java") ||
isShellScriptFile(pathfn, content) {
return false
}
return true
}
func collectDockerfilePinning(c *checker.CheckRequest, r *checker.PinningDependenciesData) error {
return fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: "*Dockerfile*",
CaseSensitive: false,
}, validateDockerfilesPinning, r)
}
var validateDockerfilesPinning fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
// Users may use various names, e.g.,
// Dockerfile.aarch64, Dockerfile.template, Dockerfile_template, dockerfile, Dockerfile-name.template
if len(args) != 1 {
return false, fmt.Errorf(
"validateDockerfilesPinning requires exactly 2 arguments: got %v: %w", len(args), errInvalidArgLength)
}
pdata := dataAsPinnedDependenciesPointer(args[0])
// Return early if this is not a dockerfile.
if !isDockerfile(pathfn, content) {
return true, nil
}
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
if fileparser.IsTemplateFile(pathfn) {
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))
// The dependency must be pinned by sha256 hash, e.g.,
// FROM something@sha256:${ARG},
// FROM something:@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
regex := regexp.MustCompile(`.*@sha256:([a-f\d]{64}|\${.*})`)
pinnedAsNames := make(map[string]bool)
res, err := parser.Parse(contentReader)
if err != nil {
return false, sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("%v: %v", errInternalInvalidDockerFile, err))
}
for _, child := range res.AST.Children {
cmdType := child.Value
if cmdType != "FROM" {
continue
}
var valueList []string
for n := child.Next; n != nil; n = n.Next {
valueList = append(valueList, n.Value)
}
switch {
// scratch is no-op.
case len(valueList) > 0 && strings.EqualFold(valueList[0], "scratch"):
continue
// FROM name AS newname.
case len(valueList) == 3 && strings.EqualFold(valueList[1], "as"):
name := valueList[0]
asName := valueList[2]
// Check if the name is pinned.
// (1): name = <>@sha245:hash
// (2): name = XXX where XXX was pinned
pinned := pinnedAsNames[name]
if pinned || regex.MatchString(name) {
// Record the asName.
pinnedAsNames[asName] = true
continue
}
pdata.Dependencies = append(pdata.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: uint(child.StartLine),
EndOffset: uint(child.EndLine),
Snippet: child.Original,
},
Name: asPointer(name),
PinnedAt: asPointer(asName),
Type: checker.DependencyUseTypeDockerfileContainerImage,
},
)
// FROM name.
case len(valueList) == 1:
name := valueList[0]
pinned := pinnedAsNames[name]
if !pinned && !regex.MatchString(name) {
dep := checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: uint(child.StartLine),
EndOffset: uint(child.EndLine),
Snippet: child.Original,
},
Type: checker.DependencyUseTypeDockerfileContainerImage,
}
parts := strings.SplitN(name, ":", 2)
if len(parts) > 0 {
dep.Name = asPointer(parts[0])
if len(parts) > 1 {
dep.PinnedAt = asPointer(parts[1])
}
}
pdata.Dependencies = append(pdata.Dependencies, dep)
}
default:
// That should not happen.
return false, sce.WithMessage(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 true, nil
}
func collectGitHubWorkflowScriptInsecureDownloads(c *checker.CheckRequest, r *checker.PinningDependenciesData) error {
return fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: false,
}, validateGitHubWorkflowIsFreeOfInsecureDownloads, r)
}
// validateGitHubWorkflowIsFreeOfInsecureDownloads checks if the workflow file downloads dependencies that are unpinned.
// Returns true if the check should continue executing after this file.
var validateGitHubWorkflowIsFreeOfInsecureDownloads fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(pathfn) {
return true, nil
}
if len(args) != 1 {
return false, fmt.Errorf(
"validateGitHubWorkflowIsFreeOfInsecureDownloads requires exactly 1 arguments: got %v: %w",
len(args), errInvalidArgLength)
}
pdata := dataAsPinnedDependenciesPointer(args[0])
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
// actionlint is a linter, so it will return errors when the yaml file does not meet its linting standards.
// Often we don't care about these errors.
return false, fileparser.FormatActionlintError(errs)
}
githubVarRegex := regexp.MustCompile(`{{[^{}]*}}`)
for jobName, job := range workflow.Jobs {
jobName := jobName
job := job
if len(fileparser.GetJobName(job)) > 0 {
jobName = fileparser.GetJobName(job)
}
taintedFiles := make(map[string]bool)
for _, step := range job.Steps {
step := step
if !fileparser.IsStepExecKind(step, actionlint.ExecKindRun) {
continue
}
execRun, ok := step.Exec.(*actionlint.ExecRun)
if !ok {
stepName := fileparser.GetStepName(step)
return false, sce.WithMessage(sce.ErrScorecardInternal,
fmt.Sprintf("unable to parse step '%v' for job '%v'", jobName, stepName))
}
if execRun == nil || execRun.Run == nil {
// Cannot check further, continue.
continue
}
run := execRun.Run.Value
// https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
shell, err := fileparser.GetShellForStep(step, job)
if err != nil {
return false, err
}
// Skip unsupported shells. We don't support Windows shells or some Unix shells.
if !isSupportedShell(shell) {
continue
}
// We replace the `${{ github.variable }}` to avoid shell parsing failures.
script := githubVarRegex.ReplaceAll([]byte(run), []byte("GITHUB_REDACTED_VAR"))
if err := validateShellFile(pathfn, uint(execRun.Run.Pos.Line), uint(execRun.Run.Pos.Line),
script, taintedFiles, pdata); err != nil {
pdata.Dependencies = append(pdata.Dependencies, checker.Dependency{
Msg: asPointer(err.Error()),
})
}
}
}
return true, nil
}
// Check pinning of github actions in workflows.
func collectGitHubActionsWorkflowPinning(c *checker.CheckRequest, r *checker.PinningDependenciesData) error {
return fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: true,
}, validateGitHubActionWorkflow, r)
}
// validateGitHubActionWorkflow checks if the workflow file contains unpinned actions. Returns true if the check
// should continue executing after this file.
var validateGitHubActionWorkflow fileparser.DoWhileTrueOnFileContent = func(
pathfn string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(pathfn) {
return true, nil
}
if len(args) != 1 {
return false, fmt.Errorf(
"validateGitHubActionWorkflow requires exactly 1 arguments: got %v: %w", len(args), errInvalidArgLength)
}
pdata := dataAsPinnedDependenciesPointer(args[0])
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
// actionlint is a linter, so it will return errors when the yaml file does not meet its linting standards.
// Often we don't care about these errors.
return false, fileparser.FormatActionlintError(errs)
}
hashRegex := regexp.MustCompile(`^.*@[a-f\d]{40,}`)
for jobName, job := range workflow.Jobs {
jobName := jobName
job := job
if len(fileparser.GetJobName(job)) > 0 {
jobName = fileparser.GetJobName(job)
}
for _, step := range job.Steps {
if !fileparser.IsStepExecKind(step, actionlint.ExecKindAction) {
continue
}
execAction, ok := step.Exec.(*actionlint.ExecAction)
if !ok {
stepName := fileparser.GetStepName(step)
return false, sce.WithMessage(sce.ErrScorecardInternal,
fmt.Sprintf("unable to parse step '%v' for job '%v'", jobName, stepName))
}
if execAction == nil || execAction.Uses == nil {
// Cannot check further, continue.
continue
}
// nolint:lll
// Check whether this is an action defined in the same repo,
// https://docs.github.com/en/actions/learn-github-actions/finding-and-customizing-actions#referencing-an-action-in-the-same-repository-where-a-workflow-file-uses-the-action.
if strings.HasPrefix(execAction.Uses.Value, "./") {
continue
}
// Ensure a hash at least as large as SHA1 is used (40 hex characters).
// Example: action-name@hash
match := hashRegex.MatchString(execAction.Uses.Value)
if !match {
dep := checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: uint(execAction.Uses.Pos.Line),
EndOffset: uint(execAction.Uses.Pos.Line), // `Uses` always span a single line.
Snippet: execAction.Uses.Value,
},
Type: checker.DependencyUseTypeGHAction,
}
parts := strings.SplitN(execAction.Uses.Value, "@", 2)
if len(parts) > 0 {
dep.Name = asPointer(parts[0])
if len(parts) > 1 {
dep.PinnedAt = asPointer(parts[1])
}
}
pdata.Dependencies = append(pdata.Dependencies, dep)
}
}
}
return true, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package checks
package raw
import (
"bufio"
@ -292,47 +292,51 @@ func getLine(startLine, endLine uint, node syntax.Node) (uint, uint) {
startLine + node.Pos().Line()
}
func isFetchPipeExecute(startLine, endLine uint, node syntax.Node, cmd, pathfn string,
dl checker.DetailLogger,
) bool {
func collectFetchPipeExecute(startLine, endLine uint, node syntax.Node, cmd, pathfn string,
r *checker.PinningDependenciesData,
) {
// BinaryCmd {Op=|, X=CallExpr{Args={curl, -s, url}}, Y=CallExpr{Args={bash,}}}.
bc, ok := node.(*syntax.BinaryCmd)
if !ok {
return false
return
}
// Look for the pipe operator.
if !strings.EqualFold(bc.Op.String(), "|") {
return false
return
}
leftStmt, ok := extractCommand(bc.X.Cmd)
if !ok {
return false
return
}
rightStmt, ok := extractCommand(bc.Y.Cmd)
if !ok {
return false
return
}
if !isDownloadUtility(leftStmt) {
return false
return
}
if !isInterpreter(rightStmt) {
return false
return
}
startLine, endLine = getLine(startLine, endLine, node)
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "insecure (not pinned by hash) download detected",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeDownloadThenRun,
},
)
}
func getRedirectFile(red []*syntax.Redirect) (string, bool) {
@ -356,36 +360,36 @@ func getRedirectFile(red []*syntax.Redirect) (string, bool) {
return "", false
}
func isExecuteFiles(startLine, endLine uint, node syntax.Node, cmd, pathfn string, files map[string]bool,
dl checker.DetailLogger,
) bool {
func collectExecuteFiles(startLine, endLine uint, node syntax.Node, cmd, pathfn string, files map[string]bool,
r *checker.PinningDependenciesData,
) {
ce, ok := node.(*syntax.CallExpr)
if !ok {
return false
return
}
c, ok := extractCommand(ce)
if !ok {
return false
return
}
ok = false
startLine, endLine = getLine(startLine, endLine, node)
for fn := range files {
if isInterpreterWithFile(c, fn) || isExecuteFile(c, fn) {
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "insecure (not pinned by hash) download-then-run",
})
ok = true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeDownloadThenRun,
},
)
}
}
return ok
}
// Npm install docs are here.
@ -607,75 +611,93 @@ func isChocoUnpinnedDownload(cmd []string) bool {
return true
}
func isUnpinnedPakageManagerDownload(startLine, endLine uint, node syntax.Node,
cmd, pathfn string, dl checker.DetailLogger,
) bool {
func collectUnpinnedPakageManagerDownload(startLine, endLine uint, node syntax.Node,
cmd, pathfn string, r *checker.PinningDependenciesData,
) {
ce, ok := node.(*syntax.CallExpr)
if !ok {
return false
return
}
c, ok := extractCommand(ce)
if !ok {
return false
return
}
startLine, endLine = getLine(startLine, endLine, node)
// Go get/install.
if isGoUnpinnedDownload(c) {
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "go installation not pinned by hash",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeGoCommand,
},
)
return
}
// Pip install.
if isPipUnpinnedDownload(c) {
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "pip installation not pinned by hash",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypePipCommand,
},
)
return
}
// Npm install.
if isNpmUnpinnedDownload(c) {
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "npm installation not pinned by hash",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeNpmCommand,
},
)
return
}
// Choco install.
if isChocoUnpinnedDownload(c) {
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "choco installation not pinned by hash",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeChocoCommand,
},
)
return
}
// TODO(laurent): add other package managers.
return false
}
func recordFetchFileFromNode(node syntax.Node) (pathfn string, ok bool, err error) {
@ -701,69 +723,72 @@ func recordFetchFileFromNode(node syntax.Node) (pathfn string, ok bool, err erro
return fn, true, nil
}
func isFetchProcSubsExecute(startLine, endLine uint, node syntax.Node, cmd, pathfn string,
dl checker.DetailLogger,
) bool {
func collectFetchProcSubsExecute(startLine, endLine uint, node syntax.Node, cmd, pathfn string,
r *checker.PinningDependenciesData,
) {
ce, ok := node.(*syntax.CallExpr)
if !ok {
return false
return
}
c, ok := extractCommand(ce)
if !ok {
return false
return
}
if !isInterpreter(c) {
return false
return
}
// Now parse the process substitution part.
// Example: `bash <(wget -qO- http://website.com/my-script.sh)`.
l := 2
if len(ce.Args) < l {
return false
return
}
parts := ce.Args[1].Parts
if len(parts) != 1 {
return false
return
}
part := parts[0]
p, ok := part.(*syntax.ProcSubst)
if !ok {
return false
return
}
if !strings.EqualFold(p.Op.String(), "<(") {
return false
return
}
if len(p.Stmts) == 0 {
return false
return
}
c, ok = extractCommand(p.Stmts[0].Cmd)
if !ok {
return false
return
}
if !isDownloadUtility(c) {
return false
return
}
startLine, endLine = getLine(startLine, endLine, node)
dl.Warn(&checker.LogMessage{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
Text: "insecure (not pinned by hash) download-then-run",
})
return true
r.Dependencies = append(r.Dependencies,
checker.Dependency{
Location: &checker.File{
Path: pathfn,
Type: checker.FileTypeSource,
Offset: startLine,
EndOffset: endLine,
Snippet: cmd,
},
Type: checker.DependencyUseTypeDownloadThenRun,
},
)
}
func isCommand(cmd []string, b string) bool {
@ -840,19 +865,18 @@ func nodeToString(p *syntax.Printer, node syntax.Node) (string, error) {
}
func validateShellFileAndRecord(pathfn string, startLine, endLine uint, content []byte, files map[string]bool,
dl checker.DetailLogger,
) (bool, error) {
r *checker.PinningDependenciesData,
) error {
in := strings.NewReader(string(content))
f, err := syntax.NewParser().Parse(in, pathfn)
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
return false, sce.WithMessage(sce.ErrorShellParsing, err.Error())
return sce.WithMessage(sce.ErrorShellParsing, err.Error())
}
printer := syntax.NewPrinter()
validated := true
syntax.Walk(f, func(node syntax.Node) bool {
cmdStr, e := nodeToString(printer, node)
@ -869,9 +893,8 @@ func validateShellFileAndRecord(pathfn string, startLine, endLine uint, content
// nolinter
if ok && isShellInterpreterOrCommand([]string{i}) {
start, end := getLine(startLine, endLine, node)
ok, e := validateShellFileAndRecord(pathfn, start, end,
[]byte(c), files, dl)
validated = ok
e := validateShellFileAndRecord(pathfn, start, end,
[]byte(c), files, r)
if e != nil {
err = e
return true
@ -879,25 +902,18 @@ func validateShellFileAndRecord(pathfn string, startLine, endLine uint, content
}
// `curl | bash` (supports `sudo`).
if isFetchPipeExecute(startLine, endLine, node, cmdStr, pathfn, dl) {
validated = false
}
collectFetchPipeExecute(startLine, endLine, node, cmdStr, pathfn, r)
// Check if we're calling a file we previously downloaded.
// Includes `curl > /tmp/file [&&|;] [bash] /tmp/file`
if isExecuteFiles(startLine, endLine, node, cmdStr, pathfn, files, dl) {
validated = false
}
collectExecuteFiles(startLine, endLine, node, cmdStr, pathfn, files, r)
// `bash <(wget -qO- http://website.com/my-script.sh)`. (supports `sudo`).
if isFetchProcSubsExecute(startLine, endLine, node, cmdStr, pathfn, dl) {
validated = false
}
collectFetchProcSubsExecute(startLine, endLine, node, cmdStr, pathfn, r)
// Package manager's unpinned installs.
if isUnpinnedPakageManagerDownload(startLine, endLine, node, cmdStr, pathfn, dl) {
validated = false
}
collectUnpinnedPakageManagerDownload(startLine, endLine, node, cmdStr, pathfn, r)
// TODO(laurent): add check for cat file | bash.
// TODO(laurent): detect downloads of zip/tar files containing scripts.
// TODO(laurent): detect command being an env variable.
@ -916,7 +932,7 @@ func validateShellFileAndRecord(pathfn string, startLine, endLine uint, content
return true
})
return validated, err
return err
}
// The functions below are the only ones that should be called by other files.
@ -991,14 +1007,7 @@ func isMatchingShellScriptFile(pathfn string, content []byte, shellsToMatch []st
}
func validateShellFile(pathfn string, startLine, endLine uint,
content []byte, taintedFiles map[string]bool, dl checker.DetailLogger,
) (bool, error) {
r, err := validateShellFileAndRecord(pathfn, startLine, endLine, content, taintedFiles, dl)
if err != nil {
// Print this particular error.
dl.Debug(&checker.LogMessage{
Text: err.Error(),
})
}
return r, err
content []byte, taintedFiles map[string]bool, r *checker.PinningDependenciesData,
) error {
return validateShellFileAndRecord(pathfn, startLine, endLine, content, taintedFiles, r)
}

View File

@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package checks
package raw
import (
"os"
"testing"
scut "github.com/ossf/scorecard/v4/utests"
"github.com/ossf/scorecard/v4/checker"
)
func TestIsSupportedShellScriptFile(t *testing.T) {
@ -99,8 +99,9 @@ func TestValidateShellFile(t *testing.T) {
if err != nil {
t.Errorf("cannot read file: %v", err)
}
dl := scut.TestDetailLogger{}
_, err = validateShellFile(filename, 0, 0, content, map[string]bool{}, &dl)
var r checker.PinningDependenciesData
err = validateShellFile(filename, 0, 0, content, map[string]bool{}, &r)
if err == nil {
t.Errorf("failed to detect shell parsing error: %v", err)
}

View File

@ -72,10 +72,6 @@ func createWorkflowPermissionRemediation(filepath string) *checker.Remediation {
return createWorkflowRemediation(filepath, "permissions")
}
func createWorkflowPinningRemediation(filepath string) *checker.Remediation {
return createWorkflowRemediation(filepath, "pin")
}
func createWorkflowRemediation(path, t string) *checker.Remediation {
p := strings.TrimPrefix(path, ".github/workflows/")
if remediationBranch == "" || remediationRepo == "" {

View File

@ -50,12 +50,12 @@ var _ = Describe("E2E TEST:"+checks.CheckPinnedDependencies, func() {
}
expected := scut.TestReturn{
Error: nil,
Score: 3,
Score: 2,
NumberOfWarn: 139,
NumberOfInfo: 2,
NumberOfInfo: 1,
NumberOfDebug: 0,
}
result := checks.PinnedDependencies(&req)
result := checks.PinningDependencies(&req)
Expect(scut.ValidateTestReturn(nil, "dependencies check", &expected, &result, &dl)).Should(BeTrue())
Expect(repoClient.Close()).Should(BeNil())
})
@ -75,12 +75,12 @@ var _ = Describe("E2E TEST:"+checks.CheckPinnedDependencies, func() {
}
expected := scut.TestReturn{
Error: nil,
Score: 3,
Score: 2,
NumberOfWarn: 139,
NumberOfInfo: 2,
NumberOfInfo: 1,
NumberOfDebug: 0,
}
result := checks.PinnedDependencies(&req)
result := checks.PinningDependencies(&req)
Expect(scut.ValidateTestReturn(nil, "dependencies check", &expected, &result, &dl)).Should(BeTrue())
Expect(repoClient.Close()).Should(BeNil())
})
@ -111,12 +111,12 @@ var _ = Describe("E2E TEST:"+checks.CheckPinnedDependencies, func() {
}
expected := scut.TestReturn{
Error: nil,
Score: 3,
Score: 2,
NumberOfWarn: 139,
NumberOfInfo: 2,
NumberOfInfo: 1,
NumberOfDebug: 0,
}
result := checks.PinnedDependencies(&req)
result := checks.PinningDependencies(&req)
Expect(scut.ValidateTestReturn(nil, "dependencies check", &expected, &result, &dl)).Should(BeTrue())
Expect(x.Close()).Should(BeNil())
})

69
go.sum
View File

@ -22,7 +22,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
@ -60,7 +59,6 @@ cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSi
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.4.0/go.mod h1:NjjGEnxCS3CAKYp+vmALu20QzcqasGodQp48WxJGAYc=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw=
@ -75,7 +73,6 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/pubsub v1.9.0/go.mod h1:G3o6/kJvEMIEAN5urdkaP4be49WQsjNiykBIto9LFtY=
cloud.google.com/go/pubsub v1.19.0/go.mod h1:/O9kmSe9bb9KRnIAWkzmqhPjHo6LtzGOBYd/kr06XSs=
cloud.google.com/go/pubsub v1.21.1 h1:ghu6wlm6WouITmmuwkxGG+6vNRXDaPdAjqLcRdsw3EQ=
cloud.google.com/go/pubsub v1.21.1/go.mod h1:u3XGeMBOBCIQLcxNzy14Svz88ZFS8vI250uDgIAQDSQ=
@ -85,7 +82,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
cloud.google.com/go/storage v1.22.0 h1:NUV0NNp9nkBuW66BFRLuMgldN60C57ET3dhbwLIYio8=
cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE=
@ -97,7 +93,6 @@ contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
contrib.go.opencensus.io/exporter/stackdriver v0.13.10/go.mod h1:I5htMbyta491eUxufwwZPQdcKvvgzMB4O9ni41YnIM8=
contrib.go.opencensus.io/exporter/stackdriver v0.13.12 h1:bjBKzIf7/TAkxd7L2utGaLM78bmUWlCval5K9UeElbY=
contrib.go.opencensus.io/exporter/stackdriver v0.13.12/go.mod h1:mmxnWlrvrFdpiOHOhxBaVi1rkc0WOqhgfknj4Yg0SeQ=
@ -110,8 +105,6 @@ git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqbl
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8=
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
github.com/Azure/azure-amqp-common-go/v3 v3.0.1/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
@ -122,24 +115,18 @@ github.com/Azure/azure-sdk-for-go v19.1.1+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v37.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v42.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v49.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
github.com/Azure/azure-service-bus-go v0.10.7/go.mod h1:o5z/3lDG1iT/T/G7vgIwIqVDTx9Qa2wndf5OdzSzpF8=
github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU=
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs=
github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM=
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
github.com/Azure/go-amqp v0.13.1/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@ -154,10 +141,6 @@ github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest v0.11.7/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs=
github.com/Azure/go-autorest/autorest v0.11.9/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs=
@ -167,15 +150,11 @@ github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMl
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
github.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.6/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.17/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3/go.mod h1:4bJZhUhcq8LB20TruwHbAQsmUs2Xh+QR7utuJpLXX3A=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9/go.mod h1:hg3/1yw0Bq87O3KvvnJoAh34/0zbP7SFizX/qN5JvjU=
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
@ -192,7 +171,6 @@ github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsI
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
@ -204,7 +182,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.19.1/go.mod h1:+yYmuKqcBVkgRePGpUhTA9OEg0XsnFE96eZ6nJ2yCQM=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
@ -287,11 +264,9 @@ github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0=
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
@ -354,9 +329,6 @@ github.com/bombsimon/wsl/v2 v2.2.0/go.mod h1:Azh8c3XGEJl9LyX0/sFC+CKMc7Ssgua0g+6
github.com/bombsimon/wsl/v3 v3.0.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
github.com/bradleyfalzon/ghinstallation/v2 v2.0.4 h1:tXKVfhE7FcSkhkv0UwkLvPDeZ4kz6OXd0PKPlFqf81M=
github.com/bradleyfalzon/ghinstallation/v2 v2.0.4/go.mod h1:B40qPqJxWE0jDZgOR1JmaMy+4AY1eBP+IByOvqyAKp0=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
@ -560,7 +532,6 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@ -818,7 +789,6 @@ github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bz
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -847,9 +817,6 @@ github.com/google/go-containerregistry v0.9.0/go.mod h1:9eq4BnSufyT1kHNffX+vSXVo
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-github/v38 v38.1.0 h1:C6h1FkaITcBFK7gAmq4eFzt6gbhEhk7L5z6R3Uva+po=
github.com/google/go-github/v38 v38.1.0/go.mod h1:cStvrz/7nFr0FoENgG6GLbp53WaelXucT+BBz/3VKx4=
github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg=
@ -858,11 +825,9 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE=
github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk=
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/go-replayers/httpreplay v1.1.1 h1:H91sIMlt1NZzN7R+/ASswyouLJfW0WLW7fhyUFvDEkY=
github.com/google/go-replayers/httpreplay v1.1.1/go.mod h1:gN9GeLIs7l6NUoVaSSnv2RiqK1NiwAmD0MrKeC9IIks=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@ -885,7 +850,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@ -945,7 +909,6 @@ github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.m
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
@ -1133,7 +1096,6 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
@ -1209,7 +1171,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
github.com/moby/buildkit v0.10.3 h1:/dGykD8FW+H4p++q5+KqKEo6gAkYKyBQHdawdjVwVAU=
@ -1250,7 +1211,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
github.com/naveensrinivasan/httpcache v1.2.2/go.mod h1:gpEVVjcTYZA3F1tqYkLqbNvZuf380rhUDaV5OZpyQ88=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -1273,7 +1233,6 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
@ -1290,7 +1249,6 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
@ -1332,8 +1290,6 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/ossf/scorecard v1.2.0 h1:Gf12BN29RZDDSev0suW/DwJyhYWH1XHsIqSmpCChgsE=
github.com/ossf/scorecard v1.2.0/go.mod h1:hc0zwnXi2NHq2aru8A/NoNZ9H+DqZZlYbmOw7jjHi/Q=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -1451,7 +1407,6 @@ github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lz
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA=
github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
@ -1522,7 +1477,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
@ -1682,12 +1636,10 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI=
gocloud.dev v0.22.0/go.mod h1:z3jKIQ0Es9LALVZFQ3wOvwqAsSLq1R5c/2RdmghDucw=
gocloud.dev v0.25.0 h1:Y7vDq8xj7SyM848KXf32Krda2e6jQ4CLh/mTeCSqXtk=
gocloud.dev v0.25.0/go.mod h1:7HegHVCYZrMiU3IE1qtnzf/vRrDwLYnRNR3EhWX8x9Y=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
@ -1700,7 +1652,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -1818,7 +1769,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -1862,7 +1812,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@ -2099,7 +2048,6 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -2143,18 +2091,12 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201202200335-bef1c476418a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201203202102-a1a1cbeaa516/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
@ -2188,7 +2130,6 @@ google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNV
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@ -2202,8 +2143,6 @@ google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
@ -2239,7 +2178,6 @@ google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@ -2284,15 +2222,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@ -2369,7 +2303,6 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
@ -2410,7 +2343,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
@ -2551,7 +2483,6 @@ mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
mvdan.cc/unparam v0.0.0-20200501210554-b37ab49443f7/go.mod h1:HGC5lll35J70Y5v7vCGb9oLhHoScFwkHDJm/05RdSTc=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -40,10 +40,10 @@ type jsonScorecardRawResult struct {
// TODO: separate each check extraction into its own file.
type jsonFile struct {
Snippet *string `json:"snippet,omitempty"`
Path string `json:"path"`
// TODO: change to an uint.
Offset int `json:"offset,omitempty"`
Snippet *string `json:"snippet,omitempty"`
Path string `json:"path"`
Offset uint `json:"offset,omitempty"`
EndOffset uint `json:"endOffset,omitempty"`
}
type jsonTool struct {
@ -193,6 +193,19 @@ type jsonRun struct {
// TODO: add fields, e.g., Result=["success", "failure"]
}
type jsonPinningDependenciesData struct {
Dependencies []jsonDependency `json:"dependencies"`
}
type jsonDependency struct {
// TODO: unique dependency name.
// TODO: Job *WorkflowJob
Location *jsonFile `json:"location"`
Name *string `json:"name"`
PinnedAt *string `json:"pinnedAt"`
Type string `json:"type"`
}
//nolint
type jsonRawResults struct {
// Workflow results.
@ -220,7 +233,7 @@ type jsonRawResults struct {
// structure for it.
Contributors jsonContributors `json:"Contributors"`
// Commits.
DefaultBranchCommits []jsonDefaultBranchCommit `json:"defaultBrancCommits"`
DefaultBranchCommits []jsonDefaultBranchCommit `json:"defaultBranchCommits"`
// Archived status of the repo.
ArchivedStatus jsonArchivedStatus `json:"archived"`
// Fuzzers.
@ -229,6 +242,8 @@ type jsonRawResults struct {
Releases []jsonRelease `json:"releases"`
// Packages.
Packages []jsonPackage `json:"packages"`
// Dependency pinning.
DependencyPinning jsonPinningDependenciesData `json:"dependencyPinning"`
}
func (r *jsonScorecardRawResult) addPackagingRawResults(pk *checker.PackagingData) error {
@ -248,7 +263,7 @@ func (r *jsonScorecardRawResult) addPackagingRawResults(pk *checker.PackagingDat
jpk.File = &jsonFile{
Path: p.File.Path,
Offset: int(p.File.Offset),
Offset: p.File.Offset,
// TODO: Snippet
}
@ -265,14 +280,44 @@ func (r *jsonScorecardRawResult) addPackagingRawResults(pk *checker.PackagingDat
return nil
}
//nolint:unparam
func (r *jsonScorecardRawResult) addDependencyPinningRawResults(pd *checker.PinningDependenciesData) error {
r.Results.DependencyPinning = jsonPinningDependenciesData{}
for i := range pd.Dependencies {
rr := pd.Dependencies[i]
if rr.Location == nil {
continue
}
v := jsonDependency{
Location: &jsonFile{
Path: rr.Location.Path,
Offset: rr.Location.Offset,
EndOffset: rr.Location.EndOffset,
},
Name: rr.Name,
PinnedAt: rr.PinnedAt,
Type: string(rr.Type),
}
if rr.Location.Snippet != "" {
v.Location.Snippet = &rr.Location.Snippet
}
r.Results.DependencyPinning.Dependencies = append(r.Results.DependencyPinning.Dependencies, v)
}
return nil
}
//nolint:unparam
func (r *jsonScorecardRawResult) addDangerousWorkflowRawResults(df *checker.DangerousWorkflowData) error {
r.Results.Workflows = []jsonWorkflow{}
for _, e := range df.Workflows {
v := jsonWorkflow{
File: &jsonFile{
Path: e.File.Path,
Offset: int(e.File.Offset),
Path: e.File.Path,
Offset: e.File.Offset,
EndOffset: e.File.EndOffset,
},
Type: string(e.Type),
}
@ -616,6 +661,11 @@ func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) err
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
}
// DependencyPinning.
if err := r.addDependencyPinningRawResults(&raw.PinningDependenciesResults); err != nil {
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
}
// CII-Best-Practices.
if err := r.addOssfBestPracticesRawResults(&raw.CIIBestPracticesResults); err != nil {
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())

View File

@ -0,0 +1,96 @@
// Copyright 2022 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 remediation
import (
"errors"
"fmt"
"strings"
"sync"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/clients"
)
var (
branch string
repo string
once *sync.Once
setupErr error
)
var errInvalidArg = errors.New("invalid argument")
var (
workflowText = "update your workflow using https://app.stepsecurity.io/secureworkflow/%s/%s/%s?enable=%s"
//nolint
workflowMarkdown = "update your workflow using [https://app.stepsecurity.io](https://app.stepsecurity.io/secureworkflow/%s/%s/%s?enable=%s)"
)
//nolint:gochecknoinits
func init() {
once = new(sync.Once)
}
// Setup sets up remediation code.
func Setup(c *checker.CheckRequest) error {
once.Do(func() {
// Get the branch for remediation.
b, err := c.RepoClient.GetDefaultBranch()
if err != nil {
if !errors.Is(err, clients.ErrUnsupportedFeature) {
setupErr = err
}
return
}
if b.Name != nil {
branch = *b.Name
uri := c.Repo.URI()
parts := strings.Split(uri, "/")
if len(parts) != 3 {
setupErr = fmt.Errorf("%w: enpty: %s", errInvalidArg, uri)
return
}
repo = fmt.Sprintf("%s/%s", parts[1], parts[2])
}
})
return setupErr
}
// CreateWorkflowPermissionRemediation create remediation for workflow permissions.
func CreateWorkflowPermissionRemediation(filepath string) *checker.Remediation {
return createWorkflowRemediation(filepath, "permissions")
}
// CreateWorkflowPinningRemediation create remediaiton for pinninn GH Actions.
func CreateWorkflowPinningRemediation(filepath string) *checker.Remediation {
return createWorkflowRemediation(filepath, "pin")
}
func createWorkflowRemediation(path, t string) *checker.Remediation {
p := strings.TrimPrefix(path, ".github/workflows/")
if branch == "" || repo == "" {
return nil
}
text := fmt.Sprintf(workflowText, repo, p, branch, t)
markdown := fmt.Sprintf(workflowMarkdown, repo, p, branch, t)
return &checker.Remediation{
HelpText: text,
HelpMarkdown: markdown,
}
}

View File

@ -118,6 +118,18 @@ func ValidateTestReturn(
return true
}
// ValidatePinningDependencies tests that at least one entry returns true for isExpectedMessage.
func ValidatePinningDependencies(isExpectedDependency func(checker.Dependency) bool,
r *checker.PinningDependenciesData,
) bool {
for _, dep := range r.Dependencies {
if isExpectedDependency(dep) {
return true
}
}
return false
}
// ValidateLogMessage tests that at least one log message returns true for isExpectedMessage.
func ValidateLogMessage(isExpectedMessage func(checker.LogMessage, checker.DetailType) bool,
dl *TestDetailLogger,