From 68fd83b59a5d944432abc9ed426ea53d8cd0a05e Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 23 Apr 2026 09:35:39 +0530 Subject: [PATCH 1/3] fix: remove deprecated in-toto-golang dependency (#2036) Signed-off-by: Abhishek --- go.mod | 2 - go.sum | 4 - pkg/handler/processor/guesser/type_ite6.go | 57 +++++---- pkg/handler/processor/ite6/ite6.go | 11 +- pkg/ingestor/parser/slsa/parser_slsa.go | 112 ++++++++++++++---- pkg/ingestor/parser/slsa/parser_slsa_test.go | 41 +++---- .../sigstore_verifier_test.go | 80 +++++++++---- 7 files changed, 210 insertions(+), 97 deletions(-) diff --git a/go.mod b/go.mod index 661cd77706..030855d4f9 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.26.0 require ( cloud.google.com/go/storage v1.61.3 github.com/fsouza/fake-gcs-server v1.54.0 - github.com/in-toto/in-toto-golang v0.10.0 github.com/secure-systems-lab/go-securesystemslib v0.10.0 github.com/spf13/cobra v1.10.2 go.uber.org/zap v1.27.1 @@ -27,7 +26,6 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pkg/xattr v0.4.12 // indirect - github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/spf13/pflag v1.0.10 go.opencensus.io v0.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index 63e9c2fb66..db7bf22654 100644 --- a/go.sum +++ b/go.sum @@ -918,8 +918,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20250628045327-2d64ad6b7ec5 h1:QCtizt3 github.com/ianlancetaylor/demangle v0.0.0-20250628045327-2d64ad6b7ec5/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= -github.com/in-toto/in-toto-golang v0.10.0 h1:+s2eZQSK3WmWfYV85qXVSBfqgawi/5L02MaqA4o/tpM= -github.com/in-toto/in-toto-golang v0.10.0/go.mod h1:wjT4RiyFlLWCmLUJjwB8oZcjaq7HA390aMJcD3xXgmg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -1328,8 +1326,6 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= -github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= diff --git a/pkg/handler/processor/guesser/type_ite6.go b/pkg/handler/processor/guesser/type_ite6.go index 42f01fca91..44853c4e6a 100644 --- a/pkg/handler/processor/guesser/type_ite6.go +++ b/pkg/handler/processor/guesser/type_ite6.go @@ -21,40 +21,53 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/guacsec/guac/pkg/handler/processor" - attestationv1 "github.com/in-toto/attestation/go/v1" - "github.com/in-toto/in-toto-golang/in_toto" ) var json = jsoniter.ConfigCompatibleWithStandardLibrary +// ite6Statement supports unmarshalling both v0.1 ("_type", "predicateType") +// and v1 ("type", "predicate_type") in-toto statement formats. +type ite6Statement struct { + TypeV01 string `json:"_type"` + PredicateTypeV01 string `json:"predicateType"` + TypeV1 string `json:"type"` + PredicateTypeV1 string `json:"predicate_type"` +} + +// getType returns the statement type from whichever format was used. +func (s *ite6Statement) getType() string { + if s.TypeV01 != "" { + return s.TypeV01 + } + return s.TypeV1 +} + +// getPredicateType returns the predicate type from whichever format was used. +func (s *ite6Statement) getPredicateType() string { + if s.PredicateTypeV01 != "" { + return s.PredicateTypeV01 + } + return s.PredicateTypeV1 +} + type ite6TypeGuesser struct{} func (_ *ite6TypeGuesser) GuessDocumentType(blob []byte, format processor.FormatType) processor.DocumentType { - var statement in_toto.Statement + var statement ite6Statement if json.Unmarshal(blob, &statement) == nil && format == processor.FormatJSON { - if strings.HasPrefix(statement.Type, "https://in-toto.io/Statement") { - if strings.HasPrefix(statement.PredicateType, "https://slsa.dev/provenance") { + stmtType := statement.getType() + predicateType := statement.getPredicateType() + if strings.HasPrefix(stmtType, "https://in-toto.io/Statement") { + if strings.HasPrefix(predicateType, "https://slsa.dev/provenance") { return processor.DocumentITE6SLSA - } else if strings.HasPrefix(statement.PredicateType, "https://crev.dev/in-toto-scheme") { + } else if strings.HasPrefix(predicateType, "https://crev.dev/in-toto-scheme") { return processor.DocumentITE6Generic - } else if strings.HasPrefix(statement.PredicateType, "https://in-toto.io/attestation/certify/v0.1") { + } else if strings.HasPrefix(predicateType, "https://in-toto.io/attestation/certify/v0.1") { return processor.DocumentITE6Generic - } else if strings.HasPrefix(statement.PredicateType, "https://in-toto.io/attestation/vulns/v0.1") || - strings.HasPrefix(statement.PredicateType, "https://in-toto.io/attestation/vulns/v0.2") { - return processor.DocumentITE6Vul - } else if strings.HasPrefix(statement.PredicateType, "https://in-toto.io/attestation/clearlydefined/v0.1") { - return processor.DocumentITE6ClearlyDefined - } - return processor.DocumentITE6Generic - } - } - var attV1Statement attestationv1.Statement - if json.Unmarshal(blob, &attV1Statement) == nil && format == processor.FormatJSON { - if strings.HasPrefix(attV1Statement.Type, "https://in-toto.io/Statement") { - if strings.HasPrefix(attV1Statement.PredicateType, "https://in-toto.io/attestation/vulns/v0.1") || - strings.HasPrefix(attV1Statement.PredicateType, "https://in-toto.io/attestation/vulns/v0.2") { + } else if strings.HasPrefix(predicateType, "https://in-toto.io/attestation/vulns/v0.1") || + strings.HasPrefix(predicateType, "https://in-toto.io/attestation/vulns/v0.2") { return processor.DocumentITE6Vul - } else if strings.HasPrefix(attV1Statement.PredicateType, "https://in-toto.io/attestation/clearlydefined/v0.1") { + } else if strings.HasPrefix(predicateType, "https://in-toto.io/attestation/clearlydefined/v0.1") { return processor.DocumentITE6ClearlyDefined } return processor.DocumentITE6Generic diff --git a/pkg/handler/processor/ite6/ite6.go b/pkg/handler/processor/ite6/ite6.go index b974ebafdc..786a938065 100644 --- a/pkg/handler/processor/ite6/ite6.go +++ b/pkg/handler/processor/ite6/ite6.go @@ -21,11 +21,16 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/guacsec/guac/pkg/handler/processor" - "github.com/in-toto/in-toto-golang/in_toto" ) var json = jsoniter.ConfigCompatibleWithStandardLibrary +// ite6Statement is used for unmarshalling in-toto statement headers. +type ite6Statement struct { + Type string `json:"_type"` + PredicateType string `json:"predicateType"` +} + type ITE6Processor struct { } @@ -53,8 +58,8 @@ func (e *ITE6Processor) Unpack(i *processor.Document) ([]*processor.Document, er return nil, nil } -func parseStatement(p []byte) (*in_toto.Statement, error) { - ps := in_toto.Statement{} +func parseStatement(p []byte) (*ite6Statement, error) { + ps := ite6Statement{} if err := json.Unmarshal(p, &ps); err != nil { return nil, err } diff --git a/pkg/ingestor/parser/slsa/parser_slsa.go b/pkg/ingestor/parser/slsa/parser_slsa.go index 464cbe6372..e0c557a0d7 100644 --- a/pkg/ingestor/parser/slsa/parser_slsa.go +++ b/pkg/ingestor/parser/slsa/parser_slsa.go @@ -27,9 +27,6 @@ import ( 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" @@ -45,7 +42,80 @@ import ( // - a pkg or source depending on what is represented by the name/URI // - An IsOccurrence input spec which will generate a predicate for each occurrence -const PredicateSLSAProvenancev1 = "https://slsa.dev/provenance/v1" +// DigestSet is a set of digests keyed by algorithm name (e.g. "sha256"). +type DigestSet = map[string]string + +// ProvenanceMaterial represents a material used in a provenance attestation. +type ProvenanceMaterial struct { + URI string `json:"uri"` + Digest DigestSet `json:"digest,omitempty"` +} + +// ProvenanceBuilder identifies the entity that executed the build steps. +type ProvenanceBuilder struct { + ID string `json:"id"` +} + +const ( + PredicateSLSAProvenanceV01 = "https://slsa.dev/provenance/v0.1" + PredicateSLSAProvenanceV02 = "https://slsa.dev/provenance/v0.2" + + // PredicateSLSAProvenancev1 is the predicate type for SLSAv1.0 provenance. + PredicateSLSAProvenancev1 = "https://slsa.dev/provenance/v1" +) + +// ProvenancePredicateV01 is the SLSA v0.1 provenance predicate. +type ProvenancePredicateV01 struct { + Builder ProvenanceBuilder `json:"builder"` + Recipe ProvenanceRecipe `json:"recipe"` + Metadata *ProvenanceMetadataV01 `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} + +// ProvenanceRecipe describes how the artifact was produced (SLSA v0.1). +type ProvenanceRecipe struct { + Type string `json:"type"` + DefinedInMaterial *int `json:"definedInMaterial,omitempty"` + EntryPoint string `json:"entryPoint,omitempty"` + Arguments interface{} `json:"arguments,omitempty"` + Environment interface{} `json:"environment,omitempty"` +} + +// ProvenanceMetadataV01 holds build metadata for SLSA v0.1 provenance. +type ProvenanceMetadataV01 struct { + BuildInvocationID string `json:"buildInvocationId,omitempty"` + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness struct { + Arguments bool `json:"arguments"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` + } `json:"completeness"` + Reproducible bool `json:"reproducible"` +} + +// ProvenancePredicateV02 is the SLSA v0.2 provenance predicate. +type ProvenancePredicateV02 struct { + Builder ProvenanceBuilder `json:"builder"` + BuildType string `json:"buildType"` + Invocation interface{} `json:"invocation,omitempty"` + BuildConfig interface{} `json:"buildConfig,omitempty"` + Metadata *ProvenanceMetadataV02 `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} + +// ProvenanceMetadataV02 holds build metadata for SLSA v0.2 provenance. +type ProvenanceMetadataV02 struct { + BuildInvocationID string `json:"buildInvocationId,omitempty"` + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness struct { + Parameters bool `json:"parameters"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` + } `json:"completeness"` + Reproducible bool `json:"reproducible"` +} var ErrMetadataNil = errors.New("SLSA Metadata is nil") var ErrBuilderNil = errors.New("SLSA Builder is nil") @@ -61,8 +131,8 @@ type slsaEntity struct { } type slsaParser struct { - pred01 *slsa01.ProvenancePredicate - pred02 *slsa02.ProvenancePredicate + pred01 *ProvenancePredicateV01 + pred02 *ProvenancePredicateV02 pred1 *slsa1.Provenance smt *attestationv1.Statement subjects []*slsaEntity @@ -131,11 +201,11 @@ func (s *slsaParser) getSubject() error { func (s *slsaParser) getMaterials() error { switch s.smt.PredicateType { - case slsa01.PredicateSLSAProvenance: + case PredicateSLSAProvenanceV01: if err := s.getMaterials0(s.pred01.Materials); err != nil { return err } - case slsa02.PredicateSLSAProvenance: + case PredicateSLSAProvenanceV02: if err := s.getMaterials0(s.pred02.Materials); err != nil { return err } @@ -174,7 +244,7 @@ func (s *slsaParser) getMaterials1(rds []*attestationv1.ResourceDescriptor) erro return nil } -func (s *slsaParser) getMaterials0(materials []scommon.ProvenanceMaterial) error { +func (s *slsaParser) getMaterials0(materials []ProvenanceMaterial) error { // append dependency nodes for the materials for _, mat := range materials { s.identifierStrings.UnclassifiedStrings = append(s.identifierStrings.UnclassifiedStrings, mat.URI) @@ -187,7 +257,7 @@ func (s *slsaParser) getMaterials0(materials []scommon.ProvenanceMaterial) error return nil } -func getArtifacts(digests scommon.DigestSet) []*model.ArtifactInputSpec { +func getArtifacts(digests DigestSet) []*model.ArtifactInputSpec { var artifacts []*model.ArtifactInputSpec for alg, ds := range digests { artifacts = append(artifacts, &model.ArtifactInputSpec{ @@ -198,7 +268,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 DigestSet) (*slsaEntity, error) { artifacts := getArtifacts(digests) slsa := &slsaEntity{ artifacts: artifacts, @@ -231,7 +301,7 @@ 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 { +func fillSLSA01(inp *model.SLSAInputSpec, pred *ProvenancePredicateV01) error { inp.BuildType = pred.Recipe.Type if pred.Metadata == nil { @@ -247,7 +317,7 @@ func fillSLSA01(inp *model.SLSAInputSpec, pred *slsa01.ProvenancePredicate) erro return nil } -func fillSLSA02(inp *model.SLSAInputSpec, pred *slsa02.ProvenancePredicate) error { +func fillSLSA02(inp *model.SLSAInputSpec, pred *ProvenancePredicateV02) error { inp.BuildType = pred.BuildType if pred.Metadata == nil { @@ -286,14 +356,14 @@ 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 { 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) } @@ -335,9 +405,9 @@ func (s *slsaParser) getSLSA() error { func (s *slsaParser) getBuilder() error { s.builder = &model.BuilderInputSpec{} switch s.smt.PredicateType { - case slsa01.PredicateSLSAProvenance: + case PredicateSLSAProvenanceV01: s.builder.Uri = s.pred01.Builder.ID - case slsa02.PredicateSLSAProvenance: + case PredicateSLSAProvenanceV02: s.builder.Uri = s.pred02.Builder.ID case PredicateSLSAProvenancev1: if s.pred1.RunDetails == nil || s.pred1.RunDetails.Builder == nil { @@ -360,13 +430,13 @@ func (s *slsaParser) parseSlsaPredicate(p []byte) error { } switch s.smt.PredicateType { - case slsa01.PredicateSLSAProvenance: - s.pred01 = &slsa01.ProvenancePredicate{} + case PredicateSLSAProvenanceV01: + s.pred01 = &ProvenancePredicateV01{} if err := json.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{} + case PredicateSLSAProvenanceV02: + s.pred02 = &ProvenancePredicateV02{} if err := json.Unmarshal(predBytes, s.pred02); err != nil { return fmt.Errorf("Could not unmarshal v0.2 SLSA provenance statement : %w", err) } diff --git a/pkg/ingestor/parser/slsa/parser_slsa_test.go b/pkg/ingestor/parser/slsa/parser_slsa_test.go index 722975c00e..083e841bad 100644 --- a/pkg/ingestor/parser/slsa/parser_slsa_test.go +++ b/pkg/ingestor/parser/slsa/parser_slsa_test.go @@ -21,11 +21,6 @@ import ( "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" @@ -87,7 +82,7 @@ func Test_fillSLSA01(t *testing.T) { endTime := time.Now() type args struct { inp *model.SLSAInputSpec - stmt *in_toto.ProvenanceStatementSLSA01 + pred *ProvenancePredicateV01 } tests := []struct { name string @@ -98,15 +93,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", - }, + pred: &ProvenancePredicateV01{ + Metadata: &ProvenanceMetadataV01{ + BuildStartedOn: &startTime, + BuildFinishedOn: &endTime, + }, + Recipe: ProvenanceRecipe{ + Type: "test", }, }, }, @@ -115,14 +108,14 @@ func Test_fillSLSA01(t *testing.T) { name: "stmt predicate metadata is nil", args: args{ inp: &model.SLSAInputSpec{}, - stmt: &in_toto.ProvenanceStatementSLSA01{}, + pred: &ProvenancePredicateV01{}, }, 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.pred) if err != test.err { t.Fatalf("fillSLSA01() error = %v, expected error %v", err, test.err) } @@ -133,13 +126,13 @@ func Test_fillSLSA01(t *testing.T) { return } - if test.args.inp.BuildType != test.args.stmt.Predicate.Recipe.Type { + if test.args.inp.BuildType != test.args.pred.Recipe.Type { t.Errorf("fillSLSA01() inp.BuildType not equal to stmt.Predicate.Recipe.Type") } - if test.args.inp.StartedOn != test.args.stmt.Predicate.Metadata.BuildStartedOn { + if test.args.inp.StartedOn != test.args.pred.Metadata.BuildStartedOn { t.Errorf("fillSLSA01() inp.BuildStartedOn not equal to stmt.Predicate.Metadata.BuildStartedOn") } - if test.args.inp.FinishedOn != test.args.stmt.Predicate.Metadata.BuildFinishedOn { + if test.args.inp.FinishedOn != test.args.pred.Metadata.BuildFinishedOn { t.Errorf("fillSLSA01() inp.BuildFinishedOn not equal to stmt.Predicate.Metadata.BuildFinishedOn") } }) @@ -155,14 +148,14 @@ func Test_getSlsaEntity(t *testing.T) { testname string uri string name string - digest scommon.DigestSet + digest DigestSet expected *slsaEntity wantErr bool }{ { testname: "with uri and digest", uri: "pkg:npm/sigstore/sigstore-js@4.2.0", - digest: scommon.DigestSet{ + digest: DigestSet{ "sha1": "428601801d1f5d105351a403f58c38269de93f680", }, expected: &slsaEntity{ @@ -188,7 +181,7 @@ func Test_getSlsaEntity(t *testing.T) { { testname: "with name and digest", name: "sigstore", - digest: scommon.DigestSet{ + digest: DigestSet{ "sha1": "428601801d1f5d105351a403f58c38269de93f680", }, expected: &slsaEntity{ @@ -213,7 +206,7 @@ func Test_getSlsaEntity(t *testing.T) { }, { testname: "without name and uri", - digest: scommon.DigestSet{ + digest: DigestSet{ "sha1": "428601801d1f5d105351a403f58c38269de93f680", }, wantErr: true, diff --git a/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go b/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go index 54bfb9e3f1..fd14ac2b06 100644 --- a/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go +++ b/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go @@ -33,13 +33,51 @@ import ( "github.com/guacsec/guac/pkg/ingestor/key" "github.com/guacsec/guac/pkg/ingestor/verifier" "github.com/guacsec/guac/pkg/logging" - "github.com/in-toto/in-toto-golang/in_toto" - "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/dsse" ) +// PayloadType is the in-toto DSSE payload type. +const PayloadType = "application/vnd.in-toto+json" + +// StatementInTotoV01 is the in-toto v0.1 statement type. +const StatementInTotoV01 = "https://in-toto.io/Statement/v0.1" + +// predicateSLSAProvenanceV02 is the predicate type for SLSAv0.2 provenance. +const predicateSLSAProvenanceV02 = "https://slsa.dev/provenance/v0.2" + +// digestSet is a set of digests keyed by algorithm name. +type digestSet = map[string]string + +// provenanceBuilder identifies the entity that executed the build steps. +type provenanceBuilder struct { + ID string `json:"id"` +} + +// subject describes the set of software artifacts the statement applies to. +type subject struct { + Name string `json:"name"` + Digest digestSet `json:"digest"` +} + +// statementHeader defines the common fields for all statements. +type statementHeader struct { + Type string `json:"_type"` + PredicateType string `json:"predicateType"` + Subject []subject `json:"subject"` +} + +// provenancePredicate is the SLSA v0.2 provenance predicate (simplified for testing). +type provenancePredicate struct { + Builder provenanceBuilder `json:"builder"` +} + +// provenanceStatement is the definition for an entire provenance statement (for testing). +type provenanceStatement struct { + statementHeader + Predicate provenancePredicate `json:"predicate"` +} + type mockKeyProvider struct { collector map[string]key.Key } @@ -105,21 +143,21 @@ func TestSigstoreVerifier_Verify(t *testing.T) { d := randomData(t, 10) id := base64.StdEncoding.EncodeToString(d) - it := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + it := provenanceStatement{ + statementHeader: statementHeader{ + Type: StatementInTotoV01, + PredicateType: predicateSLSAProvenanceV02, + Subject: []subject{ { Name: "foobar", - Digest: common.DigestSet{ + Digest: digestSet{ "foo": "bar", }, }, }, }, - Predicate: slsa.ProvenancePredicate{ - Builder: common.ProvenanceBuilder{ + Predicate: provenancePredicate{ + Builder: provenanceBuilder{ ID: "foo" + id, }, }, @@ -140,7 +178,7 @@ func TestSigstoreVerifier_Verify(t *testing.T) { if err != nil { t.Fatal(err) } - dsseSigner := dsse.WrapSigner(signer, in_toto.PayloadType) + dsseSigner := dsse.WrapSigner(signer, PayloadType) env, err := dsseSigner.SignMessage(bytes.NewReader(b)) if err != nil { @@ -218,21 +256,21 @@ func TestMultiSignatureSigstoreVerifier_Verify(t *testing.T) { d := randomData(t, 10) id := base64.StdEncoding.EncodeToString(d) - it := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + it := provenanceStatement{ + statementHeader: statementHeader{ + Type: StatementInTotoV01, + PredicateType: predicateSLSAProvenanceV02, + Subject: []subject{ { Name: "foobar", - Digest: common.DigestSet{ + Digest: digestSet{ "foo": "bar", }, }, }, }, - Predicate: slsa.ProvenancePredicate{ - Builder: common.ProvenanceBuilder{ + Predicate: provenancePredicate{ + Builder: provenanceBuilder{ ID: "foo" + id, }, }, @@ -265,7 +303,7 @@ func TestMultiSignatureSigstoreVerifier_Verify(t *testing.T) { t.Fatal(err) } - dsseSigner := dsse.WrapMultiSigner(in_toto.PayloadType, signECDSA, signRSA) + dsseSigner := dsse.WrapMultiSigner(PayloadType, signECDSA, signRSA) env, err := dsseSigner.SignMessage(bytes.NewReader(b)) if err != nil { From ae767d5c5ad324744c97d88f1c930d872e00789a Mon Sep 17 00:00:00 2001 From: Abhishek Date: Mon, 4 May 2026 05:48:44 +0530 Subject: [PATCH 2/3] fix: address review feedback for in-toto-golang removal Signed-off-by: Abhishek --- pkg/handler/processor/guesser/type_ite6.go | 25 +++-- .../processor/guesser/type_ite6_test.go | 15 +++ pkg/handler/processor/ite6/ite6.go | 13 ++- pkg/ingestor/parser/slsa/parser_slsa.go | 81 ++------------ pkg/ingestor/parser/slsa/types_legacy.go | 101 ++++++++++++++++++ .../sigstore_verifier_test.go | 23 +++- 6 files changed, 171 insertions(+), 87 deletions(-) create mode 100644 pkg/ingestor/parser/slsa/types_legacy.go diff --git a/pkg/handler/processor/guesser/type_ite6.go b/pkg/handler/processor/guesser/type_ite6.go index 44853c4e6a..fcedcb2b23 100644 --- a/pkg/handler/processor/guesser/type_ite6.go +++ b/pkg/handler/processor/guesser/type_ite6.go @@ -35,19 +35,32 @@ type ite6Statement struct { } // getType returns the statement type from whichever format was used. +// in-toto v1 is the current standard; v0.1 is supported for backwards +// compatibility. A document that populates both _type and type is malformed. func (s *ite6Statement) getType() string { - if s.TypeV01 != "" { - return s.TypeV01 + if s.TypeV1 != "" && s.TypeV01 != "" { + // Both fields set: reject the ambiguous/malformed document. + return "" } - return s.TypeV1 + if s.TypeV1 != "" { + return s.TypeV1 + } + return s.TypeV01 } // getPredicateType returns the predicate type from whichever format was used. +// in-toto v1 is the current standard; v0.1 is supported for backwards +// compatibility. A document that populates both predicateType and predicate_type +// is malformed. func (s *ite6Statement) getPredicateType() string { - if s.PredicateTypeV01 != "" { - return s.PredicateTypeV01 + if s.PredicateTypeV1 != "" && s.PredicateTypeV01 != "" { + // Both fields set: reject the ambiguous/malformed document. + return "" + } + if s.PredicateTypeV1 != "" { + return s.PredicateTypeV1 } - return s.PredicateTypeV1 + return s.PredicateTypeV01 } type ite6TypeGuesser struct{} diff --git a/pkg/handler/processor/guesser/type_ite6_test.go b/pkg/handler/processor/guesser/type_ite6_test.go index 7cd05c36ec..d045004d99 100644 --- a/pkg/handler/processor/guesser/type_ite6_test.go +++ b/pkg/handler/processor/guesser/type_ite6_test.go @@ -35,14 +35,29 @@ func Test_Ite6TypeGuesser(t *testing.T) { name: "valid ITE6 Document", blob: []byte(`{"_type": "https://in-toto.io/Statement/v0.1"}`), expected: processor.DocumentITE6Generic, + }, { + name: "valid ITE6 v1 Document", + blob: []byte(`{"type": "https://in-toto.io/Statement/v1"}`), + expected: processor.DocumentITE6Generic, }, { name: "valid SLSA ITE6 Document", blob: []byte(`{"_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2"}`), expected: processor.DocumentITE6SLSA, + }, { + name: "valid SLSA ITE6 v1 Document", + blob: []byte(`{"type": "https://in-toto.io/Statement/v1", "predicate_type": "https://slsa.dev/provenance/v0.2"}`), + expected: processor.DocumentITE6SLSA, }, { name: "valid SLSA ITE6 Document with different versions", blob: []byte(`{"_type": "https://in-toto.io/Statement/v1.1", "predicateType": "https://slsa.dev/provenance/v1.0"}`), expected: processor.DocumentITE6SLSA, + }, { + // A document that populates both v0.1 and v1 type fields is malformed and + // must be rejected (returns DocumentUnknown) rather than silently + // preferring one format over the other. + name: "ambiguous ITE6 Document with both _type and type fields", + blob: []byte(`{"_type": "https://in-toto.io/Statement/v0.1", "type": "https://in-toto.io/Statement/v1", "predicateType": "https://slsa.dev/provenance/v0.2", "predicate_type": "https://slsa.dev/provenance/v0.2"}`), + expected: processor.DocumentUnknown, }, { name: "valid CREV ITE6 Document", blob: testdata.ITE6CREVExample, diff --git a/pkg/handler/processor/ite6/ite6.go b/pkg/handler/processor/ite6/ite6.go index 786a938065..2b6994bb92 100644 --- a/pkg/handler/processor/ite6/ite6.go +++ b/pkg/handler/processor/ite6/ite6.go @@ -26,9 +26,14 @@ import ( var json = jsoniter.ConfigCompatibleWithStandardLibrary // ite6Statement is used for unmarshalling in-toto statement headers. +// It handles both v0.1 (_type/predicateType) and v1 (type/predicate_type) +// in-toto statement formats. Exactly one set of fields should be populated; +// if neither is set after unmarshalling, the document is rejected with an error. type ite6Statement struct { - Type string `json:"_type"` - PredicateType string `json:"predicateType"` + TypeV01 string `json:"_type"` + PredicateTypeV01 string `json:"predicateType"` + TypeV1 string `json:"type"` + PredicateTypeV1 string `json:"predicate_type"` } type ITE6Processor struct { @@ -63,5 +68,9 @@ func parseStatement(p []byte) (*ite6Statement, error) { if err := json.Unmarshal(p, &ps); err != nil { return nil, err } + // Reject documents where neither format's fields are populated. + if ps.TypeV01 == "" && ps.TypeV1 == "" { + return nil, fmt.Errorf("in-toto statement has neither v0.1 (_type) nor v1 (type) statement type field") + } return &ps, nil } diff --git a/pkg/ingestor/parser/slsa/parser_slsa.go b/pkg/ingestor/parser/slsa/parser_slsa.go index e0c557a0d7..2bd547b6aa 100644 --- a/pkg/ingestor/parser/slsa/parser_slsa.go +++ b/pkg/ingestor/parser/slsa/parser_slsa.go @@ -19,9 +19,7 @@ import ( "context" "errors" "fmt" - "strings" - "time" jsoniter "github.com/json-iterator/go" @@ -42,81 +40,14 @@ import ( // - a pkg or source depending on what is represented by the name/URI // - An IsOccurrence input spec which will generate a predicate for each occurrence -// DigestSet is a set of digests keyed by algorithm name (e.g. "sha256"). -type DigestSet = map[string]string - -// ProvenanceMaterial represents a material used in a provenance attestation. -type ProvenanceMaterial struct { - URI string `json:"uri"` - Digest DigestSet `json:"digest,omitempty"` -} - -// ProvenanceBuilder identifies the entity that executed the build steps. -type ProvenanceBuilder struct { - ID string `json:"id"` -} - const ( PredicateSLSAProvenanceV01 = "https://slsa.dev/provenance/v0.1" PredicateSLSAProvenanceV02 = "https://slsa.dev/provenance/v0.2" - // PredicateSLSAProvenancev1 is the predicate type for SLSAv1.0 provenance. - PredicateSLSAProvenancev1 = "https://slsa.dev/provenance/v1" + // PredicateSLSAProvenanceV1 is the predicate type for SLSAv1.0 provenance. + PredicateSLSAProvenanceV1 = "https://slsa.dev/provenance/v1" ) -// ProvenancePredicateV01 is the SLSA v0.1 provenance predicate. -type ProvenancePredicateV01 struct { - Builder ProvenanceBuilder `json:"builder"` - Recipe ProvenanceRecipe `json:"recipe"` - Metadata *ProvenanceMetadataV01 `json:"metadata,omitempty"` - Materials []ProvenanceMaterial `json:"materials,omitempty"` -} - -// ProvenanceRecipe describes how the artifact was produced (SLSA v0.1). -type ProvenanceRecipe struct { - Type string `json:"type"` - DefinedInMaterial *int `json:"definedInMaterial,omitempty"` - EntryPoint string `json:"entryPoint,omitempty"` - Arguments interface{} `json:"arguments,omitempty"` - Environment interface{} `json:"environment,omitempty"` -} - -// ProvenanceMetadataV01 holds build metadata for SLSA v0.1 provenance. -type ProvenanceMetadataV01 struct { - BuildInvocationID string `json:"buildInvocationId,omitempty"` - BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` - BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` - Completeness struct { - Arguments bool `json:"arguments"` - Environment bool `json:"environment"` - Materials bool `json:"materials"` - } `json:"completeness"` - Reproducible bool `json:"reproducible"` -} - -// ProvenancePredicateV02 is the SLSA v0.2 provenance predicate. -type ProvenancePredicateV02 struct { - Builder ProvenanceBuilder `json:"builder"` - BuildType string `json:"buildType"` - Invocation interface{} `json:"invocation,omitempty"` - BuildConfig interface{} `json:"buildConfig,omitempty"` - Metadata *ProvenanceMetadataV02 `json:"metadata,omitempty"` - Materials []ProvenanceMaterial `json:"materials,omitempty"` -} - -// ProvenanceMetadataV02 holds build metadata for SLSA v0.2 provenance. -type ProvenanceMetadataV02 struct { - BuildInvocationID string `json:"buildInvocationId,omitempty"` - BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` - BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` - Completeness struct { - Parameters bool `json:"parameters"` - Environment bool `json:"environment"` - Materials bool `json:"materials"` - } `json:"completeness"` - Reproducible bool `json:"reproducible"` -} - var ErrMetadataNil = errors.New("SLSA Metadata is nil") var ErrBuilderNil = errors.New("SLSA Builder is nil") var json = jsoniter.ConfigCompatibleWithStandardLibrary @@ -209,7 +140,7 @@ func (s *slsaParser) getMaterials() error { if err := s.getMaterials0(s.pred02.Materials); err != nil { return err } - case PredicateSLSAProvenancev1: + case PredicateSLSAProvenanceV1: if s.pred1.BuildDefinition == nil { return errors.New("SLSA1 buildDefinition is nil") } @@ -370,7 +301,7 @@ func (s *slsaParser) getSLSA() error { if data, err = json.Marshal(s.pred02); err != nil { return fmt.Errorf("could not marshal SLSA02: %w", err) } - case PredicateSLSAProvenancev1: + case PredicateSLSAProvenanceV1: if err := fillSLSA1(inp, s.pred1); err != nil { return fmt.Errorf("could not fill SLSA1: %w", err) } @@ -409,7 +340,7 @@ func (s *slsaParser) getBuilder() error { s.builder.Uri = s.pred01.Builder.ID case PredicateSLSAProvenanceV02: s.builder.Uri = s.pred02.Builder.ID - case PredicateSLSAProvenancev1: + case PredicateSLSAProvenanceV1: if s.pred1.RunDetails == nil || s.pred1.RunDetails.Builder == nil { return ErrBuilderNil } @@ -440,7 +371,7 @@ func (s *slsaParser) parseSlsaPredicate(p []byte) error { if err := json.Unmarshal(predBytes, s.pred02); err != nil { return fmt.Errorf("Could not unmarshal v0.2 SLSA provenance statement : %w", err) } - case PredicateSLSAProvenancev1: + case PredicateSLSAProvenanceV1: s.pred1 = &slsa1.Provenance{} if err := protojson.Unmarshal(predBytes, s.pred1); err != nil { return fmt.Errorf("Could not unmarshal v1.0 SLSA provenance statement : %w", err) diff --git a/pkg/ingestor/parser/slsa/types_legacy.go b/pkg/ingestor/parser/slsa/types_legacy.go new file mode 100644 index 0000000000..daeeb98d22 --- /dev/null +++ b/pkg/ingestor/parser/slsa/types_legacy.go @@ -0,0 +1,101 @@ +// +// Copyright 2022 The GUAC Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package slsa + +import "time" + +// This file contains local copies of types originally defined in the deprecated +// github.com/in-toto/in-toto-golang library. They are kept here to support +// SLSA v0.1 and v0.2 provenance parsing until those formats are dropped. +// +// Original definitions can be found in: +// https://github.com/in-toto/in-toto-golang +// +// TODO: delete this file when SLSA v0.1/v0.2 support is dropped. + +// DigestSet is a set of digests keyed by algorithm name (e.g. "sha256"). +type DigestSet = map[string]string + +// ProvenanceMaterial represents a material used in a provenance attestation. +type ProvenanceMaterial struct { + URI string `json:"uri"` + Digest DigestSet `json:"digest,omitempty"` +} + +// ProvenanceBuilder identifies the entity that executed the build steps. +type ProvenanceBuilder struct { + ID string `json:"id"` +} + +// ProvenanceRecipe describes how the artifact was produced (SLSA v0.1). +type ProvenanceRecipe struct { + Type string `json:"type"` + DefinedInMaterial *int `json:"definedInMaterial,omitempty"` + EntryPoint string `json:"entryPoint,omitempty"` + Arguments interface{} `json:"arguments,omitempty"` + Environment interface{} `json:"environment,omitempty"` +} + +// CompletenessV01 tracks which fields are complete for SLSA v0.1 metadata. +type CompletenessV01 struct { + Arguments bool `json:"arguments"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` +} + +// ProvenanceMetadataV01 holds build metadata for SLSA v0.1 provenance. +type ProvenanceMetadataV01 struct { + BuildInvocationID string `json:"buildInvocationId,omitempty"` + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness CompletenessV01 `json:"completeness"` + Reproducible bool `json:"reproducible"` +} + +// ProvenancePredicateV01 is the SLSA v0.1 provenance predicate. +type ProvenancePredicateV01 struct { + Builder ProvenanceBuilder `json:"builder"` + Recipe ProvenanceRecipe `json:"recipe"` + Metadata *ProvenanceMetadataV01 `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} + +// CompletenessV02 tracks which fields are complete for SLSA v0.2 metadata. +// Note: field set differs from CompletenessV01 (Parameters instead of Arguments). +type CompletenessV02 struct { + Parameters bool `json:"parameters"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` +} + +// ProvenanceMetadataV02 holds build metadata for SLSA v0.2 provenance. +type ProvenanceMetadataV02 struct { + BuildInvocationID string `json:"buildInvocationId,omitempty"` + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness CompletenessV02 `json:"completeness"` + Reproducible bool `json:"reproducible"` +} + +// ProvenancePredicateV02 is the SLSA v0.2 provenance predicate. +type ProvenancePredicateV02 struct { + Builder ProvenanceBuilder `json:"builder"` + BuildType string `json:"buildType"` + Invocation interface{} `json:"invocation,omitempty"` + BuildConfig interface{} `json:"buildConfig,omitempty"` + Metadata *ProvenanceMetadataV02 `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} diff --git a/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go b/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go index fd14ac2b06..4f2691773a 100644 --- a/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go +++ b/pkg/ingestor/verifier/sigstore_verifier/sigstore_verifier_test.go @@ -41,38 +41,53 @@ import ( const PayloadType = "application/vnd.in-toto+json" // StatementInTotoV01 is the in-toto v0.1 statement type. +// Mirrors the canonical upstream type URI from github.com/in-toto/in-toto-golang. const StatementInTotoV01 = "https://in-toto.io/Statement/v0.1" // predicateSLSAProvenanceV02 is the predicate type for SLSAv0.2 provenance. +// Mirrors parser_slsa.PredicateSLSAProvenanceV02. const predicateSLSAProvenanceV02 = "https://slsa.dev/provenance/v0.2" -// digestSet is a set of digests keyed by algorithm name. +// digestSet is a set of digests keyed by algorithm name (e.g. "sha256"). +// Mirrors the DigestSet type from github.com/in-toto/in-toto-golang/in_toto. type digestSet = map[string]string // provenanceBuilder identifies the entity that executed the build steps. +// Mirrors the ProvenanceBuilder type from github.com/in-toto/in-toto-golang/in_toto +// with the same JSON field name to ensure the test exercises the correct wire format. type provenanceBuilder struct { ID string `json:"id"` } // subject describes the set of software artifacts the statement applies to. +// Mirrors the Subject type from github.com/in-toto/in-toto-golang/in_toto +// with the same JSON field names to ensure the test exercises the correct wire format. type subject struct { Name string `json:"name"` Digest digestSet `json:"digest"` } -// statementHeader defines the common fields for all statements. +// statementHeader defines the common fields for all in-toto v0.1 statements. +// Mirrors the StatementHeader type from github.com/in-toto/in-toto-golang/in_toto +// with identical JSON tags (_type, predicateType, subject) to exercise the +// exact wire format the verifier processes in production. type statementHeader struct { Type string `json:"_type"` PredicateType string `json:"predicateType"` Subject []subject `json:"subject"` } -// provenancePredicate is the SLSA v0.2 provenance predicate (simplified for testing). +// provenancePredicate is the SLSA v0.2 provenance predicate. +// Mirrors the ProvenancePredicate type from github.com/in-toto/in-toto-golang/in_toto +// (simplified to only the fields needed for signing/verification testing). type provenancePredicate struct { Builder provenanceBuilder `json:"builder"` } -// provenanceStatement is the definition for an entire provenance statement (for testing). +// provenanceStatement is the complete in-toto v0.1 provenance statement. +// Mirrors the ProvenanceStatement type from github.com/in-toto/in-toto-golang/in_toto +// with the same embedded statementHeader and JSON field names to ensure +// the test exercises the exact format the verifier consumes in production. type provenanceStatement struct { statementHeader Predicate provenancePredicate `json:"predicate"` From 975101ec2abe2a751b60a7db73723cdc0581aa39 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Tue, 5 May 2026 18:33:05 +0530 Subject: [PATCH 3/3] fix: correct time import and FinishedOn bugs in SLSA parser Signed-off-by: Abhishek --- pkg/ingestor/parser/slsa/parser_slsa.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/ingestor/parser/slsa/parser_slsa.go b/pkg/ingestor/parser/slsa/parser_slsa.go index 2bd547b6aa..559d9893f0 100644 --- a/pkg/ingestor/parser/slsa/parser_slsa.go +++ b/pkg/ingestor/parser/slsa/parser_slsa.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "strings" + "time" jsoniter "github.com/json-iterator/go" @@ -258,7 +259,7 @@ func fillSLSA02(inp *model.SLSAInputSpec, pred *ProvenancePredicateV02) error { inp.StartedOn = pred.Metadata.BuildStartedOn } if pred.Metadata.BuildFinishedOn != nil { - inp.FinishedOn = pred.Metadata.BuildStartedOn + inp.FinishedOn = pred.Metadata.BuildFinishedOn } return nil } @@ -273,7 +274,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