Skip to content
Open
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
3 changes: 0 additions & 3 deletions internal/testing/testdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,15 +571,12 @@ var (
SlsaPredicate: []model.SLSAPredicateInputSpec{
{Key: "slsa.metadata.completeness.environment", Value: "true"},
{Key: "slsa.metadata.buildStartedOn", Value: "2020-08-19T08:38:00Z"},
{Key: "slsa.metadata.completeness.materials", Value: "false"},
{Key: "slsa.buildType", Value: "https://github.com/Attestations/GitHubActionsWorkflow@v1"},
{Key: "slsa.invocation.configSource.entryPoint", Value: "build.yaml:maketgz"},
{Key: "slsa.invocation.configSource.uri", Value: "git+https://github.com/curl/curl-docker@master"},
{Key: "slsa.metadata.reproducible", Value: "false"},
{Key: "slsa.materials.0.uri", Value: "git+https://github.com/curl/curl-docker@master"},
{Key: "slsa.builder.id", Value: "https://github.com/Attestations/GitHubHostedActions@v1"},
{Key: "slsa.invocation.configSource.digest.sha1", Value: "d6525c840a62b398424a78d792f457477135d0cf"},
{Key: "slsa.metadata.completeness.parameters", Value: "false"},
{Key: "slsa.materials.0.digest.sha1", Value: "24279c5185ddc042896e3748f47fa89b48c1c14e"},
{Key: "slsa.materials.1.uri", Value: "github_hosted_vm:ubuntu-18.04:20210123.1"},
{Key: "slsa.materials.1.digest.sha1", Value: "0bcaaa161e719bca41b6d33fc02547c0f97d5397"},
Expand Down
93 changes: 56 additions & 37 deletions pkg/ingestor/parser/slsa/parser_slsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ import (

jsoniter "github.com/json-iterator/go"

slsa01 "github.com/in-toto/attestation/go/predicates/provenance/v01"
slsa02 "github.com/in-toto/attestation/go/predicates/provenance/v02"
slsa1 "github.com/in-toto/attestation/go/predicates/provenance/v1"
attestationv1 "github.com/in-toto/attestation/go/v1"
scommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
slsa01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1"
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
"github.com/jeremywohl/flatten"
"google.golang.org/protobuf/encoding/protojson"

Expand All @@ -46,6 +45,8 @@ import (
// - An IsOccurence input spec which will generate a predicate for each occurence

const PredicateSLSAProvenancev1 = "https://slsa.dev/provenance/v1"
const PredicateSLSAProvenancev01 = "https://slsa.dev/provenance/v0.1"
const PredicateSLSAProvenancev02 = "https://slsa.dev/provenance/v0.2"

var ErrMetadataNil = errors.New("SLSA Metadata is nil")
var ErrBuilderNil = errors.New("SLSA Builder is nil")
Expand All @@ -61,8 +62,8 @@ type slsaEntity struct {
}

type slsaParser struct {
pred01 *slsa01.ProvenancePredicate
pred02 *slsa02.ProvenancePredicate
pred01 *slsa01.Provenance
pred02 *slsa02.Provenance
pred1 *slsa1.Provenance
smt *attestationv1.Statement
subjects []*slsaEntity
Expand All @@ -89,7 +90,6 @@ func (s *slsaParser) initializeSLSAParser() {
s.subjects = make([]*slsaEntity, 0)
s.materials = make([]*slsaEntity, 0)
s.bareMaterials = make([]*model.ArtifactInputSpec, 0)
s.bareMaterials = make([]*model.ArtifactInputSpec, 0)
s.builder = nil
s.slsaAttestation = nil
s.identifierStrings = &common.IdentifierStrings{}
Expand Down Expand Up @@ -131,12 +131,12 @@ func (s *slsaParser) getSubject() error {

func (s *slsaParser) getMaterials() error {
switch s.smt.PredicateType {
case slsa01.PredicateSLSAProvenance:
if err := s.getMaterials0(s.pred01.Materials); err != nil {
case PredicateSLSAProvenancev01:
if err := s.getMaterials01(s.pred01.Materials); err != nil {
return err
}
case slsa02.PredicateSLSAProvenance:
if err := s.getMaterials0(s.pred02.Materials); err != nil {
case PredicateSLSAProvenancev02:
if err := s.getMaterials02(s.pred02.Materials); err != nil {
return err
}
case PredicateSLSAProvenancev1:
Expand Down Expand Up @@ -174,11 +174,24 @@ func (s *slsaParser) getMaterials1(rds []*attestationv1.ResourceDescriptor) erro
return nil
}

func (s *slsaParser) getMaterials0(materials []scommon.ProvenanceMaterial) error {
func (s *slsaParser) getMaterials01(materials []*slsa01.Material) error {
// append dependency nodes for the materials
for _, mat := range materials {
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, mat.Uri)
se, err := getSlsaEntity("", mat.Uri, mat.Digest)
if err != nil {
return err
}
s.materials = append(s.materials, se)
}
return nil
}

func (s *slsaParser) getMaterials02(materials []*slsa02.Material) error {
// append dependency nodes for the materials
for _, mat := range materials {
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, mat.URI)
se, err := getSlsaEntity("", mat.URI, mat.Digest)
s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, mat.Uri)
se, err := getSlsaEntity("", mat.Uri, mat.Digest)
if err != nil {
return err
}
Expand All @@ -187,7 +200,7 @@ func (s *slsaParser) getMaterials0(materials []scommon.ProvenanceMaterial) error
return nil
}

func getArtifacts(digests scommon.DigestSet) []*model.ArtifactInputSpec {
func getArtifacts(digests map[string]string) []*model.ArtifactInputSpec {
var artifacts []*model.ArtifactInputSpec
for alg, ds := range digests {
artifacts = append(artifacts, &model.ArtifactInputSpec{
Expand All @@ -198,7 +211,7 @@ func getArtifacts(digests scommon.DigestSet) []*model.ArtifactInputSpec {
return artifacts
}

func getSlsaEntity(name, uri string, digests scommon.DigestSet) (*slsaEntity, error) {
func getSlsaEntity(name, uri string, digests map[string]string) (*slsaEntity, error) {
artifacts := getArtifacts(digests)
slsa := &slsaEntity{
artifacts: artifacts,
Expand Down Expand Up @@ -231,33 +244,39 @@ func getSlsaEntity(name, uri string, digests scommon.DigestSet) (*slsaEntity, er
return nil, fmt.Errorf("%w unable to get Guac Generic Purl, this should not happen", err)
}

func fillSLSA01(inp *model.SLSAInputSpec, pred *slsa01.ProvenancePredicate) error {
inp.BuildType = pred.Recipe.Type
func fillSLSA01(inp *model.SLSAInputSpec, pred *slsa01.Provenance) error {
if pred.Recipe != nil {
inp.BuildType = pred.Recipe.Type
}

if pred.Metadata == nil {
return ErrMetadataNil
}
if pred.Metadata.BuildStartedOn != nil {
inp.StartedOn = pred.Metadata.BuildStartedOn
startTimePB := time.Unix(pred.Metadata.BuildStartedOn.GetSeconds(), int64(pred.Metadata.BuildStartedOn.GetNanos()))
inp.StartedOn = &startTimePB
}
if pred.Metadata.BuildFinishedOn != nil {
inp.FinishedOn = pred.Metadata.BuildFinishedOn
finishTimePB := time.Unix(pred.Metadata.BuildFinishedOn.GetSeconds(), int64(pred.Metadata.BuildFinishedOn.GetNanos()))
inp.FinishedOn = &finishTimePB
}

return nil
}

func fillSLSA02(inp *model.SLSAInputSpec, pred *slsa02.ProvenancePredicate) error {
func fillSLSA02(inp *model.SLSAInputSpec, pred *slsa02.Provenance) error {
inp.BuildType = pred.BuildType

if pred.Metadata == nil {
return ErrMetadataNil
}
if pred.Metadata.BuildStartedOn != nil {
inp.StartedOn = pred.Metadata.BuildStartedOn
startTimePB := time.Unix(pred.Metadata.BuildStartedOn.GetSeconds(), int64(pred.Metadata.BuildStartedOn.GetNanos()))
inp.StartedOn = &startTimePB
}
if pred.Metadata.BuildFinishedOn != nil {
inp.FinishedOn = pred.Metadata.BuildStartedOn
finishTimePB := time.Unix(pred.Metadata.BuildFinishedOn.GetSeconds(), int64(pred.Metadata.BuildFinishedOn.GetNanos()))
inp.FinishedOn = &finishTimePB
}
return nil
}
Expand All @@ -272,7 +291,7 @@ func fillSLSA1(inp *model.SLSAInputSpec, pred *slsa1.Provenance) error {
inp.StartedOn = &startTimePB
}
if pred.RunDetails.Metadata.FinishedOn != nil {
finishTimePB := time.Unix(pred.RunDetails.Metadata.StartedOn.GetSeconds(), int64(pred.RunDetails.Metadata.StartedOn.GetNanos()))
finishTimePB := time.Unix(pred.RunDetails.Metadata.FinishedOn.GetSeconds(), int64(pred.RunDetails.Metadata.FinishedOn.GetNanos()))
inp.FinishedOn = &finishTimePB
}
return nil
Expand All @@ -286,18 +305,18 @@ func (s *slsaParser) getSLSA() error {
var data []byte
var err error
switch s.smt.PredicateType {
case slsa01.PredicateSLSAProvenance:
case PredicateSLSAProvenancev01:
if err := fillSLSA01(inp, s.pred01); err != nil {
return fmt.Errorf("could not fill SLSA01: %w", err)
}
if data, err = json.Marshal(s.pred01); err != nil {
if data, err = protojson.Marshal(s.pred01); err != nil {
return fmt.Errorf("could not marshal SLSA01: %w", err)
}
case slsa02.PredicateSLSAProvenance:
case PredicateSLSAProvenancev02:
if err := fillSLSA02(inp, s.pred02); err != nil {
return fmt.Errorf("could not fill SLSA02: %w", err)
}
if data, err = json.Marshal(s.pred02); err != nil {
if data, err = protojson.Marshal(s.pred02); err != nil {
return fmt.Errorf("could not marshal SLSA02: %w", err)
}
case PredicateSLSAProvenancev1:
Expand Down Expand Up @@ -335,10 +354,10 @@ func (s *slsaParser) getSLSA() error {
func (s *slsaParser) getBuilder() error {
s.builder = &model.BuilderInputSpec{}
switch s.smt.PredicateType {
case slsa01.PredicateSLSAProvenance:
s.builder.Uri = s.pred01.Builder.ID
case slsa02.PredicateSLSAProvenance:
s.builder.Uri = s.pred02.Builder.ID
case PredicateSLSAProvenancev01:
s.builder.Uri = s.pred01.Builder.Id
case PredicateSLSAProvenancev02:
s.builder.Uri = s.pred02.Builder.Id
case PredicateSLSAProvenancev1:
if s.pred1.RunDetails == nil || s.pred1.RunDetails.Builder == nil {
return ErrBuilderNil
Expand All @@ -360,14 +379,14 @@ func (s *slsaParser) parseSlsaPredicate(p []byte) error {
}

switch s.smt.PredicateType {
case slsa01.PredicateSLSAProvenance:
s.pred01 = &slsa01.ProvenancePredicate{}
if err := json.Unmarshal(predBytes, s.pred01); err != nil {
case PredicateSLSAProvenancev01:
s.pred01 = &slsa01.Provenance{}
if err := protojson.Unmarshal(predBytes, s.pred01); err != nil {
return fmt.Errorf("Could not unmarshal v0.1 SLSA provenance statement : %w", err)
}
case slsa02.PredicateSLSAProvenance:
s.pred02 = &slsa02.ProvenancePredicate{}
if err := json.Unmarshal(predBytes, s.pred02); err != nil {
case PredicateSLSAProvenancev02:
s.pred02 = &slsa02.Provenance{}
if err := protojson.Unmarshal(predBytes, s.pred02); err != nil {
return fmt.Errorf("Could not unmarshal v0.2 SLSA provenance statement : %w", err)
}
case PredicateSLSAProvenancev1:
Expand Down
57 changes: 30 additions & 27 deletions pkg/ingestor/parser/slsa/parser_slsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,18 @@ package slsa

import (
"context"
slsa01 "github.com/in-toto/attestation/go/predicates/provenance/v01"
"reflect"
"testing"
"time"

scommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
slsa01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1"

"github.com/in-toto/in-toto-golang/in_toto"

"github.com/google/go-cmp/cmp"
"github.com/guacsec/guac/internal/testing/testdata"
"github.com/guacsec/guac/pkg/assembler"
model "github.com/guacsec/guac/pkg/assembler/clients/generated"
"github.com/guacsec/guac/pkg/handler/processor"
"github.com/guacsec/guac/pkg/logging"
"google.golang.org/protobuf/types/known/timestamppb"
)

func Test_slsaParser(t *testing.T) {
Expand Down Expand Up @@ -85,9 +82,11 @@ func Test_slsaParser(t *testing.T) {
func Test_fillSLSA01(t *testing.T) {
startTime := time.Now()
endTime := time.Now()
startTimePB := timestamppb.New(startTime)
endTimePB := timestamppb.New(endTime)
type args struct {
inp *model.SLSAInputSpec
stmt *in_toto.ProvenanceStatementSLSA01
stmt *slsa01.Provenance
}
tests := []struct {
name string
Expand All @@ -98,15 +97,13 @@ func Test_fillSLSA01(t *testing.T) {
name: "default",
args: args{
inp: &model.SLSAInputSpec{},
stmt: &in_toto.ProvenanceStatementSLSA01{
Predicate: slsa01.ProvenancePredicate{
Metadata: &slsa01.ProvenanceMetadata{
BuildStartedOn: &startTime,
BuildFinishedOn: &endTime,
},
Recipe: slsa01.ProvenanceRecipe{
Type: "test",
},
stmt: &slsa01.Provenance{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sticking the provenance predicate at the statement layer. Instead, this should be using in-toto/attestation/go/v1/statement and adding slsa01.Provenance as the predicate

Metadata: &slsa01.Metadata{
BuildStartedOn: startTimePB,
BuildFinishedOn: endTimePB,
},
Recipe: &slsa01.Recipe{
Type: "test",
},
},
},
Expand All @@ -115,14 +112,14 @@ func Test_fillSLSA01(t *testing.T) {
name: "stmt predicate metadata is nil",
args: args{
inp: &model.SLSAInputSpec{},
stmt: &in_toto.ProvenanceStatementSLSA01{},
stmt: &slsa01.Provenance{},
},
err: ErrMetadataNil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := fillSLSA01(test.args.inp, &test.args.stmt.Predicate)
err := fillSLSA01(test.args.inp, test.args.stmt)
if err != test.err {
t.Fatalf("fillSLSA01() error = %v, expected error %v", err, test.err)
}
Expand All @@ -133,14 +130,20 @@ func Test_fillSLSA01(t *testing.T) {
return
}

if test.args.inp.BuildType != test.args.stmt.Predicate.Recipe.Type {
t.Errorf("fillSLSA01() inp.BuildType not equal to stmt.Predicate.Recipe.Type")
if test.args.inp.BuildType != test.args.stmt.Recipe.Type {
t.Errorf("fillSLSA01() inp.BuildType not equal to stmt.Recipe.Type")
}
if test.args.inp.StartedOn != test.args.stmt.Predicate.Metadata.BuildStartedOn {
t.Errorf("fillSLSA01() inp.BuildStartedOn not equal to stmt.Predicate.Metadata.BuildStartedOn")
if test.args.stmt.Metadata != nil && test.args.stmt.Metadata.BuildStartedOn != nil {
expectedStartTime := time.Unix(test.args.stmt.Metadata.BuildStartedOn.GetSeconds(), int64(test.args.stmt.Metadata.BuildStartedOn.GetNanos()))
if test.args.inp.StartedOn != nil && !test.args.inp.StartedOn.Equal(expectedStartTime) {
t.Errorf("fillSLSA01() inp.StartedOn not equal to expected time")
}
}
if test.args.inp.FinishedOn != test.args.stmt.Predicate.Metadata.BuildFinishedOn {
t.Errorf("fillSLSA01() inp.BuildFinishedOn not equal to stmt.Predicate.Metadata.BuildFinishedOn")
if test.args.stmt.Metadata != nil && test.args.stmt.Metadata.BuildFinishedOn != nil {
expectedFinishTime := time.Unix(test.args.stmt.Metadata.BuildFinishedOn.GetSeconds(), int64(test.args.stmt.Metadata.BuildFinishedOn.GetNanos()))
if test.args.inp.FinishedOn != nil && !test.args.inp.FinishedOn.Equal(expectedFinishTime) {
t.Errorf("fillSLSA01() inp.FinishedOn not equal to expected time")
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to restore the nesting based on the comment above. It should also check for the subject field

}
})
}
Expand All @@ -155,14 +158,14 @@ func Test_getSlsaEntity(t *testing.T) {
testname string
uri string
name string
digest scommon.DigestSet
digest map[string]string
expected *slsaEntity
wantErr bool
}{
{
testname: "with uri and digest",
uri: "pkg:npm/sigstore/[email protected]",
digest: scommon.DigestSet{
digest: map[string]string{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
expected: &slsaEntity{
Expand All @@ -188,7 +191,7 @@ func Test_getSlsaEntity(t *testing.T) {
{
testname: "with name and digest",
name: "sigstore",
digest: scommon.DigestSet{
digest: map[string]string{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
expected: &slsaEntity{
Expand All @@ -213,7 +216,7 @@ func Test_getSlsaEntity(t *testing.T) {
},
{
testname: "without name and uri",
digest: scommon.DigestSet{
digest: map[string]string{
"sha1": "428601801d1f5d105351a403f58c38269de93f680",
},
wantErr: true,
Expand Down
Loading