diff --git a/checks/pinned_dependencies.go b/checks/pinned_dependencies.go index 02c184c22827..bb2839964548 100644 --- a/checks/pinned_dependencies.go +++ b/checks/pinned_dependencies.go @@ -16,12 +16,11 @@ package checks import ( "fmt" + "gopkg.in/yaml.v3" "regexp" "strings" "github.com/moby/buildkit/frontend/dockerfile/parser" - "gopkg.in/yaml.v2" - "github.com/ossf/scorecard/v2/checker" sce "github.com/ossf/scorecard/v2/errors" ) @@ -52,6 +51,13 @@ type gitHubActionWorkflowConfig struct { Name string `yaml:"name"` } +// Structure to host information about pinned github +// or third party dependencies +type worklowPinningResult struct { + thirdParties pinnedResult + gitHubOwned pinnedResult +} + //nolint:gochecknoinits func init() { registerCheck(CheckPinnedDependencies, PinnedDependencies) @@ -153,6 +159,16 @@ func dataAsResultPointer(data FileCbData) *pinnedResult { return pdata } +func dataAsWorkflowPointer(data FileCbData) *worklowPinningResult { + pdata, ok := data.(*worklowPinningResult) + if !ok { + // This never happens. + panic("invalid type") + } + return pdata +} + + func createReturnValues(r pinnedResult, infoMsg string, dl checker.DetailLogger, err error) (int, error) { if err != nil { return checker.InconclusiveResultScore, err @@ -479,20 +495,20 @@ func validateGitHubWorkflowIsFreeOfInsecureDownloads(pathfn string, content []by // Check pinning of github actions in workflows. func isGitHubActionsWorkflowPinned(c *checker.CheckRequest) (int, error) { - var r pinnedResult + var r worklowPinningResult err := CheckFilesContent(".github/workflows/*", true, c, validateGitHubActionWorkflow, &r) return createReturnForIsGitHubActionsWorkflowPinned(r, c.Dlogger, err) } // Create the result. -func createReturnForIsGitHubActionsWorkflowPinned(r pinnedResult, dl checker.DetailLogger, err error) (int, error) { - return createReturnValues(r, +func createReturnForIsGitHubActionsWorkflowPinned(r worklowPinningResult, dl checker.DetailLogger, err error) (int, error) { + return createReturnValues(r.gitHubOwned, "GitHub actions are pinned", dl, err) } func testIsGitHubActionsWorkflowPinned(pathfn string, content []byte, dl checker.DetailLogger) (int, error) { - var r pinnedResult + var r worklowPinningResult _, err := validateGitHubActionWorkflow(pathfn, content, dl, &r) return createReturnForIsGitHubActionsWorkflowPinned(r, dl, err) } @@ -500,10 +516,10 @@ func testIsGitHubActionsWorkflowPinned(pathfn string, content []byte, dl checker // Check file content. func validateGitHubActionWorkflow(pathfn string, content []byte, dl checker.DetailLogger, data FileCbData) (bool, error) { - pdata := dataAsResultPointer(data) + pdata := dataAsWorkflowPointer(data) if !CheckFileContainsCommands(content, "#") { - addPinnedResult(pdata, true) + addPinnedResult(&pdata.gitHubOwned, true) return true, nil } @@ -517,12 +533,27 @@ func validateGitHubActionWorkflow(pathfn string, content []byte, hashRegex := regexp.MustCompile(`^.*@[a-f\d]{40,}`) ret := true + gh := true for jobName, job := range workflow.Jobs { if len(job.Name) > 0 { jobName = job.Name } for _, step := range job.Steps { if len(step.Uses) > 0 { + // check whether we have github related action + // either action/ or github/ + ghaction := regexp.MustCompile(`(actions/)`) + ghgithub := regexp.MustCompile(`(github/)`) + + maction := ghaction.Match([]byte(step.Uses)) + mgithub := ghgithub.Match([]byte(step.Uses)) + + + if (!maction) && (!mgithub) { + fmt.Println("not matched either action/ or github/") + gh = false + } + // Ensure a hash at least as large as SHA1 is used (40 hex characters). // Example: action-name@hash match := hashRegex.Match([]byte(step.Uses)) @@ -534,7 +565,7 @@ func validateGitHubActionWorkflow(pathfn string, content []byte, } } - addPinnedResult(pdata, ret) + addPinnedResult(&pdata.gitHubOwned, (ret && gh)) return true, nil }