From b2a305adac356172cde71c877d5f79b06595f780 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Mon, 25 Sep 2023 15:27:09 -0700 Subject: [PATCH] Add negative tests for Provenance v1 protos Signed-off-by: Marcela Melara --- go/predicates/provenance/v1/provenance.go | 26 ++++--- .../provenance/v1/provenance_test.go | 67 +++++++++++++++++++ 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/go/predicates/provenance/v1/provenance.go b/go/predicates/provenance/v1/provenance.go index dba9c975..7064068f 100644 --- a/go/predicates/provenance/v1/provenance.go +++ b/go/predicates/provenance/v1/provenance.go @@ -6,16 +6,19 @@ package v1 import ( "errors" "fmt" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" ) // all of the following errors apply to SLSA Build L1 and above var ( - ErrBuilderRequired = errors.New("RunDetails.Builder required") - ErrBuilderIdRequired = errors.New("Builder.Id required") - ErrBuildDefinitionRequired = errors.New("BuildeDefinition required") - ErrBuildTypeRequired = errors.New("BuildDefinition.BuildType required") - ErrExternalParamsRequired = errors.New("BuildDefinition.ExternalParameters required") - ErrRunDetailsRequired = errors.New("RunDetails required") + ErrBuilderRequired = errors.New("runDetails.builder required") + ErrBuilderIdRequired = errors.New("runDetails.builder.id required") + ErrBuildDefinitionRequired = errors.New("buildDefinition required") + ErrBuildTypeRequired = errors.New("buildDefinition.buildType required") + ErrExternalParamsRequired = errors.New("buildDefinition.externalParameters required") + ErrRunDetailsRequired = errors.New("runDetails required") ) func (m *BuildMetadata) Validate() error { @@ -63,7 +66,8 @@ func (b *BuildDefinition) Validate() error { } // the externalParameters field is required for SLSA Build L1 - if b.GetExternalParameters() == nil { + ext := b.GetExternalParameters() + if ext == nil || proto.Equal(ext, &structpb.Struct{}) { return ErrExternalParamsRequired } @@ -83,7 +87,7 @@ func (b *BuildDefinition) Validate() error { func (r *RunDetails) Validate() error { // the builder field is required for SLSA Build L1 builder := r.GetBuilder() - if builder == nil { + if builder == nil || proto.Equal(builder, &Builder{}) { return ErrBuilderRequired } @@ -94,7 +98,7 @@ func (r *RunDetails) Validate() error { // check the Metadata, if present metadata := r.GetMetadata() - if metadata != nil { + if metadata != nil && !proto.Equal(metadata, &BuildMetadata{}) { if err := metadata.Validate(); err != nil { return fmt.Errorf("Invalid RunDetails.Metadata: %w", err) } @@ -116,7 +120,7 @@ func (r *RunDetails) Validate() error { func (p *Provenance) Validate() error { // the buildDefinition field is required for SLSA Build L1 buildDef := p.GetBuildDefinition() - if buildDef == nil { + if buildDef == nil || proto.Equal(buildDef, &BuildDefinition{}) { return ErrBuildDefinitionRequired } @@ -127,7 +131,7 @@ func (p *Provenance) Validate() error { // the runDetails field is required for SLSA Build L1 runDetails := p.GetRunDetails() - if runDetails == nil { + if runDetails == nil || proto.Equal(runDetails, &RunDetails{}) { return ErrRunDetailsRequired } diff --git a/go/predicates/provenance/v1/provenance_test.go b/go/predicates/provenance/v1/provenance_test.go index c2c544ed..bf482b30 100644 --- a/go/predicates/provenance/v1/provenance_test.go +++ b/go/predicates/provenance/v1/provenance_test.go @@ -5,6 +5,7 @@ Tests for SLSA Provenance v1 protos. package v1 import ( + "fmt" "testing" ita1 "github.com/in-toto/attestation/go/v1" @@ -70,3 +71,69 @@ func TestJsonUnmarshalProvenance(t *testing.T) { assert.NoError(t, err, "unexpected error during test Statement creation") assert.True(t, proto.Equal(got, want), "protos do not match") } + +func TestBadProvenanceBuildDefinition(t *testing.T) { + tests := map[string]struct { + input string + err error + noErrMessage string + }{ + "no buildDefinition": { + input: `{"buildDefinition":{},"runDetails":{"builder":{"id":"theId","version":{"theComponent":"v0.1"},"builderDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"metadata":{"invocationId":"theInvocationId"},"byproducts":[{"name":"theResource","digest":{"alg1":"abc123"}}]}}`, + err: ErrBuildDefinitionRequired, + noErrMessage: "created malformed Provenance (empty buildDefinition)", + }, + "buildDefinition missing buildType required field": { + input: `{"buildDefinition":{"externalParameters":{"param1":{"subKey":"subVal"}},"resolvedDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"runDetails":{"builder":{"id":"theId","version":{"theComponent":"v0.1"},"builderDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"metadata":{"invocationId":"theInvocationId"},"byproducts":[{"name":"theResource","digest":{"alg1":"abc123"}}]}}`, + err: ErrBuildTypeRequired, + noErrMessage: "created malformed Provenance (buildDefinition missing required buildType field)", + }, + "buildDefinition missing externalParameters required field": { + input: `{"buildDefinition":{"buildType":"theBuildType","resolvedDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"runDetails":{"builder":{"id":"theId","version":{"theComponent":"v0.1"},"builderDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"metadata":{"invocationId":"theInvocationId"},"byproducts":[{"name":"theResource","digest":{"alg1":"abc123"}}]}}`, + err: ErrExternalParamsRequired, + noErrMessage: "created malformed Provenance (buildDefinition missing requried externalParameters field)", + }, + } + + for name, test := range tests { + got := &Provenance{} + err := protojson.Unmarshal([]byte(test.input), got) + assert.NoError(t, err, fmt.Sprintf("error during JSON unmarshalling in test '%s'", name)) + + err = got.Validate() + assert.ErrorIs(t, err, test.err, fmt.Sprintf("%s in test '%s'", test.noErrMessage, name)) + } +} + +func TestBadProvenanceRunDetails(t *testing.T) { + tests := map[string]struct { + input string + err error + noErrMessage string + }{ + "no runDetails": { + input: `{"buildDefinition":{"buildType":"theBuildType","externalParameters":{"param1":{"subKey":"subVal"}},"resolvedDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"runDetails":{}}`, + err: ErrRunDetailsRequired, + noErrMessage: "created malformed Provenance (empty runDetails)", + }, + "runDetails missing builder required field": { + input: `{"buildDefinition":{"buildType":"theBuildType","externalParameters":{"param1":{"subKey":"subVal"}},"resolvedDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"runDetails":{"builder":{},"metadata":{"invocationId":"theInvocationId"},"byproducts":[{"name":"theResource","digest":{"alg1":"abc123"}}]}}`, + err: ErrBuilderRequired, + noErrMessage: "created malformed Provenance (runDetails missing required builder field)", + }, + "runDetails.builder missing id required field": { + input: `{"buildDefinition":{"buildType":"theBuildType","externalParameters":{"param1":{"subKey":"subVal"}},"resolvedDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"runDetails":{"builder":{"id":"","version":{"theComponent":"v0.1"},"builderDependencies":[{"name":"theResource","digest":{"alg1":"abc123"}}]},"metadata":{"invocationId":"theInvocationId"},"byproducts":[{"name":"theResource","digest":{"alg1":"abc123"}}]}}`, + err: ErrBuilderIdRequired, + noErrMessage: "created malformed Provenance (runDetails.builder missing requried id field)", + }, + } + + for name, test := range tests { + got := &Provenance{} + err := protojson.Unmarshal([]byte(test.input), got) + assert.NoError(t, err, fmt.Sprintf("error during JSON unmarshalling in test '%s'", name)) + + err = got.Validate() + assert.ErrorIs(t, err, test.err, fmt.Sprintf("%s in test '%s'", test.noErrMessage, name)) + } +}