-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add validator APIs for Provenance v1 predicate
Signed-off-by: Marcela Melara <[email protected]>
- Loading branch information
1 parent
6af5732
commit aedbff9
Showing
2 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
Validator APIs for SLSA Provenance v1 protos. | ||
*/ | ||
package v1 | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
// 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") | ||
) | ||
|
||
func (b *Builder) Validate() error { | ||
// the id field is required for SLSA Build L1 | ||
if b.GetId() == "" { | ||
return ErrBuilderIdRequired | ||
} | ||
|
||
// check that all builderDependencies are valid RDs | ||
builderDeps := b.GetBuilderDependencies() | ||
if len(builderDeps) > 0 { | ||
for i, rd := range builderDeps { | ||
if err := rd.Validate(); err != nil { | ||
return fmt.Errorf("Invalid Builder.BuilderDependencies[%d]: %w", i, err) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (b *BuildDefinition) Validate() error { | ||
// the buildType field is required for SLSA Build L1 | ||
if b.GetBuildType() == "" { | ||
return ErrBuildTypeRequired | ||
} | ||
|
||
// the externalParameters field is required for SLSA Build L1 | ||
if b.GetExternalParameters() == nil { | ||
return ErrExternalParamsRequired | ||
} | ||
|
||
// check that all resolvedDependencies are valid RDs | ||
resolvedDeps := b.GetResolvedDependencies() | ||
if len(resolvedDeps) > 0 { | ||
for i, rd := range resolvedDeps { | ||
if err := rd.Validate(); err != nil { | ||
return fmt.Errorf("Invalid BuildDefinition.ResolvedDependencies[%d]: %w", i, err) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (r *RunDetails) Validate() error { | ||
// the builder field is required for SLSA Build L1 | ||
builder := r.GetBuilder() | ||
if builder == nil { | ||
return ErrBuilderRequired | ||
} | ||
|
||
// check the Builder | ||
if err := builder.Validate(); err != nil { | ||
return err | ||
} | ||
|
||
// check that all byproducts are valid RDs | ||
byproducts := r.GetByproducts() | ||
if len(byproducts) > 0 { | ||
for i, rd := range byproducts { | ||
if err := rd.Validate(); err != nil { | ||
return fmt.Errorf("Invalid RunDetails.Byproducts[%d]: %w", i, err) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (p *Provenance) Validate() error { | ||
// the buildDefinition field is required for SLSA Build L1 | ||
buildDef := p.GetBuildDefinition() | ||
if buildDef == nil { | ||
return ErrBuildDefinitionRequired | ||
} | ||
|
||
// check the BuildDefinition | ||
if err := buildDef.Validate(); err != nil { | ||
return err | ||
} | ||
|
||
// the runDetails field is required for SLSA Build L1 | ||
runDetails := p.GetRunDetails() | ||
if runDetails == nil { | ||
return ErrRunDetailsRequired | ||
} | ||
|
||
// check the RunDetails | ||
if err := runDetails.Validate(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
Tests for SLSA Provenance v1 protos. | ||
*/ | ||
|
||
package v1 | ||
|
||
import ( | ||
"testing" | ||
|
||
ita1 "github.com/in-toto/attestation/go/v1" | ||
"github.com/stretchr/testify/assert" | ||
"google.golang.org/protobuf/encoding/protojson" | ||
"google.golang.org/protobuf/proto" | ||
"google.golang.org/protobuf/types/known/structpb" | ||
) | ||
|
||
func createTestProvenance(t *testing.T) *Provenance { | ||
// Create a Provenance | ||
|
||
t.Helper() | ||
|
||
rd := &ita1.ResourceDescriptor{ | ||
Name: "theResource", | ||
Digest: map[string]string{"alg1": "abc123"}, | ||
} | ||
|
||
builder := &Builder{ | ||
Id: "theId", | ||
Version: map[string]string{"theComponent": "v0.1"}, | ||
BuilderDependencies: []*ita1.ResourceDescriptor{rd}, | ||
} | ||
|
||
buildMeta := &BuildMetadata{ | ||
InvocationId: "theInvocationId", | ||
} | ||
|
||
runDetails := &RunDetails{ | ||
Builder: builder, | ||
Metadata: buildMeta, | ||
Byproducts: []*ita1.ResourceDescriptor{rd}, | ||
} | ||
|
||
externalParams, err := structpb.NewStruct(map[string]interface{}{ | ||
"param1": map[string]interface{}{ | ||
"subKey": "subVal"}}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
buildDef := &BuildDefinition{ | ||
BuildType: "theBuildType", | ||
ExternalParameters: externalParams, | ||
ResolvedDependencies: []*ita1.ResourceDescriptor{rd}, | ||
} | ||
|
||
return &Provenance{ | ||
BuildDefinition: buildDef, | ||
RunDetails: runDetails, | ||
} | ||
} | ||
|
||
func TestJsonUnmarshalProvenance(t *testing.T) { | ||
var wantSt = `{"buildDefinition":{"buildType":"theBuildType","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"}}]}}` | ||
|
||
got := &Provenance{} | ||
err := protojson.Unmarshal([]byte(wantSt), got) | ||
assert.NoError(t, err, "error during JSON unmarshalling") | ||
|
||
want := createTestProvenance(t) | ||
assert.NoError(t, err, "unexpected error during test Statement creation") | ||
assert.True(t, proto.Equal(got, want), "protos do not match") | ||
} |