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

feat: Verify provenance by build type #632

Merged
merged 6 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 3 additions & 2 deletions verifiers/internal/gha/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/secure-systems-lab/go-securesystemslib/dsse"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"
)

Expand Down Expand Up @@ -114,7 +115,7 @@ func extractAttestations(attestations []attestation) (*attestation, *attestation
for i := range attestations {
att := attestations[i]
// Provenance type verification.
if att.PredicateType == slsaprovenance.ProvenanceV02Type {
if att.PredicateType == common.ProvenanceV02Type {
provenanceAttestation = &att
}
// Publish type verification.
Expand Down Expand Up @@ -196,7 +197,7 @@ func (n *Npm) verifyPublishAttesttationSignature() error {

func (n *Npm) verifyIntotoHeaders() error {
if err := verifyIntotoTypes(n.verifiedProvenanceAtt,
slsaprovenance.ProvenanceV02Type, intoto.PayloadType, false); err != nil {
common.ProvenanceV02Type, intoto.PayloadType, false); err != nil {
return err
}
if err := verifyIntotoTypes(n.verifiedPublishAtt,
Expand Down
8 changes: 4 additions & 4 deletions verifiers/internal/gha/npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
intoto "github.com/in-toto/in-toto-golang/in_toto"
dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse"
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
)

func Test_verifyName(t *testing.T) {
Expand Down Expand Up @@ -671,7 +671,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
}{
{
name: "prov correct",
predicateType: slsaprovenance.ProvenanceV02Type,
predicateType: common.ProvenanceV02Type,
payloadType: intoto.PayloadType,
att: &SignedAttestation{
Envelope: &dsselib.Envelope{
Expand All @@ -682,7 +682,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
},
{
name: "prov mismatch payload type",
predicateType: slsaprovenance.ProvenanceV02Type,
predicateType: common.ProvenanceV02Type,
payloadType: intoto.PayloadType,
att: &SignedAttestation{
Envelope: &dsselib.Envelope{
Expand All @@ -694,7 +694,7 @@ func Test_verifyIntotoTypes(t *testing.T) {
},
{
name: "prov mismatch predicate type",
predicateType: slsaprovenance.ProvenanceV02Type + "a",
predicateType: common.ProvenanceV02Type + "a",
payloadType: intoto.PayloadType,
att: &SignedAttestation{
Envelope: &dsselib.Envelope{
Expand Down
34 changes: 19 additions & 15 deletions verifiers/internal/gha/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import (
serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/common"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/iface"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/utils"

// Load provenance types.

_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
)

// SignedAttestation contains a signed DSSE envelope
Expand All @@ -34,6 +32,7 @@ type SignedAttestation struct {
RekorEntry *models.LogEntryAnon
}

// EnvelopeFromBytes reads a DSSE envelope from the given payload.
func EnvelopeFromBytes(payload []byte) (env *dsselib.Envelope, err error) {
env = &dsselib.Envelope{}
err = json.Unmarshal(payload, env)
Expand All @@ -43,7 +42,7 @@ func EnvelopeFromBytes(payload []byte) (env *dsselib.Envelope, err error) {
// Verify Builder ID in provenance statement.
// This function does an exact comparison, and expects expectedBuilderID to be the full
// `name@refs/tags/<name>`.
func verifyBuilderIDExactMatch(prov slsaprovenance.Provenance, expectedBuilderID string) error {
func verifyBuilderIDExactMatch(prov iface.Provenance, expectedBuilderID string) error {
id, err := prov.BuilderID()
if err != nil {
return err
Expand All @@ -62,7 +61,7 @@ func verifyBuilderIDExactMatch(prov slsaprovenance.Provenance, expectedBuilderID
// Verify Builder ID in provenance statement.
// This function verifies the names match. If the expected builder ID contains a version,
// it also verifies the versions match.
func verifyBuilderIDLooseMatch(prov slsaprovenance.Provenance, expectedBuilderID string) error {
func verifyBuilderIDLooseMatch(prov iface.Provenance, expectedBuilderID string) error {
id, err := prov.BuilderID()
if err != nil {
return err
Expand Down Expand Up @@ -91,7 +90,7 @@ func asURI(s string) string {
}

// Verify source URI in provenance statement.
func verifySourceURI(prov slsaprovenance.Provenance, expectedSourceURI string, allowNoMaterialRef bool) error {
func verifySourceURI(prov iface.Provenance, expectedSourceURI string, allowNoMaterialRef bool) error {
source := asURI(expectedSourceURI)

// We expect github.com URIs only.
Expand Down Expand Up @@ -172,7 +171,7 @@ func sourceFromURI(uri string, allowNoRef bool) (string, error) {
}

// Verify Subject Digest from the provenance statement.
func verifyDigest(prov slsaprovenance.Provenance, expectedHash string) error {
func verifyDigest(prov iface.Provenance, expectedHash string) error {
subjects, err := prov.Subjects()
if err != nil {
return err
Expand Down Expand Up @@ -221,6 +220,7 @@ func VerifyProvenanceSignature(ctx context.Context, trustedRoot *TrustedRoot,
provenance, rClient, trustedRoot)
}

// VerifyNpmPackageProvenance verifies provenance for an npm package.
func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentity,
provenanceOpts *options.ProvenanceOpts, isTrustedBuilder bool,
) error {
Expand Down Expand Up @@ -282,7 +282,7 @@ func VerifyNpmPackageProvenance(env *dsselib.Envelope, workflow *WorkflowIdentit
return nil
}

func isValidDelegatorBuilderID(prov slsaprovenance.Provenance) error {
func isValidDelegatorBuilderID(prov iface.Provenance) error {
// Verify the TRW was referenced at a proper tag by the user.
id, err := prov.BuilderID()
if err != nil {
Expand All @@ -295,6 +295,7 @@ func isValidDelegatorBuilderID(prov slsaprovenance.Provenance) error {
return utils.IsValidBuilderTag(parts[1], false)
}

// VerifyProvenance verifies the provenance for the given DSSE envelope.
func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceOpts, byob bool,
) error {
prov, err := slsaprovenance.ProvenanceFromEnvelope(env)
Expand Down Expand Up @@ -322,7 +323,8 @@ func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceO
return VerifyProvenanceCommonOptions(prov, provenanceOpts, false)
}

func VerifyProvenanceCommonOptions(prov slsaprovenance.Provenance, provenanceOpts *options.ProvenanceOpts,
// VerifyProvenanceCommonOptions verifies the given provenance.
func VerifyProvenanceCommonOptions(prov iface.Provenance, provenanceOpts *options.ProvenanceOpts,
allowNoMaterialRef bool,
) error {
// Verify source.
Expand Down Expand Up @@ -366,15 +368,17 @@ func VerifyProvenanceCommonOptions(prov slsaprovenance.Provenance, provenanceOpt
return nil
}

func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]string) error {
// VerifyWorkflowInputs verifies that the workflow inputs in the provenance
// match the expected values.
func VerifyWorkflowInputs(prov iface.Provenance, inputs map[string]string) error {
pyldInputs, err := prov.GetWorkflowInputs()
if err != nil {
return err
}

// Verify all inputs.
for k, v := range inputs {
value, err := slsaprovenance.GetAsString(pyldInputs, k)
value, err := common.GetAsString(pyldInputs, k)
if err != nil {
return fmt.Errorf("%w: cannot retrieve value of '%s'", serrors.ErrorMismatchWorkflowInputs, k)
}
Expand All @@ -390,7 +394,7 @@ func VerifyWorkflowInputs(prov slsaprovenance.Provenance, inputs map[string]stri

// VerifyBranch verifies that the source branch in the provenance matches the
// expected value.
func VerifyBranch(prov slsaprovenance.Provenance, expectedBranch string) error {
func VerifyBranch(prov iface.Provenance, expectedBranch string) error {
ref, err := prov.GetBranch()
if err != nil {
return err
Expand All @@ -410,7 +414,7 @@ func VerifyBranch(prov slsaprovenance.Provenance, expectedBranch string) error {

// VerifyTag verifies that the source tag in the provenance matches the
// expected value.
func VerifyTag(prov slsaprovenance.Provenance, expectedTag string) error {
func VerifyTag(prov iface.Provenance, expectedTag string) error {
ref, err := prov.GetTag()
if err != nil {
return err
Expand All @@ -430,7 +434,7 @@ func VerifyTag(prov slsaprovenance.Provenance, expectedTag string) error {

// VerifyVersionedTag verifies that the source tag in the provenance matches the
// expected semver value.
func VerifyVersionedTag(prov slsaprovenance.Provenance, expectedTag string) error {
func VerifyVersionedTag(prov iface.Provenance, expectedTag string) error {
// Retrieve, validate and canonicalize the provenance tag.
// Note: prerelease is validated as part of patch validation
// and must be equal. Build is discarded as per https://semver.org/:
Expand Down
63 changes: 33 additions & 30 deletions verifiers/internal/gha/provenance_forgeable.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import (
"strings"

serrors "github.com/slsa-framework/slsa-verifier/v2/errors"
"github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance"

// Load provenance types.
"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"
_ "github.com/slsa-framework/slsa-verifier/v2/verifiers/internal/gha/slsaprovenance/v1.0"
)

func verifyProvenanceMatchesCertificate(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
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 {
Expand Down Expand Up @@ -65,7 +63,7 @@ func verifyProvenanceMatchesCertificate(prov slsaprovenance.Provenance, workflow
return nil
}

func verifySubjectDigestName(prov slsaprovenance.Provenance, digestName string) error {
func verifySubjectDigestName(prov iface.Provenance, digestName string) error {
subjects, err := prov.Subjects()
if err != nil {
return err
Expand All @@ -84,7 +82,7 @@ func verifySubjectDigestName(prov slsaprovenance.Provenance, digestName string)
return nil
}

func verifyBuildConfig(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
func verifyBuildConfig(prov iface.Provenance, workflow *WorkflowIdentity) error {
triggerPath, err := prov.GetBuildTriggerPath()
if err != nil {
// If the field is not available in the provenance,
Expand All @@ -98,7 +96,7 @@ func verifyBuildConfig(prov slsaprovenance.Provenance, workflow *WorkflowIdentit
return equalCertificateValue(workflow.BuildConfigPath, triggerPath, "trigger workflow")
}

func verifyResolvedDependencies(prov slsaprovenance.Provenance) error {
func verifyResolvedDependencies(prov iface.Provenance) error {
n, err := prov.GetNumberResolvedDependencies()
if err != nil {
return err
Expand All @@ -110,7 +108,7 @@ func verifyResolvedDependencies(prov slsaprovenance.Provenance) error {
return nil
}

func verifyMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
func verifyMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {
if err := verifyCommonMetadata(prov, workflow); err != nil {
return err
}
Expand All @@ -125,7 +123,7 @@ func verifyMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity)
return nil
}

func verifyCommonMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
func verifyCommonMetadata(prov iface.Provenance, workflow *WorkflowIdentity) error {
// Verify build invocation ID.
invocationID, err := prov.GetBuildInvocationID()
if err != nil {
Expand Down Expand Up @@ -169,7 +167,7 @@ func verifyCommonMetadata(prov slsaprovenance.Provenance, workflow *WorkflowIden
return nil
}

func verifyV02Metadata(prov slsaprovenance.Provenance) error {
func verifyV02Metadata(prov iface.Provenance) error {
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
/*
v0.2:
Expand All @@ -181,21 +179,23 @@ func verifyV02Metadata(prov slsaprovenance.Provenance) error {
},
"reproducible": false
*/
prov02, ok := prov.(*slsav02.ProvenanceV02)
prov02, ok := prov.(slsav02.ProvenanceV02)
if !ok {
return nil
}
if prov02.Predicate.Metadata == nil {
predicate := prov02.Predicate()

if predicate.Metadata == nil {
return nil
}

if prov02.Predicate.Metadata.Reproducible {
if predicate.Metadata.Reproducible {
return fmt.Errorf("%w: reproducible: %v",
serrors.ErrorNonVerifiableClaim,
prov02.Predicate.Metadata.Reproducible)
predicate.Metadata.Reproducible)
}

completeness := prov02.Predicate.Metadata.Completeness
completeness := predicate.Metadata.Completeness
if completeness.Parameters || completeness.Materials ||
completeness.Environment {
return fmt.Errorf("%w: completeness: %v",
Expand All @@ -205,44 +205,47 @@ func verifyV02Metadata(prov slsaprovenance.Provenance) error {
return nil
}

func verifyV02Parameters(prov slsaprovenance.Provenance) error {
func verifyV02Parameters(prov iface.Provenance) error {
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
prov02, ok := prov.(*slsav02.ProvenanceV02)
prov02, ok := prov.(slsav02.ProvenanceV02)
if !ok {
return nil
}
if prov02.Predicate.Invocation.Parameters == nil {
predicate := prov02.Predicate()

if predicate.Invocation.Parameters == nil {
return nil
}
m, ok := prov02.Predicate.Invocation.Parameters.(map[string]any)
m, ok := predicate.Invocation.Parameters.(map[string]any)
if !ok || len(m) > 0 {
return fmt.Errorf("%w: parameters: %v",
serrors.ErrorNonVerifiableClaim, prov02.Predicate.Invocation.Parameters)
serrors.ErrorNonVerifiableClaim, predicate.Invocation.Parameters)
}

return nil
}

func verifyV02BuildConfig(prov slsaprovenance.Provenance) error {
func verifyV02BuildConfig(prov iface.Provenance) error {
// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go
prov02, ok := prov.(*slsav02.ProvenanceV02)
prov02, ok := prov.(slsav02.ProvenanceV02)
if !ok {
return nil
}
predicate := prov02.Predicate()

if prov02.Predicate.BuildConfig == nil {
if predicate.BuildConfig == nil {
return nil
}
m, ok := prov02.Predicate.BuildConfig.(map[string]any)
m, ok := predicate.BuildConfig.(map[string]any)
if !ok || len(m) > 0 {
return fmt.Errorf("%w: buildConfig: %v",
serrors.ErrorNonVerifiableClaim, prov02.Predicate.BuildConfig)
serrors.ErrorNonVerifiableClaim, predicate.BuildConfig)
}

return nil
}

func verifySystemParameters(prov slsaprovenance.Provenance, workflow *WorkflowIdentity) error {
func verifySystemParameters(prov iface.Provenance, workflow *WorkflowIdentity) error {
/*
"environment": {
"GITHUB_EVENT_NAME": "workflow_dispatch",
Expand Down Expand Up @@ -338,7 +341,7 @@ func getRunIDs(workflow *WorkflowIdentity) (string, string, error) {

func verifySystemRun(params map[string]any, workflow *WorkflowIdentity) error {
// Verify only if the values are provided in the provenance.
if !slsaprovenance.Exists(params, "GITHUB_RUN_ID") && !slsaprovenance.Exists(params, "GITHUB_RUN_ATTEMPT") {
if !common.Exists(params, "GITHUB_RUN_ID") && !common.Exists(params, "GITHUB_RUN_ATTEMPT") {
return nil
}
// The certificate contains runID as '4757060009/attempts/1'.
Expand All @@ -364,11 +367,11 @@ func verifySystemRun(params map[string]any, workflow *WorkflowIdentity) error {

func verifySystemParameter(params map[string]any, name string, certValue *string) error {
// If the provenance does not contain an env variable.
if !slsaprovenance.Exists(params, name) {
if !common.Exists(params, name) {
return nil
}
// Provenance contains the field, we must verify it.
provValue, err := slsaprovenance.GetAsString(params, name)
provValue, err := common.GetAsString(params, name)
if err != nil {
return err
}
Expand Down
Loading
Loading