Skip to content

Commit

Permalink
use type switches instead of mangling sysParams
Browse files Browse the repository at this point in the history
Signed-off-by: Ramon Petgrave <ramon.petgrave64@gmail.com>
  • Loading branch information
ramonpetgrave64 committed May 30, 2024
1 parent cb1b033 commit dd00555
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 66 deletions.
90 changes: 76 additions & 14 deletions verifiers/internal/gha/provenance_forgeable.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@ import (
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
slsav02 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v0.2"
slsav1 "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
)

func verifyProvenanceMatchesCertificate(prov iface.Provenance, workflow *WorkflowIdentity) error {
// See the generation at https://github.com/npm/cli/blob/latest/workspaces/libnpmpublish/lib/provenance.js.
// Verify systemParameters.
if err := verifySystemParameters(prov, workflow); err != nil {
return err
switch typedProv := prov.(type) {
case *slsav1.NpmCLIGithubActionsProvenance:
if err := verifyNpmCLIGithubActionsV1SystemParameters(typedProv, workflow); err != nil {
return err
}
default:
if err := verifySystemParameters(typedProv, workflow); err != nil {
return err
}
}

// Verify v0.2 parameters.
Expand Down Expand Up @@ -125,23 +133,45 @@ func verifyMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {

func verifyCommonMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {
// Verify build invocation ID.
invocationID, err := prov.GetBuildInvocationID()
provInvocationID, err := prov.GetBuildInvocationID()
if err != nil {
return err
}

runID, runAttempt, err := getRunIDs(workflow)
if err != nil {
return err
}
if provInvocationID != "" {
// Verify runID and runAttempt.
var provRunID string
var provRunAttempt string
switch prov.(type) {
case *slsav1.NpmCLIGithubActionsProvenance:
provenanceInvocationIDParts := strings.Split(strings.TrimPrefix(provInvocationID, "https://github.com/"), "/")
lenParts := len(provenanceInvocationIDParts)
if lenParts != 7 {
return fmt.Errorf("%w: invalid invocation ID: %v", serrors.ErrorInvalidFormat, provInvocationID)
}
provRunID = provenanceInvocationIDParts[lenParts-3]
provRunAttempt = provenanceInvocationIDParts[lenParts-1]
default:
provenanceInvocationIDParts := strings.Split(provInvocationID, "-")
if len(provenanceInvocationIDParts) != 2 {
return fmt.Errorf("%w: invalid invocation ID: %v", serrors.ErrorInvalidFormat, provInvocationID)
}
provRunID = provenanceInvocationIDParts[0]
provRunAttempt = provenanceInvocationIDParts[1]
}

certRunID, certRunAttempt, err := getRunIDs(workflow)
if err != nil {
return err
}

// Only verify a non-empty buildID claim.
if invocationID != "" {
expectedID := fmt.Sprintf("%v-%v", runID, runAttempt)
if invocationID != expectedID {
return fmt.Errorf("%w: invocation ID: '%v' != '%v'",
serrors.ErrorMismatchCertificate, invocationID,
expectedID)
if provRunID != certRunID {
return fmt.Errorf("%w: run ID: '%v' != '%v'",
serrors.ErrorMismatchCertificate, provRunID, certRunID)
}
if provRunAttempt != certRunAttempt {
return fmt.Errorf("%w: run ID: '%v' != '%v'",
serrors.ErrorMismatchCertificate, provRunAttempt, certRunAttempt)
}
}

Expand Down Expand Up @@ -245,6 +275,38 @@ func verifyV02BuildConfig(prov iface.Provenance) error {
return nil
}

func verifyNpmCLIGithubActionsV1SystemParameters(prov iface.Provenance, workflow *WorkflowIdentity) error {
prov, ok := prov.(*slsav1.NpmCLIGithubActionsProvenance)
if !ok {
return nil
}
sysParams, err := prov.GetSystemParameters()
if err != nil {
return err
}
githubParams, ok := sysParams["github"].(map[string]any)
if !ok {
return fmt.Errorf("%w: %s", serrors.ErrorInvalidFormat, "github parameters")
}
// Verify that the parameters contain only fields we are able to verify
// and that the values match the certificate.
supportedNames := map[string]*string{
"event_name": &workflow.BuildTrigger,
"repository_id": workflow.SourceID,
"repository_owner_id": workflow.SourceOwnerID,
}
for k := range githubParams {
certValue, ok := supportedNames[k]
if !ok {
return fmt.Errorf("%w: unknown '%s' parameter", serrors.ErrorMismatchCertificate, k)
}
if err := verifySystemParameter(githubParams, k, certValue); err != nil {
return err
}
}
return nil
}

func verifySystemParameters(prov iface.Provenance, workflow *WorkflowIdentity) error {
/*
"environment": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package v1

import (
"fmt"
"path"
"strings"

serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
)
Expand Down Expand Up @@ -34,53 +32,3 @@ func (p *NpmCLIGithubActionsProvenance) TriggerURI() (string, error) {
uri := fmt.Sprintf("git+%s@%s", repository, ref)
return uri, nil
}

// GetBuildInvocationID implements Provenance.GetBuildInvocationID.
func (p *NpmCLIGithubActionsProvenance) GetBuildInvocationID() (string, error) {
url := p.prov.Predicate.RunDetails.BuildMetadata.InvocationID
attempt := path.Base(url)
runID := path.Base(path.Dir(path.Dir(url)))
invocationID := fmt.Sprintf("%s-%s", runID, attempt)
return invocationID, nil
}

// GetSystemParameters implements Provenance.GetSystemParameters.
// Definitions are in https://github.com/slsa-framework/github-actions-buildtypes/tree/5f855ef0106dad3ee0e0f1046dc31b3b65152956/workflow/v1
// See also https://github.com/slsa-framework/slsa/blob/main/docs/spec/v1.0/provenance.md#migrating-from-02.
func (p *NpmCLIGithubActionsProvenance) GetSystemParameters() (map[string]any, error) {
internalParams, ok := p.prov.Predicate.BuildDefinition.InternalParameters.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "internal parameters type")
}
github, ok := internalParams["github"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidFormat, "github parameters")
}
externalParams, ok := p.prov.Predicate.BuildDefinition.ExternalParameters.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, "external parameters type")
}
workflow, ok := externalParams["workflow"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("%w: %s", serrors.ErrorInvalidFormat, "workflow parameters")
}
invocationID, err := p.GetBuildInvocationID()
if err != nil {
return nil, err
}
invocationParts := strings.Split(invocationID, "-")
repo := strings.TrimPrefix(workflow["repository"].(string), "https://github.com/")
workflowRef := fmt.Sprintf("%s/%s@%s", repo, workflow["path"], workflow["ref"])
sysParams := make(map[string]any)
sysParams["GITHUB_EVENT_NAME"] = github["event_name"]
sysParams["GITHUB_REF"] = workflow["ref"]
sysParams["GITHUB_REPOSITORY"] = repo
sysParams["GITHUB_REPOSITORY_ID"] = github["repository_id"]
sysParams["GITHUB_REPOSITORY_OWNER_ID"] = github["repository_owner_id"]
sysParams["GITHUB_RUN_ATTEMPT"] = invocationParts[1]
sysParams["GITHUB_RUN_ID"] = invocationParts[0]
// not supporting GITHUB_SHA, though according to spec, it should be the same as GITHUB_WORKFLOW_SHA
sysParams["GITHUB_WORKFLOW_REF"] = workflowRef
sysParams["GITHUB_WORKFLOW_SHA"] = p.prov.Predicate.BuildDefinition.ResolvedDependencies[0].Digest["gitCommit"]
return sysParams, nil
}

0 comments on commit dd00555

Please sign in to comment.