Add raw results for Token-Permissions (#1912)

* draft

* update

* update

* draft

* updates

* update

* update

* update

* update

* update

* update

* update

* update

* e2e test for empty repo

* update

* rename structure

* update
This commit is contained in:
laurentsimon 2022-07-15 14:48:50 -07:00 committed by GitHub
parent 2b8c7b4b88
commit 838f62f65a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1282 additions and 941 deletions

View File

@ -20,7 +20,7 @@ import (
// RawResults contains results before a policy
// is applied.
//nolint
// nolint
type RawResults struct {
PackagingResults PackagingData
CIIBestPracticesResults CIIBestPracticesData
@ -38,6 +38,7 @@ type RawResults struct {
SignedReleasesResults SignedReleasesData
FuzzingResults FuzzingData
LicenseResults LicenseData
TokenPermissionsResults TokenPermissionsData
}
// FuzzingData represents different fuzzing done.
@ -243,3 +244,46 @@ type WorkflowJob struct {
Name *string
ID *string
}
// TokenPermissionsData represents data about a permission failure.
type TokenPermissionsData struct {
TokenPermissions []TokenPermission
}
// PermissionLocation represents a declaration type.
type PermissionLocation string
const (
// PermissionLocationTop is top-level workflow permission.
PermissionLocationTop PermissionLocation = "topLevel"
// PermissionLocationJob is job-level workflow permission.
PermissionLocationJob PermissionLocation = "jobLevel"
)
// PermissionLevel represents a permission type.
type PermissionLevel string
const (
// PermissionLevelUndeclared is an undecleared permission.
PermissionLevelUndeclared PermissionLevel = "undeclared"
// PermissionLevelWrite is a permission set to `write` for a permission we consider potentially dangerous.
PermissionLevelWrite PermissionLevel = "write"
// PermissionLevelRead is a permission set to `read`.
PermissionLevelRead PermissionLevel = "read"
// PermissionLevelNone is a permission set to `none`.
PermissionLevelNone PermissionLevel = "none"
// PermissionLevelUnknown is for other kinds of alerts, mostly to support debug messages.
// TODO: remove it once we have implemented severity (#1874).
PermissionLevelUnknown PermissionLevel = "unknown"
)
// TokenPermission defines a token permission result.
type TokenPermission struct {
Job *WorkflowJob
LocationType *PermissionLocation
Name *string
Value *string
File *File
Msg *string
Type PermissionLevel
}

View File

@ -57,6 +57,7 @@ func TestReleaseAndDevBranchProtected(t *testing.T) {
rel1 := "release/v.1"
sha := "8fb3cb86082b17144a80402f5367ae65f06083bd"
//nolint:goconst
main := "main"
trueVal := true
falseVal := false

View File

@ -19,10 +19,6 @@ import (
)
var (
errInvalidGitHubWorkflow = errors.New("invalid GitHub workflow")
errInternalNameCannotBeEmpty = errors.New("name cannot be empty")
errInternalCheckFuncCannotBeNil = errors.New("checkFunc cannot be nil")
// TODO(#1245): these should be moved under `raw` package after migration.
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
)

View File

@ -0,0 +1,286 @@
// 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 (
"fmt"
"github.com/ossf/scorecard/v4/checker"
sce "github.com/ossf/scorecard/v4/errors"
"github.com/ossf/scorecard/v4/remediation"
)
type permissions struct {
topLevelWritePermissions map[string]bool
jobLevelWritePermissions map[string]bool
}
// TokenPermissions applies the score policy for the Token-Permissions check.
func TokenPermissions(name string, dl checker.DetailLogger, r *checker.TokenPermissionsData) checker.CheckResult {
if r == nil {
e := sce.WithMessage(sce.ErrScorecardInternal, "empty raw data")
return checker.CreateRuntimeErrorResult(name, e)
}
score, err := applyScorePolicy(r, dl)
if err != nil {
return checker.CreateRuntimeErrorResult(name, err)
}
if score != checker.MaxResultScore {
return checker.CreateResultWithScore(name,
"non read-only tokens detected in GitHub workflows", score)
}
return checker.CreateMaxScoreResult(name,
"tokens are read-only in GitHub workflows")
}
func applyScorePolicy(results *checker.TokenPermissionsData, dl checker.DetailLogger) (int, error) {
// See list https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/.
// Note: there are legitimate reasons to use some of the permissions like checks, deployments, etc.
// in CI/CD systems https://docs.travis-ci.com/user/github-oauth-scopes/.
hm := make(map[string]permissions)
for _, r := range results.TokenPermissions {
var msg checker.LogMessage
if r.File != nil {
msg.Path = r.File.Path
msg.Offset = r.File.Offset
msg.Type = r.File.Type
msg.Snippet = r.File.Snippet
if msg.Path != "" {
msg.Remediation = remediation.CreateWorkflowPermissionRemediation(r.File.Path)
}
}
text, err := createMessage(r)
if err != nil {
return checker.MinResultScore, err
}
msg.Text = text
switch r.Type {
case checker.PermissionLevelNone, checker.PermissionLevelRead:
dl.Info(&msg)
case checker.PermissionLevelUnknown:
dl.Debug(&msg)
case checker.PermissionLevelUndeclared:
if r.LocationType == nil {
return checker.InconclusiveResultScore,
sce.WithMessage(sce.ErrScorecardInternal, "locationType is nil")
}
// We warn only for top-level.
if *r.LocationType == checker.PermissionLocationTop {
dl.Warn(&msg)
} else {
dl.Debug(&msg)
}
// Group results by workflow name for score computation.
if err := updateWorkflowHashMap(hm, r); err != nil {
return checker.InconclusiveResultScore, err
}
case checker.PermissionLevelWrite:
dl.Warn(&msg)
// Group results by workflow name for score computation.
if err := updateWorkflowHashMap(hm, r); err != nil {
return checker.InconclusiveResultScore, err
}
}
}
return calculateScore(hm), nil
}
func recordPermissionWrite(hm map[string]permissions, path string,
locType checker.PermissionLocation, permName *string,
) {
if _, exists := hm[path]; !exists {
hm[path] = permissions{
topLevelWritePermissions: make(map[string]bool),
jobLevelWritePermissions: make(map[string]bool),
}
}
// Select the hash map to update.
m := hm[path].jobLevelWritePermissions
if locType == checker.PermissionLocationTop {
m = hm[path].topLevelWritePermissions
}
// Set the permission name to record.
name := "all"
if permName != nil && *permName != "" {
name = *permName
}
m[name] = true
}
func updateWorkflowHashMap(hm map[string]permissions, t checker.TokenPermission) error {
if t.LocationType == nil {
return sce.WithMessage(sce.ErrScorecardInternal, "locationType is nil")
}
if t.File == nil || t.File.Path == "" {
return sce.WithMessage(sce.ErrScorecardInternal, "path is not set")
}
if t.Type != checker.PermissionLevelWrite &&
t.Type != checker.PermissionLevelUndeclared {
return nil
}
recordPermissionWrite(hm, t.File.Path, *t.LocationType, t.Name)
return nil
}
func createMessage(t checker.TokenPermission) (string, error) {
// By default, use the message already present.
if t.Msg != nil {
return *t.Msg, nil
}
// Ensure there's no implementation bug.
if t.LocationType == nil {
return "", sce.WithMessage(sce.ErrScorecardInternal, "locationType is nil")
}
// Use a different message depending on the type.
if t.Type == checker.PermissionLevelUndeclared {
return fmt.Sprintf("no %s permission defined", *t.LocationType), nil
}
if t.Value == nil {
return "", sce.WithMessage(sce.ErrScorecardInternal, "Value fields is nil")
}
if t.Name == nil {
return fmt.Sprintf("%s permissions set to '%v'", *t.LocationType,
*t.Value), nil
}
return fmt.Sprintf("%s '%v' permission set to '%v'", *t.LocationType,
*t.Name, *t.Value), nil
}
// Calculate the score.
func calculateScore(result map[string]permissions) int {
// See list https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/.
// Note: there are legitimate reasons to use some of the permissions like checks, deployments, etc.
// in CI/CD systems https://docs.travis-ci.com/user/github-oauth-scopes/.
// Start with a perfect score.
score := float32(checker.MaxResultScore)
// Retrieve the overall results.
for _, perms := range result {
// If no top level permissions are defined, all the permissions
// are enabled by default. In this case,
if permissionIsPresentInTopLevel(perms, "all") {
if permissionIsPresentInRunLevel(perms, "all") {
// ... give lowest score if no run level permissions are defined either.
return checker.MinResultScore
}
// ... reduce score if run level permissions are defined.
score -= 0.5
}
// status: https://docs.github.com/en/rest/reference/repos#statuses.
// May allow an attacker to change the result of pre-submit and get a PR merged.
// Low risk: -0.5.
if permissionIsPresent(perms, "statuses") {
score -= 0.5
}
// checks.
// May allow an attacker to edit checks to remove pre-submit and introduce a bug.
// Low risk: -0.5.
if permissionIsPresent(perms, "checks") {
score -= 0.5
}
// secEvents.
// May allow attacker to read vuln reports before patch available.
// Low risk: -1
if permissionIsPresent(perms, "security-events") {
score--
}
// deployments: https://docs.github.com/en/rest/reference/repos#deployments.
// May allow attacker to charge repo owner by triggering VM runs,
// and tiny chance an attacker can trigger a remote
// service with code they own if server accepts code/location var unsanitized.
// Low risk: -1
if permissionIsPresent(perms, "deployments") {
score--
}
// contents.
// Allows attacker to commit unreviewed code.
// High risk: -10
if permissionIsPresent(perms, "contents") {
score -= checker.MaxResultScore
}
// packages: https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages.
// Allows attacker to publish packages.
// High risk: -10
if permissionIsPresent(perms, "packages") {
score -= checker.MaxResultScore
}
// actions.
// May allow an attacker to steal GitHub secrets by approving to run an action that needs approval.
// High risk: -10
if permissionIsPresent(perms, "actions") {
score -= checker.MaxResultScore
}
if score < checker.MinResultScore {
break
}
}
// We're done, calculate the final score.
if score < checker.MinResultScore {
return checker.MinResultScore
}
return int(score)
}
func permissionIsPresent(perms permissions, name string) bool {
return permissionIsPresentInTopLevel(perms, name) ||
permissionIsPresentInRunLevel(perms, name)
}
func permissionIsPresentInTopLevel(perms permissions, name string) bool {
_, ok := perms.topLevelWritePermissions[name]
return ok
}
func permissionIsPresentInRunLevel(perms permissions, name string) bool {
_, ok := perms.jobLevelWritePermissions[name]
return ok
}

View File

@ -336,9 +336,8 @@ type JobMatchResult struct {
File checker.File
}
// RawAnyJobsMatch returns true if any of the jobs have a match in the given workflow.
// TODO: Rename after migraiton is complete.
func RawAnyJobsMatch(workflow *actionlint.Workflow, jobMatchers []JobMatcher, fp string,
// AnyJobsMatch returns true if any of the jobs have a match in the given workflow.
func AnyJobsMatch(workflow *actionlint.Workflow, jobMatchers []JobMatcher, fp string,
logMsgNoMatch string,
) (JobMatchResult, bool) {
for _, job := range workflow.Jobs {
@ -368,39 +367,19 @@ func RawAnyJobsMatch(workflow *actionlint.Workflow, jobMatchers []JobMatcher, fp
}, false
}
// AnyJobsMatch returns true if any of the jobs have a match in the given workflow.
func AnyJobsMatch(workflow *actionlint.Workflow, jobMatchers []JobMatcher, fp string, dl checker.DetailLogger,
logMsgNoMatch string,
) bool {
for _, job := range workflow.Jobs {
for _, matcher := range jobMatchers {
if !matcher.matches(job) {
continue
}
dl.Info(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: GetLineNumber(job.Pos),
Text: matcher.LogText,
})
return true
}
}
dl.Debug(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: logMsgNoMatch,
})
return false
}
// matches returns true if the job matches the job matcher.
func (m *JobMatcher) matches(job *actionlint.Job) bool {
for _, stepToMatch := range m.Steps {
hasMatch := false
// First look for re-usable workflow calls.
if job.WorkflowCall != nil &&
job.WorkflowCall.Uses != nil &&
strings.HasPrefix(job.WorkflowCall.Uses.Value, stepToMatch.Uses+"@") {
return true
}
// Second looks for steps in the job.
for _, step := range job.Steps {
if stepsMatch(stepToMatch, step) {
hasMatch = true
@ -455,3 +434,136 @@ func stepsMatch(stepToMatch *JobMatcherStep, step *actionlint.Step) bool {
return true
}
// IsPackagingWorkflow checks for a packaging workflow.
func IsPackagingWorkflow(workflow *actionlint.Workflow, fp string) (JobMatchResult, bool) {
jobMatchers := []JobMatcher{
{
Steps: []*JobMatcherStep{
{
Uses: "actions/setup-node",
With: map[string]string{"registry-url": "https://registry.npmjs.org"},
},
{
Run: "npm.*publish",
},
},
LogText: "candidate node publishing workflow using npm",
},
{
// Java packages with maven.
Steps: []*JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "mvn.*deploy",
},
},
LogText: "candidate java publishing workflow using maven",
},
{
// Java packages with gradle.
Steps: []*JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "gradle.*publish",
},
},
LogText: "candidate java publishing workflow using gradle",
},
{
// Ruby packages.
Steps: []*JobMatcherStep{
{
Run: "gem.*push",
},
},
LogText: "candidate ruby publishing workflow using gem",
},
{
// NuGet packages.
Steps: []*JobMatcherStep{
{
Run: "nuget.*push",
},
},
LogText: "candidate nuget publishing workflow",
},
{
// Docker packages.
Steps: []*JobMatcherStep{
{
Run: "docker.*push",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Docker packages.
Steps: []*JobMatcherStep{
{
Uses: "docker/build-push-action",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Python packages.
Steps: []*JobMatcherStep{
{
Uses: "actions/setup-python",
},
{
Uses: "pypa/gh-action-pypi-publish",
},
},
LogText: "candidate python publishing workflow using pypi",
},
{
// Python packages.
// This is a custom Python packaging workflow based on semantic versioning.
// TODO(#1642): accept custom workflows through a separate configuration.
Steps: []*JobMatcherStep{
{
Uses: "relekang/python-semantic-release",
},
},
LogText: "candidate python publishing workflow using python-semantic-release",
},
{
// Go packages.
Steps: []*JobMatcherStep{
{
Uses: "actions/setup-go",
},
{
Uses: "goreleaser/goreleaser-action",
},
},
LogText: "candidate golang publishing workflow",
},
{
// Rust packages. https://doc.rust-lang.org/cargo/reference/publishing.html
Steps: []*JobMatcherStep{
{
Run: "cargo.*publish",
},
},
LogText: "candidate rust publishing workflow using cargo",
},
{
// Ko container action. https://github.com/google/ko
Steps: []*JobMatcherStep{
{
Uses: "imjasonh/setup-ko",
},
},
LogText: "candidate container publishing workflow using ko",
},
}
return AnyJobsMatch(workflow, jobMatchers, fp, "not a publishing workflow")
}

View File

@ -15,42 +15,15 @@
package checks
import (
"fmt"
"strings"
"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"
)
// CheckTokenPermissions is the exported name for Token-Permissions check.
const (
CheckTokenPermissions = "Token-Permissions"
jobLevelPermission = "job level"
topLevelPermission = "top level"
)
type permission string
const (
permissionAll = permission("all")
permissionStatuses = permission("statuses")
permissionChecks = permission("checks")
permissionSecurityEvents = permission("security-events")
permissionDeployments = permission("deployments")
permissionContents = permission("contents")
permissionPackages = permission("packages")
permissionActions = permission("actions")
)
var permissionsOfInterest = []permission{
permissionStatuses, permissionChecks,
permissionSecurityEvents, permissionDeployments,
permissionContents, permissionPackages, permissionActions,
}
const CheckTokenPermissions = "Token-Permissions"
//nolint:gochecknoinits
func init() {
@ -64,678 +37,24 @@ func init() {
}
}
// Holds stateful data to pass thru callbacks.
// Each field correpsonds to a GitHub permission type, and
// will hold true if declared non-write, false otherwise.
type permissions struct {
topLevelWritePermissions map[permission]bool
jobLevelWritePermissions map[permission]bool
}
type permissionCbData struct {
// map of filename to write permissions used.
workflows map[string]permissions
}
// TokenPermissions runs Token-Permissions check.
// TokenPermissions will run the Token-Permissions check.
func TokenPermissions(c *checker.CheckRequest) checker.CheckResult {
// data is shared across all GitHub workflows.
data := permissionCbData{
workflows: make(map[string]permissions),
}
if err := remediation.Setup(c); err != nil {
createResultForLeastPrivilegeTokens(data, err)
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckTokenPermissions, e)
}
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: false,
}, validateGitHubActionTokenPermissions, c.Dlogger, &data)
return createResultForLeastPrivilegeTokens(data, err)
}
// Check file content.
var validateGitHubActionTokenPermissions fileparser.DoWhileTrueOnFileContent = func(path string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(path) {
return true, nil
}
// Verify the type of the data.
if len(args) != 2 {
return false, fmt.Errorf(
"validateGitHubActionTokenPermissions requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata, ok := args[1].(*permissionCbData)
if !ok {
return false, fmt.Errorf(
"validateGitHubActionTokenPermissions requires arg[0] of type *permissionCbData: %w", errInvalidArgType)
}
dl, ok := args[0].(checker.DetailLogger)
if !ok {
return false, fmt.Errorf(
"validateGitHubActionTokenPermissions requires arg[1] of type checker.DetailLogger: %w", errInvalidArgType)
}
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
return false, fileparser.FormatActionlintError(errs)
}
// 1. Top-level permission definitions.
//nolint
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#example-1-passing-the-github_token-as-an-input,
// https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/,
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#modifying-the-permissions-for-the-github_token.
if err := validateTopLevelPermissions(workflow, path, dl, pdata); err != nil {
return false, err
}
// 2. Run-level permission definitions,
// see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idpermissions.
ignoredPermissions := createIgnoredPermissions(workflow, path, dl)
if err := validatejobLevelPermissions(workflow, path, dl, pdata, ignoredPermissions); err != nil {
return false, err
}
// TODO(laurent): 2. Identify github actions that require write and add checks.
// TODO(laurent): 3. Read a few runs and ensures they have the same permissions.
return true, nil
}
func validatePermission(permissionKey permission, permissionValue *actionlint.PermissionScope,
permLevel, path string, dl checker.DetailLogger, pPermissions map[permission]bool,
ignoredPermissions map[permission]bool,
) error {
if permissionValue.Value == nil {
return sce.WithMessage(sce.ErrScorecardInternal, errInvalidGitHubWorkflow.Error())
}
val := permissionValue.Value.Value
lineNumber := fileparser.GetLineNumber(permissionValue.Value.Pos)
if strings.EqualFold(val, "write") {
if isPermissionOfInterest(permissionKey, ignoredPermissions) {
dl.Warn(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Text: fmt.Sprintf("%s '%v' permission set to '%v'", permLevel, permissionKey, val),
Snippet: val,
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
recordPermissionWrite(pPermissions, permissionKey)
} else {
// Only log for debugging, otherwise
// it may confuse users.
dl.Debug(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Text: fmt.Sprintf("%s '%v' permission set to '%v'", permLevel, permissionKey, val),
Snippet: val,
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
}
return nil
}
dl.Info(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Text: fmt.Sprintf("%s '%v' permission set to '%v'", permLevel, permissionKey, val),
// TODO: set Snippet.
})
return nil
}
func validateMapPermissions(scopes map[string]*actionlint.PermissionScope, permLevel, path string,
dl checker.DetailLogger, pPermissions map[permission]bool,
ignoredPermissions map[permission]bool,
) error {
for key, v := range scopes {
if err := validatePermission(permission(key), v, permLevel, path, dl, pPermissions, ignoredPermissions); err != nil {
return err
}
}
return nil
}
func recordPermissionWrite(pPermissions map[permission]bool, perm permission) {
pPermissions[perm] = true
}
func getWritePermissionsMap(p *permissionCbData, path, permLevel string) map[permission]bool {
if _, exists := p.workflows[path]; !exists {
p.workflows[path] = permissions{
topLevelWritePermissions: make(map[permission]bool),
jobLevelWritePermissions: make(map[permission]bool),
}
}
if permLevel == jobLevelPermission {
return p.workflows[path].jobLevelWritePermissions
}
return p.workflows[path].topLevelWritePermissions
}
func recordAllPermissionsWrite(p *permissionCbData, permLevel, path string) {
// Special case: `all` does not correspond
// to a GitHub permission.
m := getWritePermissionsMap(p, path, permLevel)
m[permissionAll] = true
}
func validatePermissions(permissions *actionlint.Permissions, permLevel, path string,
dl checker.DetailLogger, pdata *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
allIsSet := permissions != nil && permissions.All != nil && permissions.All.Value != ""
scopeIsSet := permissions != nil && len(permissions.Scopes) > 0
if permissions == nil || (!allIsSet && !scopeIsSet) {
dl.Info(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("%s permissions set to 'none'", permLevel),
})
}
if allIsSet {
val := permissions.All.Value
lineNumber := fileparser.GetLineNumber(permissions.All.Pos)
if !strings.EqualFold(val, "read-all") && val != "" {
dl.Warn(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Text: fmt.Sprintf("%s permissions set to '%v'", permLevel, val),
Snippet: val,
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
recordAllPermissionsWrite(pdata, permLevel, path)
return nil
}
dl.Info(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Text: fmt.Sprintf("%s permissions set to '%v'", permLevel, val),
Snippet: val,
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
} else /* scopeIsSet == true */ if err := validateMapPermissions(permissions.Scopes,
permLevel, path, dl, getWritePermissionsMap(pdata, path, permLevel), ignoredPermissions); err != nil {
return err
}
return nil
}
func validateTopLevelPermissions(workflow *actionlint.Workflow, path string,
dl checker.DetailLogger, pdata *permissionCbData,
) error {
// Check if permissions are set explicitly.
if workflow.Permissions == nil {
dl.Warn(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: fmt.Sprintf("no %s permission defined", topLevelPermission),
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
recordAllPermissionsWrite(pdata, topLevelPermission, path)
return nil
}
return validatePermissions(workflow.Permissions, topLevelPermission, path, dl,
pdata, map[permission]bool{})
}
func validatejobLevelPermissions(workflow *actionlint.Workflow, path string,
dl checker.DetailLogger, pdata *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
for _, job := range workflow.Jobs {
// Run-level permissions may be left undefined.
// For most workflows, no write permissions are needed,
// so only top-level read-only permissions need to be declared.
if job.Permissions == nil {
dl.Debug(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(job.Pos),
Text: fmt.Sprintf("no %s permission defined", jobLevelPermission),
Remediation: remediation.CreateWorkflowPermissionRemediation(path),
})
recordAllPermissionsWrite(pdata, jobLevelPermission, path)
continue
}
err := validatePermissions(job.Permissions, jobLevelPermission,
path, dl, pdata, ignoredPermissions)
if err != nil {
return err
}
}
return nil
}
func isPermissionOfInterest(name permission, ignoredPermissions map[permission]bool) bool {
for _, p := range permissionsOfInterest {
_, present := ignoredPermissions[p]
if strings.EqualFold(string(name), string(p)) && !present {
return true
}
}
return false
}
func permissionIsPresent(perms permissions, name permission) bool {
return permissionIsPresentInTopLevel(perms, name) ||
permissionIsPresentInRunLevel(perms, name)
}
func permissionIsPresentInTopLevel(perms permissions, name permission) bool {
_, ok := perms.topLevelWritePermissions[name]
return ok
}
func permissionIsPresentInRunLevel(perms permissions, name permission) bool {
_, ok := perms.jobLevelWritePermissions[name]
return ok
}
// Calculate the score.
func calculateScore(result permissionCbData) int {
// See list https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/.
// Note: there are legitimate reasons to use some of the permissions like checks, deployments, etc.
// in CI/CD systems https://docs.travis-ci.com/user/github-oauth-scopes/.
// Start with a perfect score.
score := float32(checker.MaxResultScore)
// Retrieve the overall results.
for _, perms := range result.workflows {
// If no top level permissions are defined, all the permissions
// are enabled by default, hence permissionAll. In this case,
if permissionIsPresentInTopLevel(perms, permissionAll) {
if permissionIsPresentInRunLevel(perms, permissionAll) {
// ... give lowest score if no run level permissions are defined either.
return checker.MinResultScore
}
// ... reduce score if run level permissions are defined.
score -= 0.5
}
// status: https://docs.github.com/en/rest/reference/repos#statuses.
// May allow an attacker to change the result of pre-submit and get a PR merged.
// Low risk: -0.5.
if permissionIsPresent(perms, permissionStatuses) {
score -= 0.5
}
// checks.
// May allow an attacker to edit checks to remove pre-submit and introduce a bug.
// Low risk: -0.5.
if permissionIsPresent(perms, permissionChecks) {
score -= 0.5
}
// secEvents.
// May allow attacker to read vuln reports before patch available.
// Low risk: -1
if permissionIsPresent(perms, permissionSecurityEvents) {
score--
}
// deployments: https://docs.github.com/en/rest/reference/repos#deployments.
// May allow attacker to charge repo owner by triggering VM runs,
// and tiny chance an attacker can trigger a remote
// service with code they own if server accepts code/location var unsanitized.
// Low risk: -1
if permissionIsPresent(perms, permissionDeployments) {
score--
}
// contents.
// Allows attacker to commit unreviewed code.
// High risk: -10
if permissionIsPresent(perms, permissionContents) {
score -= checker.MaxResultScore
}
// packages: https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages.
// Allows attacker to publish packages.
// High risk: -10
if permissionIsPresent(perms, permissionPackages) {
score -= checker.MaxResultScore
}
// actions.
// May allow an attacker to steal GitHub secrets by approving to run an action that needs approval.
// High risk: -10
if permissionIsPresent(perms, permissionActions) {
score -= checker.MaxResultScore
}
if score < checker.MinResultScore {
break
}
}
// We're done, calculate the final score.
if score < checker.MinResultScore {
return checker.MinResultScore
}
return int(score)
}
// Create the result.
func createResultForLeastPrivilegeTokens(result permissionCbData, err error) checker.CheckResult {
rawData, err := raw.TokenPermissions(c)
if err != nil {
return checker.CreateRuntimeErrorResult(CheckTokenPermissions, err)
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckTokenPermissions, e)
}
score := calculateScore(result)
if score != checker.MaxResultScore {
return checker.CreateResultWithScore(CheckTokenPermissions,
"non read-only tokens detected in GitHub workflows", score)
// Return raw results.
if c.RawResults != nil {
c.RawResults.TokenPermissionsResults = rawData
}
return checker.CreateMaxScoreResult(CheckTokenPermissions,
"tokens are read-only in GitHub workflows")
}
func createIgnoredPermissions(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) map[permission]bool {
ignoredPermissions := make(map[permission]bool)
if requiresPackagesPermissions(workflow, fp, dl) {
ignoredPermissions[permissionPackages] = true
}
if requiresContentsPermissions(workflow, fp, dl) {
ignoredPermissions[permissionContents] = true
}
if isSARIFUploadWorkflow(workflow, fp, dl) {
ignoredPermissions[permissionSecurityEvents] = true
}
return ignoredPermissions
}
// Scanning tool run externally and SARIF file uploaded.
func isSARIFUploadWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
//nolint
// CodeQl analysis workflow automatically sends sarif file to GitHub.
// https://docs.github.com/en/code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github#about-sarif-file-uploads-for-code-scanning.
// `The CodeQL action uploads the SARIF file automatically when it completes analysis`.
if isCodeQlAnalysisWorkflow(workflow, fp, dl) {
return true
}
//nolint
// Third-party scanning tools use the SARIF-upload action from code-ql.
// https://docs.github.com/en/code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github#uploading-a-code-scanning-analysis-with-github-actions
// We only support CodeQl today.
if isSARIFUploadAction(workflow, fp, dl) {
return true
}
// TODO: some third party tools may upload directly thru their actions.
// Very unlikely.
// See https://github.com/marketplace for tools.
return false
}
// CodeQl run externally and SARIF file uploaded.
func isSARIFUploadAction(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
for _, job := range workflow.Jobs {
for _, step := range job.Steps {
uses := fileparser.GetUses(step)
if uses == nil {
continue
}
if strings.HasPrefix(uses.Value, "github/codeql-action/upload-sarif@") {
dl.Debug(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(uses.Pos),
Text: "codeql SARIF upload workflow detected",
// TODO: set Snippet.
})
return true
}
}
}
dl.Debug(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: "not a codeql upload SARIF workflow",
})
return false
}
//nolint
// CodeQl run within GitHub worklow automatically bubbled up to
// security events, see
// https://docs.github.com/en/code-security/secure-coding/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning.
func isCodeQlAnalysisWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
for _, job := range workflow.Jobs {
for _, step := range job.Steps {
uses := fileparser.GetUses(step)
if uses == nil {
continue
}
if strings.HasPrefix(uses.Value, "github/codeql-action/analyze@") {
dl.Debug(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(uses.Pos),
Text: "codeql workflow detected",
// TODO: set Snippet.
})
return true
}
}
}
dl.Debug(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
Text: "not a codeql workflow",
})
return false
}
// A packaging workflow using GitHub's supported packages:
// https://docs.github.com/en/packages.
func requiresPackagesPermissions(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
// TODO: add support for GitHub registries.
// Example: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry.
// This feature requires parsing actions properly.
// For now, we just re-use the Packaging check to verify that the
// workflow is a packaging workflow.
return isPackagingWorkflow(workflow, fp, dl)
}
// requiresContentsPermissions returns true if the workflow requires the `contents: write` permission.
func requiresContentsPermissions(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
return isReleasingWorkflow(workflow, fp, dl) || isGitHubPagesDeploymentWorkflow(workflow, fp, dl)
}
// isGitHubPagesDeploymentWorkflow returns true if the workflow involves pushing static pages to GitHub pages.
func isGitHubPagesDeploymentWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
jobMatchers := []fileparser.JobMatcher{
{
Steps: []*fileparser.JobMatcherStep{
{
Uses: "peaceiris/actions-gh-pages",
},
},
LogText: "candidate GitHub page deployment workflow using peaceiris/actions-gh-pages",
},
}
return fileparser.AnyJobsMatch(workflow, jobMatchers, fp, dl, "not a GitHub Pages deployment workflow")
}
// isReleasingWorkflow returns true if the workflow involves creating a release on GitHub.
func isReleasingWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
jobMatchers := []fileparser.JobMatcher{
{
// Python packages.
// This is a custom Python packaging/releasing workflow based on semantic versioning.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "relekang/python-semantic-release",
},
},
LogText: "candidate python publishing workflow using python-semantic-release",
},
{
// Go packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-go",
},
{
Uses: "goreleaser/goreleaser-action",
},
},
LogText: "candidate golang publishing workflow",
},
}
return fileparser.AnyJobsMatch(workflow, jobMatchers, fp, dl, "not a releasing workflow")
}
// TODO: remove when migrated to raw results.
// Should be using the definition in raw/packaging.go.
func isPackagingWorkflow(workflow *actionlint.Workflow, fp string, dl checker.DetailLogger) bool {
jobMatchers := []fileparser.JobMatcher{
{
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-node",
With: map[string]string{"registry-url": "https://registry.npmjs.org"},
},
{
Run: "npm.*publish",
},
},
LogText: "candidate node publishing workflow using npm",
},
{
// Java packages with maven.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "mvn.*deploy",
},
},
LogText: "candidate java publishing workflow using maven",
},
{
// Java packages with gradle.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "gradle.*publish",
},
},
LogText: "candidate java publishing workflow using gradle",
},
{
// Ruby packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "gem.*push",
},
},
LogText: "candidate ruby publishing workflow using gem",
},
{
// NuGet packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "nuget.*push",
},
},
LogText: "candidate nuget publishing workflow",
},
{
// Docker packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "docker.*push",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Docker packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "docker/build-push-action",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Python packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-python",
},
{
Uses: "pypa/gh-action-pypi-publish",
},
},
LogText: "candidate python publishing workflow using pypi",
},
{
// Python packages.
// This is a custom Python packaging workflow based on semantic versioning.
// TODO(#1642): accept custom workflows through a separate configuration.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "relekang/python-semantic-release",
},
},
LogText: "candidate python publishing workflow using python-semantic-release",
},
{
// Go packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-go",
},
{
Uses: "goreleaser/goreleaser-action",
},
},
LogText: "candidate golang publishing workflow",
},
{
// Rust packages. https://doc.rust-lang.org/cargo/reference/publishing.html
Steps: []*fileparser.JobMatcherStep{
{
Run: "cargo.*publish",
},
},
LogText: "candidate rust publishing workflow using cargo",
},
}
return fileparser.AnyJobsMatch(workflow, jobMatchers, fp, dl, "not a publishing workflow")
// Return the score evaluation.
return evaluation.TokenPermissions(CheckTokenPermissions, c.Dlogger, &rawData)
}

View File

@ -20,33 +20,15 @@ import (
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/clients"
mockrepo "github.com/ossf/scorecard/v4/clients/mockclients"
scut "github.com/ossf/scorecard/v4/utests"
)
type file struct {
pathfn string
content []byte
}
func testValidateGitHubActionTokenPermissions(files []file,
dl checker.DetailLogger,
) checker.CheckResult {
data := permissionCbData{
workflows: make(map[string]permissions),
}
var err error
for _, f := range files {
_, err = validateGitHubActionTokenPermissions(f.pathfn, f.content, dl, &data)
if err != nil {
break
}
}
return createResultForLeastPrivilegeTokens(data, err)
}
//nolint
// nolint
func TestGithubTokenPermissions(t *testing.T) {
t.Parallel()
@ -95,8 +77,8 @@ func TestGithubTokenPermissions(t *testing.T) {
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 2,
NumberOfDebug: 5,
NumberOfInfo: 1,
NumberOfDebug: 6,
},
},
{
@ -271,8 +253,8 @@ func TestGithubTokenPermissions(t *testing.T) {
Error: nil,
Score: checker.MinResultScore,
NumberOfWarn: 1,
NumberOfInfo: 2,
NumberOfDebug: 4,
NumberOfInfo: 1,
NumberOfDebug: 5,
},
},
{
@ -282,8 +264,8 @@ func TestGithubTokenPermissions(t *testing.T) {
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 3,
NumberOfDebug: 3,
NumberOfInfo: 1,
NumberOfDebug: 5,
},
},
{
@ -293,8 +275,8 @@ func TestGithubTokenPermissions(t *testing.T) {
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 2,
NumberOfDebug: 5,
NumberOfInfo: 1,
NumberOfDebug: 6,
},
},
{
@ -370,8 +352,8 @@ func TestGithubTokenPermissions(t *testing.T) {
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 2,
NumberOfDebug: 5,
NumberOfInfo: 1,
NumberOfDebug: 6,
},
},
}
@ -379,22 +361,37 @@ func TestGithubTokenPermissions(t *testing.T) {
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()
var files []file
var content []byte
var err error
for _, fn := range tt.filenames {
content, err = os.ReadFile(fn)
if err != nil {
panic(fmt.Errorf("cannot read file: %w", err))
}
files = append(files, file{pathfn: strings.Replace(fn, "./testdata/", "", 1), content: content})
ctrl := gomock.NewController(t)
mockRepo := mockrepo.NewMockRepoClient(ctrl)
main := "main"
mockRepo.EXPECT().URI().Return("github.com/ossf/scorecard").AnyTimes()
mockRepo.EXPECT().GetDefaultBranch().Return(&clients.BranchRef{Name: &main}, nil).AnyTimes()
mockRepo.EXPECT().ListFiles(gomock.Any()).DoAndReturn(func(predicate func(string) (bool, error)) ([]string, error) {
files := []string{}
for _, fn := range tt.filenames {
files = append(files, strings.TrimPrefix(fn, "./testdata/"))
}
return files, nil
}).AnyTimes()
mockRepo.EXPECT().GetFileContent(gomock.Any()).DoAndReturn(func(fn string) ([]byte, error) {
content, err := os.ReadFile("./testdata/" + fn)
if err != nil {
return content, fmt.Errorf("%w", err)
}
return content, nil
}).AnyTimes()
dl := scut.TestDetailLogger{}
c := checker.CheckRequest{
RepoClient: mockRepo,
Dlogger: &dl,
}
dl := scut.TestDetailLogger{}
r := testValidateGitHubActionTokenPermissions(files, &dl)
if !scut.ValidateTestReturn(t, tt.name, &tt.expected, &r, &dl) {
t.Fail()
res := TokenPermissions(&c)
if !scut.ValidateTestReturn(t, tt.name, &tt.expected, &res, &dl) {
t.Errorf("test failed: log message not present: %+v\n%+v", tt.expected, dl)
}
})
}
@ -440,11 +437,28 @@ func TestGithubTokenPermissionsLineNumber(t *testing.T) {
if err != nil {
t.Errorf("cannot read file: %v", err)
}
dl := scut.TestDetailLogger{}
p := strings.Replace(tt.filename, "./testdata/", "", 1)
files := []file{{pathfn: p, content: content}}
testValidateGitHubActionTokenPermissions(files, &dl)
p := strings.Replace(tt.filename, "./testdata/", "", 1)
ctrl := gomock.NewController(t)
mockRepo := mockrepo.NewMockRepoClient(ctrl)
main := "main"
mockRepo.EXPECT().URI().Return("github.com/ossf/scorecard").AnyTimes()
mockRepo.EXPECT().GetDefaultBranch().Return(&clients.BranchRef{Name: &main}, nil).AnyTimes()
mockRepo.EXPECT().ListFiles(gomock.Any()).DoAndReturn(func(predicate func(string) (bool, error)) ([]string, error) {
return []string{p}, nil
}).AnyTimes()
mockRepo.EXPECT().GetFileContent(gomock.Any()).DoAndReturn(func(fn string) ([]byte, error) {
return content, nil
}).AnyTimes()
dl := scut.TestDetailLogger{}
c := checker.CheckRequest{
RepoClient: mockRepo,
Dlogger: &dl,
}
_ = TokenPermissions(&c)
for _, expectedLog := range tt.expected {
isExpectedLog := func(logMessage checker.LogMessage, logType checker.DetailType) bool {
return logMessage.Offset == expectedLog.lineNumber && logMessage.Path == p &&

View File

@ -15,8 +15,6 @@
package checks
import (
"fmt"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/evaluation"
"github.com/ossf/scorecard/v4/checks/raw"
@ -42,7 +40,7 @@ func init() {
// 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))
e := sce.WithMessage(sce.ErrScorecardInternal, err.Error())
return checker.CreateRuntimeErrorResult(CheckPinnedDependencies, e)
}

View File

@ -48,7 +48,7 @@ func Packaging(c *checker.CheckRequest) (checker.PackagingData, error) {
}
// Check if it's a packaging workflow.
match, ok := isPackagingWorkflow(workflow, fp)
match, ok := fileparser.IsPackagingWorkflow(workflow, fp)
// Always print debug messages.
data.Packages = append(data.Packages,
checker.Package{
@ -117,127 +117,3 @@ func Packaging(c *checker.CheckRequest) (checker.PackagingData, error) {
func stringPointer(s string) *string {
return &s
}
// A packaging workflow.
func isPackagingWorkflow(workflow *actionlint.Workflow, fp string) (fileparser.JobMatchResult, bool) {
jobMatchers := []fileparser.JobMatcher{
{
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-node",
With: map[string]string{"registry-url": "https://registry.npmjs.org"},
},
{
Run: "npm.*publish",
},
},
LogText: "candidate node publishing workflow using npm",
},
{
// Java packages with maven.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "mvn.*deploy",
},
},
LogText: "candidate java publishing workflow using maven",
},
{
// Java packages with gradle.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-java",
},
{
Run: "gradle.*publish",
},
},
LogText: "candidate java publishing workflow using gradle",
},
{
// Ruby packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "gem.*push",
},
},
LogText: "candidate ruby publishing workflow using gem",
},
{
// NuGet packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "nuget.*push",
},
},
LogText: "candidate nuget publishing workflow",
},
{
// Docker packages.
Steps: []*fileparser.JobMatcherStep{
{
Run: "docker.*push",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Docker packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "docker/build-push-action",
},
},
LogText: "candidate docker publishing workflow",
},
{
// Python packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-python",
},
{
Uses: "pypa/gh-action-pypi-publish",
},
},
LogText: "candidate python publishing workflow using pypi",
},
{
// Python packages.
// This is a custom Python packaging workflow based on semantic versioning.
// TODO(#1642): accept custom workflows through a separate configuration.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "relekang/python-semantic-release",
},
},
LogText: "candidate python publishing workflow using python-semantic-release",
},
{
// Go packages.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-go",
},
{
Uses: "goreleaser/goreleaser-action",
},
},
LogText: "candidate golang publishing workflow",
},
{
// Rust packages. https://doc.rust-lang.org/cargo/reference/publishing.html
Steps: []*fileparser.JobMatcherStep{
{
Run: "cargo.*publish",
},
},
LogText: "candidate rust publishing workflow using cargo",
},
}
return fileparser.RawAnyJobsMatch(workflow, jobMatchers, fp, "not a publishing workflow")
}

View File

@ -21,6 +21,8 @@ import (
"testing"
"github.com/rhysd/actionlint"
"github.com/ossf/scorecard/v4/checks/fileparser"
)
func TestIsPackagingWorkflow(t *testing.T) {
@ -106,7 +108,7 @@ func TestIsPackagingWorkflow(t *testing.T) {
}
p := strings.Replace(tt.filename, "./testdata/", "", 1)
_, ok := isPackagingWorkflow(workflow, p)
_, ok := fileparser.IsPackagingWorkflow(workflow, p)
if ok != tt.expected {
t.Errorf("isPackagingWorkflow() = %v, expected %v", ok, tt.expected)
}

569
checks/raw/permissions.go Normal file
View File

@ -0,0 +1,569 @@
// 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 raw
import (
"fmt"
"strings"
"github.com/rhysd/actionlint"
"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks/fileparser"
sce "github.com/ossf/scorecard/v4/errors"
)
type permission string
const (
permissionStatuses = permission("statuses")
permissionChecks = permission("checks")
permissionSecurityEvents = permission("security-events")
permissionDeployments = permission("deployments")
permissionContents = permission("contents")
permissionPackages = permission("packages")
permissionActions = permission("actions")
)
var permissionsOfInterest = []permission{
permissionStatuses, permissionChecks,
permissionSecurityEvents, permissionDeployments,
permissionContents, permissionPackages, permissionActions,
}
type permissionCbData struct {
results checker.TokenPermissionsData
}
// TokenPermissions runs Token-Permissions check.
func TokenPermissions(c *checker.CheckRequest) (checker.TokenPermissionsData, error) {
// data is shared across all GitHub workflows.
var data permissionCbData
err := fileparser.OnMatchingFileContentDo(c.RepoClient, fileparser.PathMatcher{
Pattern: ".github/workflows/*",
CaseSensitive: false,
}, validateGitHubActionTokenPermissions, &data)
return data.results, err
}
// Check file content.
var validateGitHubActionTokenPermissions fileparser.DoWhileTrueOnFileContent = func(path string,
content []byte,
args ...interface{},
) (bool, error) {
if !fileparser.IsWorkflowFile(path) {
return true, nil
}
// Verify the type of the data.
if len(args) != 1 {
return false, fmt.Errorf(
"validateGitHubActionTokenPermissions requires exactly 2 arguments: %w", errInvalidArgLength)
}
pdata, ok := args[0].(*permissionCbData)
if !ok {
return false, fmt.Errorf(
"validateGitHubActionTokenPermissions requires arg[0] of type *permissionCbData: %w", errInvalidArgType)
}
if !fileparser.CheckFileContainsCommands(content, "#") {
return true, nil
}
workflow, errs := actionlint.Parse(content)
if len(errs) > 0 && workflow == nil {
return false, fileparser.FormatActionlintError(errs)
}
// 1. Top-level permission definitions.
//nolint
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#example-1-passing-the-github_token-as-an-input,
// https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/,
// https://docs.github.com/en/actions/reference/authentication-in-a-workflow#modifying-the-permissions-for-the-github_token.
if err := validateTopLevelPermissions(workflow, path, pdata); err != nil {
return false, err
}
// 2. Run-level permission definitions,
// see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idpermissions.
ignoredPermissions := createIgnoredPermissions(workflow, path, pdata)
if err := validatejobLevelPermissions(workflow, path, pdata, ignoredPermissions); err != nil {
return false, err
}
// TODO(laurent): 2. Identify github actions that require write and add checks.
// TODO(laurent): 3. Read a few runs and ensures they have the same permissions.
return true, nil
}
func validatePermission(permissionKey permission, permissionValue *actionlint.PermissionScope,
permLoc checker.PermissionLocation, path string, p *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
if permissionValue.Value == nil {
return sce.WithMessage(sce.ErrScorecardInternal, errInvalidGitHubWorkflow.Error())
}
key := string(permissionKey)
val := permissionValue.Value.Value
lineNumber := fileparser.GetLineNumber(permissionValue.Value.Pos)
if strings.EqualFold(val, "write") {
if isPermissionOfInterest(permissionKey, ignoredPermissions) {
p.results.TokenPermissions = append(p.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Snippet: val,
},
LocationType: &permLoc,
Name: &key,
Value: &val,
Type: checker.PermissionLevelWrite,
// TODO: Job
})
} else {
// Only log for debugging, otherwise
// it may confuse users.
p.results.TokenPermissions = append(p.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Snippet: val,
},
LocationType: &permLoc,
Name: &key,
Value: &val,
// It's a write but not considered dangerous.
Type: checker.PermissionLevelUnknown,
// TODO: Job
})
}
return nil
}
p.results.TokenPermissions = append(p.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
// TODO: set Snippet.
},
LocationType: &permLoc,
Name: &key,
Value: &val,
Type: typeOfPermission(val),
// TODO: Job
})
return nil
}
func typeOfPermission(val string) checker.PermissionLevel {
switch val {
case "read", "read-all":
return checker.PermissionLevelRead
case "none":
return checker.PermissionLevelNone
}
return checker.PermissionLevelUnknown
}
func validateMapPermissions(scopes map[string]*actionlint.PermissionScope, permLoc checker.PermissionLocation,
path string, pdata *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
for key, v := range scopes {
if err := validatePermission(permission(key), v, permLoc, path, pdata, ignoredPermissions); err != nil {
return err
}
}
return nil
}
func validatePermissions(permissions *actionlint.Permissions, permLoc checker.PermissionLocation,
path string, pdata *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
allIsSet := permissions != nil && permissions.All != nil && permissions.All.Value != ""
scopeIsSet := permissions != nil && len(permissions.Scopes) > 0
none := "none"
if permissions == nil || (!allIsSet && !scopeIsSet) {
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
LocationType: &permLoc,
Type: checker.PermissionLevelNone,
Value: &none,
// TODO: Job, etc.
})
}
if allIsSet {
val := permissions.All.Value
lineNumber := fileparser.GetLineNumber(permissions.All.Pos)
if !strings.EqualFold(val, "read-all") && val != "" {
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Snippet: val,
},
LocationType: &permLoc,
Value: &val,
Type: checker.PermissionLevelWrite,
// TODO: Job
})
return nil
}
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: lineNumber,
Snippet: val,
},
LocationType: &permLoc,
Value: &val,
Type: typeOfPermission(val),
// TODO: Job
})
} else /* scopeIsSet == true */ if err := validateMapPermissions(permissions.Scopes,
permLoc, path, pdata, ignoredPermissions); err != nil {
return err
}
return nil
}
func validateTopLevelPermissions(workflow *actionlint.Workflow, path string,
pdata *permissionCbData,
) error {
// Check if permissions are set explicitly.
if workflow.Permissions == nil {
permLoc := checker.PermissionLocationTop
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
LocationType: &permLoc,
Type: checker.PermissionLevelUndeclared,
// TODO: Job
})
return nil
}
return validatePermissions(workflow.Permissions, checker.PermissionLocationTop, path,
pdata, map[permission]bool{})
}
func validatejobLevelPermissions(workflow *actionlint.Workflow, path string,
pdata *permissionCbData,
ignoredPermissions map[permission]bool,
) error {
for _, job := range workflow.Jobs {
// Run-level permissions may be left undefined.
// For most workflows, no write permissions are needed,
// so only top-level read-only permissions need to be declared.
if job.Permissions == nil {
permLoc := checker.PermissionLocationJob
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: path,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(job.Pos),
},
LocationType: &permLoc,
Type: checker.PermissionLevelUndeclared,
Msg: stringPointer(fmt.Sprintf("no %s permission defined", permLoc)),
// TODO: Job
})
continue
}
err := validatePermissions(job.Permissions, checker.PermissionLocationJob,
path, pdata, ignoredPermissions)
if err != nil {
return err
}
}
return nil
}
func isPermissionOfInterest(name permission, ignoredPermissions map[permission]bool) bool {
for _, p := range permissionsOfInterest {
_, present := ignoredPermissions[p]
if strings.EqualFold(string(name), string(p)) && !present {
return true
}
}
return false
}
func createIgnoredPermissions(workflow *actionlint.Workflow, fp string,
pdata *permissionCbData,
) map[permission]bool {
ignoredPermissions := make(map[permission]bool)
if requiresPackagesPermissions(workflow, fp, pdata) {
ignoredPermissions[permissionPackages] = true
}
if requiresContentsPermissions(workflow, fp, pdata) {
ignoredPermissions[permissionContents] = true
}
if isSARIFUploadWorkflow(workflow, fp, pdata) {
ignoredPermissions[permissionSecurityEvents] = true
}
return ignoredPermissions
}
// Scanning tool run externally and SARIF file uploaded.
func isSARIFUploadWorkflow(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
//nolint
// CodeQl analysis workflow automatically sends sarif file to GitHub.
// https://docs.github.com/en/code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github#about-sarif-file-uploads-for-code-scanning.
// `The CodeQL action uploads the SARIF file automatically when it completes analysis`.
if isCodeQlAnalysisWorkflow(workflow, fp, pdata) {
return true
}
//nolint
// Third-party scanning tools use the SARIF-upload action from code-ql.
// https://docs.github.com/en/code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github#uploading-a-code-scanning-analysis-with-github-actions
// We only support CodeQl today.
if isSARIFUploadAction(workflow, fp, pdata) {
return true
}
// TODO: some third party tools may upload directly thru their actions.
// Very unlikely.
// See https://github.com/marketplace for tools.
return false
}
// CodeQl run externally and SARIF file uploaded.
func isSARIFUploadAction(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
for _, job := range workflow.Jobs {
for _, step := range job.Steps {
uses := fileparser.GetUses(step)
if uses == nil {
continue
}
if strings.HasPrefix(uses.Value, "github/codeql-action/upload-sarif@") {
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(uses.Pos),
// TODO: set Snippet.
},
Type: checker.PermissionLevelUnknown,
Msg: stringPointer("codeql SARIF upload workflow detected"),
// TODO: Job
})
return true
}
}
}
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
Type: checker.PermissionLevelUnknown,
Msg: stringPointer("not a codeql upload SARIF workflow"),
// TODO: Job
})
return false
}
// nolint
// CodeQl run within GitHub worklow automatically bubbled up to
// security events, see
// https://docs.github.com/en/code-security/secure-coding/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning.
func isCodeQlAnalysisWorkflow(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
for _, job := range workflow.Jobs {
for _, step := range job.Steps {
uses := fileparser.GetUses(step)
if uses == nil {
continue
}
if strings.HasPrefix(uses.Value, "github/codeql-action/analyze@") {
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: fileparser.GetLineNumber(uses.Pos),
},
Type: checker.PermissionLevelUnknown,
Msg: stringPointer("codeql workflow detected"),
// TODO: Job
})
return true
}
}
}
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
Type: checker.PermissionLevelUnknown,
Msg: stringPointer("not a codeql workflow"),
})
return false
}
// A packaging workflow using GitHub's supported packages:
// https://docs.github.com/en/packages.
func requiresPackagesPermissions(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
// TODO: add support for GitHub registries.
// Example: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry.
match, ok := fileparser.IsPackagingWorkflow(workflow, fp)
// Print debug messages.
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
Msg: &match.Msg,
Type: checker.PermissionLevelUnknown,
})
return ok
}
// requiresContentsPermissions returns true if the workflow requires the `contents: write` permission.
func requiresContentsPermissions(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
return isReleasingWorkflow(workflow, fp, pdata) || isGitHubPagesDeploymentWorkflow(workflow, fp, pdata)
}
// isGitHubPagesDeploymentWorkflow returns true if the workflow involves pushing static pages to GitHub pages.
func isGitHubPagesDeploymentWorkflow(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
jobMatchers := []fileparser.JobMatcher{
{
Steps: []*fileparser.JobMatcherStep{
{
Uses: "peaceiris/actions-gh-pages",
},
},
LogText: "candidate GitHub page deployment workflow using peaceiris/actions-gh-pages",
},
}
return isWorkflowOf(workflow, fp, jobMatchers,
"not a GitHub Pages deployment workflow", pdata)
}
// isReleasingWorkflow returns true if the workflow involves creating a release on GitHub.
func isReleasingWorkflow(workflow *actionlint.Workflow, fp string, pdata *permissionCbData) bool {
jobMatchers := []fileparser.JobMatcher{
{
// Python packages.
// This is a custom Python packaging/releasing workflow based on semantic versioning.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "relekang/python-semantic-release",
},
},
LogText: "candidate python publishing workflow using python-semantic-release",
},
{
// Go binaries.
Steps: []*fileparser.JobMatcherStep{
{
Uses: "actions/setup-go",
},
{
Uses: "goreleaser/goreleaser-action",
},
},
LogText: "candidate golang publishing workflow",
},
{
// SLSA Go builder. https://github.com/slsa-framework/slsa-github-generator
Steps: []*fileparser.JobMatcherStep{
{
Uses: "slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml",
},
},
LogText: "candidate SLSA publishing workflow using slsa-github-generator",
},
{
// SLSA generic generator. https://github.com/slsa-framework/slsa-github-generator
Steps: []*fileparser.JobMatcherStep{
{
Uses: "slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml",
},
},
LogText: "candidate SLSA publishing workflow using slsa-github-generator",
},
}
return isWorkflowOf(workflow, fp, jobMatchers, "not a releasing workflow", pdata)
}
func isWorkflowOf(workflow *actionlint.Workflow, fp string,
jobMatchers []fileparser.JobMatcher, msg string,
pdata *permissionCbData,
) bool {
match, ok := fileparser.AnyJobsMatch(workflow, jobMatchers, fp, msg)
// Print debug messages.
pdata.results.TokenPermissions = append(pdata.results.TokenPermissions,
checker.TokenPermission{
File: &checker.File{
Path: fp,
Type: checker.FileTypeSource,
Offset: checker.OffsetDefault,
},
Msg: &match.Msg,
Type: checker.PermissionLevelUnknown,
})
return ok
}

View File

@ -31,6 +31,7 @@ import (
// 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

View File

@ -4,7 +4,7 @@
// 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
// 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,
@ -57,30 +57,6 @@ var _ = Describe("E2E TEST:"+checks.CheckTokenPermissions, func() {
Expect(scut.ValidateTestReturn(nil, "token permissions", &expected, &result, &dl)).Should(BeTrue())
Expect(repoClient.Close()).Should(BeNil())
})
It("Should return token permission works on empty repo", func() {
dl := scut.TestDetailLogger{}
repo, err := githubrepo.MakeGithubRepo("ossf-tests/scorecard-empty-repo")
Expect(err).Should(BeNil())
repoClient := githubrepo.CreateGithubRepoClient(context.Background(), logger)
err = repoClient.InitRepo(repo, clients.HeadSHA)
Expect(err).Should(BeNil())
req := checker.CheckRequest{
Ctx: context.Background(),
RepoClient: repoClient,
Repo: repo,
Dlogger: &dl,
}
expected := scut.TestReturn{
Error: nil,
Score: checker.MaxResultScore,
NumberOfWarn: 0,
NumberOfInfo: 0,
NumberOfDebug: 0,
}
result := checks.TokenPermissions(&req)
Expect(scut.ValidateTestReturn(nil, "token permissions", &expected, &result, &dl)).Should(BeTrue())
Expect(repoClient.Close()).Should(BeNil())
})
It("Should return token permission at commit", func() {
dl := scut.TestDetailLogger{}
repo, err := githubrepo.MakeGithubRepo("ossf-tests/scorecard-check-token-permissions-e2e")

1
go.mod
View File

@ -95,6 +95,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
github.com/ossf/scorecard v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/prometheus v2.5.0+incompatible // indirect
github.com/rivo/uniseg v0.2.0 // indirect

77
go.sum
View File

@ -22,6 +22,7 @@ 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=
@ -62,6 +63,7 @@ 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=
@ -76,6 +78,7 @@ 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.23.1 h1:eVtkabVa+1M5ai67fGU+idws0hVb/KEPXiDmSS17+qc=
cloud.google.com/go/pubsub v1.23.1/go.mod h1:ttM6nEGYK/2CnB36ndNySU3ZxPwpBk8cXM6+iOlxH9U=
@ -85,6 +88,7 @@ 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.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
cloud.google.com/go/storage v1.23.0 h1:wWRIaDURQA8xxHguFCshYepGlrWIrbBnAmc7wfg07qY=
@ -97,6 +101,7 @@ 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=
@ -109,6 +114,8 @@ 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=
@ -119,18 +126,24 @@ 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=
@ -145,6 +158,10 @@ 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=
@ -154,11 +171,15 @@ 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=
@ -175,6 +196,7 @@ 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=
@ -186,6 +208,7 @@ 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=
@ -268,9 +291,11 @@ 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=
@ -333,6 +358,9 @@ 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=
@ -537,6 +565,7 @@ 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=
@ -728,6 +757,7 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
@ -795,8 +825,10 @@ 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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/crfs v0.0.0-20191108021818-71d77da419c9/go.mod h1:etGhoOqfwPkooV6aqoX3eBGQOJblqdoc9XvWOeuxpPw=
@ -823,6 +855,10 @@ 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 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
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=
@ -831,9 +867,11 @@ 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=
@ -856,6 +894,7 @@ 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=
@ -919,6 +958,7 @@ 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=
@ -1106,6 +1146,7 @@ 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=
@ -1181,6 +1222,7 @@ 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=
@ -1221,6 +1263,8 @@ 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 h1:1mvisGk8KNWdixG7FZ+MGEGdZFxo2e4u82Ke56RqUos=
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=
@ -1243,6 +1287,7 @@ 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=
@ -1259,6 +1304,7 @@ 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=
@ -1300,6 +1346,8 @@ 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=
@ -1307,6 +1355,7 @@ github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bA
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@ -1417,6 +1466,7 @@ 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=
@ -1487,6 +1537,7 @@ 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=
@ -1633,6 +1684,7 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
@ -1641,15 +1693,19 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
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 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
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=
@ -1662,6 +1718,7 @@ 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=
@ -1780,6 +1837,7 @@ 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=
@ -1827,6 +1885,7 @@ 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=
@ -2072,6 +2131,7 @@ 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=
@ -2115,12 +2175,18 @@ 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=
@ -2157,6 +2223,7 @@ 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=
@ -2170,6 +2237,8 @@ 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=
@ -2209,6 +2278,7 @@ 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=
@ -2253,11 +2323,15 @@ 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=
@ -2341,6 +2415,7 @@ 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=
@ -2384,6 +2459,7 @@ 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=
@ -2525,6 +2601,7 @@ 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

@ -162,7 +162,7 @@ type jsonOssfBestPractices struct {
Badge string `json:"badge"`
}
//nolint
// nolint
type jsonLicense struct {
File jsonFile `json:"file"`
// TODO: add fields, like type of license, etc.
@ -206,10 +206,25 @@ type jsonDependency struct {
Type string `json:"type"`
}
//nolint
type jsonPermissionsData struct {
TokenPermissions []jsonTokenPermission `json:"tokens,omitempty"`
}
type jsonTokenPermission struct {
Job *jsonWorkflowJob `json:"job,omitempty"`
LocationType *string `json:"locationType,omitempty"`
Name *string `json:"name,omitempty"`
Value *string `json:"value,omitempty"`
File *jsonFile `json:"file,omitempty"`
Type string `json:"type"`
}
// nolint
type jsonRawResults struct {
// Workflow results.
Workflows []jsonWorkflow `json:"workflows"`
// Permissions.
Permissions jsonPermissionsData `json:"permissions"`
// License.
Licenses []jsonLicense `json:"licenses"`
// List of recent issues.
@ -246,6 +261,54 @@ type jsonRawResults struct {
DependencyPinning jsonPinningDependenciesData `json:"dependencyPinning"`
}
func asPointer(s string) *string {
return &s
}
func (r *jsonScorecardRawResult) addTokenPermissionsRawResults(tp *checker.TokenPermissionsData) error {
r.Results.Permissions = jsonPermissionsData{}
for _, t := range tp.TokenPermissions {
// We ignore debug messages for read/none permissions.
if t.Type != checker.PermissionLevelUndeclared &&
t.Type != checker.PermissionLevelWrite {
continue
}
if t.LocationType == nil {
//nolint
return errors.New("locationType is nil")
}
p := jsonTokenPermission{
LocationType: asPointer(string(*t.LocationType)),
Name: t.Name,
Value: t.Value,
Type: string(t.Type),
}
if t.Job != nil {
p.Job = &jsonWorkflowJob{
Name: t.Job.Name,
ID: t.Job.ID,
}
}
if t.File != nil {
p.File = &jsonFile{
Path: t.File.Path,
Offset: t.File.Offset,
}
if t.File.Snippet != "" {
p.File.Snippet = &t.File.Snippet
}
}
r.Results.Permissions.TokenPermissions = append(r.Results.Permissions.TokenPermissions, p)
}
return nil
}
func (r *jsonScorecardRawResult) addPackagingRawResults(pk *checker.PackagingData) error {
r.Results.Packages = []jsonPackage{}
@ -264,7 +327,10 @@ func (r *jsonScorecardRawResult) addPackagingRawResults(pk *checker.PackagingDat
jpk.File = &jsonFile{
Path: p.File.Path,
Offset: p.File.Offset,
// TODO: Snippet
}
if p.File.Snippet != "" {
jpk.File.Snippet = &p.File.Snippet
}
for _, run := range p.Runs {
@ -690,6 +756,10 @@ func (r *jsonScorecardRawResult) fillJSONRawResults(raw *checker.RawResults) err
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
}
if err := r.addTokenPermissionsRawResults(&raw.TokenPermissionsResults); err != nil {
return sce.WithMessage(sce.ErrScorecardInternal, err.Error())
}
return nil
}

View File

@ -58,7 +58,7 @@ func Setup(c *checker.CheckRequest) error {
if b != nil && b.Name != nil {
branch = *b.Name
uri := c.Repo.URI()
uri := c.RepoClient.URI()
parts := strings.Split(uri, "/")
if len(parts) != 3 {
setupErr = fmt.Errorf("%w: enpty: %s", errInvalidArg, uri)
@ -67,7 +67,6 @@ func Setup(c *checker.CheckRequest) error {
repo = fmt.Sprintf("%s/%s", parts[1], parts[2])
}
})
return setupErr
}