Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Surface workflow verification errors with doc link #408

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Wrap verification errors with single type.
Signed-off-by: Spencer Schrock <sschrock@google.com>
  • Loading branch information
spencerschrock committed May 26, 2023
commit fb6f971d12b5f27ac085db01023b4ebf77b4a00a
28 changes: 8 additions & 20 deletions app/server/post_results.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ var (
errCertMissingURI = errors.New("certificate has no URIs")
errCertWorkflowPathEmpty = errors.New("cert workflow path is empty")
errMismatchedCertAndRequest = errors.New("repository and branch of cert doesn't match that of request")
errWorkflowVerification = errors.New("workflow verification failed")
)

type certInfo struct {
Expand Down Expand Up @@ -108,21 +109,10 @@ func PostResultsHandler(params results.PostResultParams) middleware.Responder {
if err == nil {
return results.NewPostResultCreated().WithPayload("successfully verified and published ScorecardResult")
}
if errors.Is(err, errMismatchedCertAndRequest) ||
errors.Is(err, errGlobalVarsOrDefaults) ||
errors.Is(err, errGlobalWriteAll) ||
errors.Is(err, errGlobalWrite) ||
errors.Is(err, errScorecardJobNotFound) ||
errors.Is(err, errNonScorecardJobHasTokenWrite) ||
errors.Is(err, errJobHasContainerOrServices) ||
errors.Is(err, errScorecardJobRunsOn) ||
errors.Is(err, errUnallowedStepName) ||
errors.Is(err, errScorecardJobEnvVars) ||
errors.Is(err, errScorecardJobDefaults) ||
errors.Is(err, errEmptyStepUses) {
if errors.Is(err, errMismatchedCertAndRequest) || errors.Is(err, errWorkflowVerification) {
return results.NewPostResultBadRequest().WithPayload(&models.Error{
Code: http.StatusBadRequest,
Message: err.Error(),
Message: fmt.Sprintf("Your request failed validation. See foo.bar/explanation.md for details. %v", err),
})
}
log.Println(err)
Expand Down Expand Up @@ -150,7 +140,8 @@ func processRequest(host, org, repo string, scorecardResult *models.VerifiedScor
}

if err := getAndVerifyWorkflowContent(ctx, scorecardResult, info); err != nil {
return fmt.Errorf("error verifying workflow: %w", err)
// TODO(go 1.20) wrap multiple errors https://go.dev/doc/go1.20#errors
return fmt.Errorf("%w: %v", errWorkflowVerification, err.Error())
}

// Save scorecard results (results.json, score.txt) to GCS
Expand Down Expand Up @@ -222,19 +213,16 @@ func getAndVerifyWorkflowContent(ctx context.Context,

contents, _, _, err := client.Repositories.GetContents(ctx, org, repo, path, opts)
if err != nil {
return fmt.Errorf("error downloading workflow contents from repo: %v", err)
return fmt.Errorf("error downloading workflow contents from repo: %w", err)
}

workflowContent, err := contents.GetContent()
if err != nil {
return fmt.Errorf("error decoding workflow contents: %v", err)
return fmt.Errorf("error decoding workflow contents: %w", err)
}

// Verify scorecard workflow.
if err := verifyScorecardWorkflow(workflowContent); err != nil {
return fmt.Errorf("workflow could not be verified: %v", err)
}
return nil
return verifyScorecardWorkflow(workflowContent)
}

func writeToBlobStore(ctx context.Context, bucketURL, filename string, data []byte) error {
Expand Down
18 changes: 9 additions & 9 deletions app/server/verify_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ func verifyScorecardWorkflow(workflowContent string) error {

// Verify that there are no global env vars or defaults.
if workflow.Env != nil || workflow.Defaults != nil {
return fmt.Errorf("%w", errGlobalVarsOrDefaults)
return errGlobalVarsOrDefaults
}

if workflow.Permissions != nil {
globalPerms := workflow.Permissions
// Verify that the all scope, if set, isn't write-all.
if globalPerms.All != nil && globalPerms.All.Value == "write-all" {
return fmt.Errorf("%w", errGlobalWriteAll)
return errGlobalWriteAll
}

// Verify that there are no global permissions (including id-token) set to write.
Expand All @@ -78,27 +78,27 @@ func verifyScorecardWorkflow(workflowContent string) error {
// Find the (first) job with a step that calls scorecard-action.
scorecardJob := findScorecardJob(workflow.Jobs)
if scorecardJob == nil {
return fmt.Errorf("%w", errScorecardJobNotFound)
return errScorecardJobNotFound
}

// Make sure other jobs don't have id-token permissions.
for _, job := range workflow.Jobs {
if job != scorecardJob && job.Permissions != nil {
idToken := job.Permissions.Scopes["id-token"]
if idToken != nil && idToken.Value.Value == "write" {
return fmt.Errorf("%w", errNonScorecardJobHasTokenWrite)
return errNonScorecardJobHasTokenWrite
}
}
}

// Verify that there is no job container or services.
if scorecardJob.Container != nil || len(scorecardJob.Services) > 0 {
return fmt.Errorf("%w", errJobHasContainerOrServices)
return errJobHasContainerOrServices
}

labels := scorecardJob.RunsOn.Labels
if len(labels) != 1 {
return fmt.Errorf("%w", errScorecardJobRunsOn)
return errScorecardJobRunsOn
}
label := labels[0].Value
if _, ok := ubuntuRunners[label]; !ok {
Expand All @@ -107,12 +107,12 @@ func verifyScorecardWorkflow(workflowContent string) error {

// Verify that there are no job env vars set.
if scorecardJob.Env != nil {
return fmt.Errorf("%w", errScorecardJobEnvVars)
return errScorecardJobEnvVars
}

// Verify that there are no job defaults set.
if scorecardJob.Defaults != nil {
return fmt.Errorf("%w", errScorecardJobDefaults)
return errScorecardJobDefaults
}

// Get steps in job.
Expand All @@ -122,7 +122,7 @@ func verifyScorecardWorkflow(workflowContent string) error {
for _, step := range steps {
stepUses := getStepUses(step)
if stepUses == nil {
return fmt.Errorf("%w", errEmptyStepUses)
return errEmptyStepUses
}
stepName := getStepName(stepUses.Value)

Expand Down