diff --git a/go.mod b/go.mod index 16e99b2858..289619feee 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/google/go-licenses v1.6.0 github.com/grafeas/grafeas v0.2.3 github.com/hashicorp/go-multierror v1.1.1 + github.com/in-toto/attestation v1.0.1 github.com/in-toto/in-toto-golang v0.9.1-0.20240317085821-8e2966059a09 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 @@ -254,7 +255,6 @@ require ( github.com/hashicorp/vault/api v1.12.2 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/imdario/mergo v0.3.16 // indirect - github.com/in-toto/attestation v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect diff --git a/pkg/artifacts/signable_test.go b/pkg/artifacts/signable_test.go index 98c303c0ac..8863984561 100644 --- a/pkg/artifacts/signable_test.go +++ b/pkg/artifacts/signable_test.go @@ -46,7 +46,6 @@ const ( var ignore = []cmp.Option{cmpopts.IgnoreUnexported(name.Registry{}, name.Repository{}, name.Digest{})} func TestOCIArtifact_ExtractObjects(t *testing.T) { - tests := []struct { name string obj objects.TektonObject @@ -808,6 +807,7 @@ func TestExtractBuildArtifactsFromResults(t *testing.T) { } func createDigest(t *testing.T, dgst string) name.Digest { + t.Helper() result, err := name.NewDigest(dgst) if err != nil { t.Fatal(err) diff --git a/pkg/chains/formats/slsa/extract/extract.go b/pkg/chains/formats/slsa/extract/extract.go index 510d13c06a..a47d558f7c 100644 --- a/pkg/chains/formats/slsa/extract/extract.go +++ b/pkg/chains/formats/slsa/extract/extract.go @@ -22,7 +22,7 @@ import ( "strings" "github.com/google/go-containerregistry/pkg/name" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" @@ -44,8 +44,8 @@ import ( // - the `*_URL` or `*_URI` fields cannot be empty. // //nolint:all -func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []intoto.Subject { - var subjects []intoto.Subject +func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { + var subjects []*intoto.ResourceDescriptor switch obj.GetObject().(type) { case *v1.PipelineRun: @@ -61,7 +61,7 @@ func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *s return subjects } -func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []intoto.Subject { +func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { prSubjects := subjectsFromTektonObject(ctx, obj) // If deep inspection is not enabled, just return subjects observed on the pipelinerun level @@ -71,13 +71,14 @@ func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsa logger := logging.FromContext(ctx) // If deep inspection is enabled, collect subjects from child taskruns - var result []intoto.Subject + var result []*intoto.ResourceDescriptor pro := obj.(*objects.PipelineRunObjectV1) pSpec := pro.Status.PipelineSpec if pSpec != nil { - pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) + pipelineTasks := pSpec.Tasks + pipelineTasks = append(pipelineTasks, pSpec.Finally...) for _, t := range pipelineTasks { tr := pro.GetTaskRunFromTask(t.Name) // Ignore Tasks that did not execute during the PipelineRun. @@ -97,14 +98,14 @@ func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsa return result } -func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []intoto.Subject { +func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []*intoto.ResourceDescriptor { logger := logging.FromContext(ctx) - var subjects []intoto.Subject + var subjects []*intoto.ResourceDescriptor imgs := artifacts.ExtractOCIImagesFromResults(ctx, obj.GetResults()) for _, i := range imgs { if d, ok := i.(name.Digest); ok { - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: d.Repository.Name(), Digest: common.DigestSet{ "sha256": strings.TrimPrefix(d.DigestStr(), "sha256:"), @@ -120,7 +121,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i logger.Errorf("Digest %s should be in the format of: algorthm:abc", obj.Digest) continue } - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: obj.URI, Digest: common.DigestSet{ splits[0]: splits[1], @@ -133,7 +134,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i splits := strings.Split(s.Digest, ":") alg := splits[0] digest := splits[1] - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: s.URI, Digest: common.DigestSet{ alg: digest, @@ -173,7 +174,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i } } } - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: url, Digest: common.DigestSet{ "sha256": strings.TrimPrefix(digest, "sha256:"), @@ -211,8 +212,8 @@ func RetrieveAllArtifactURIs(ctx context.Context, obj objects.TektonObject, deep // with the fields `uri`, `digest`, and `isBuildArtifact` set to true. // - Use the IMAGES type-hint // - Use the *IMAGE_URL / *IMAGE_DIGEST type-hint suffix -func SubjectsFromBuildArtifact(ctx context.Context, results []objects.Result) []intoto.Subject { - var subjects []intoto.Subject +func SubjectsFromBuildArtifact(ctx context.Context, results []objects.Result) []*intoto.ResourceDescriptor { + var subjects []*intoto.ResourceDescriptor logger := logging.FromContext(ctx) buildArtifacts := artifacts.ExtractBuildArtifactsFromResults(ctx, results) for _, ba := range buildArtifacts { @@ -224,7 +225,7 @@ func SubjectsFromBuildArtifact(ctx context.Context, results []objects.Result) [] alg := splits[0] digest := splits[1] - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: ba.URI, Digest: common.DigestSet{ alg: digest, @@ -235,7 +236,7 @@ func SubjectsFromBuildArtifact(ctx context.Context, results []objects.Result) [] imgs := artifacts.ExtractOCIImagesFromResults(ctx, results) for _, i := range imgs { if d, ok := i.(name.Digest); ok { - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: d.Repository.Name(), Digest: common.DigestSet{ "sha256": strings.TrimPrefix(d.DigestStr(), "sha256:"), diff --git a/pkg/chains/formats/slsa/extract/extract_test.go b/pkg/chains/formats/slsa/extract/extract_test.go index 7a9b18828d..6467087466 100644 --- a/pkg/chains/formats/slsa/extract/extract_test.go +++ b/pkg/chains/formats/slsa/extract/extract_test.go @@ -23,12 +23,13 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/testing/protocmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logtesting "knative.dev/pkg/logging/testing" ) @@ -45,7 +46,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { name string // a map of url:digest pairs for type hinting results results map[string]string - wantSubjects []intoto.Subject + wantSubjects []*intoto.ResourceDescriptor wantFullURLs []string }{ { @@ -54,7 +55,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { artifactURL1: "sha256:" + artifactDigest1, artifactURL2: "sha256:" + artifactDigest2, }, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -110,7 +111,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { } for _, o := range runObjects { gotSubjects := extract.SubjectDigests(ctx, o, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}) - if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption()); diff != "" { + if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("Wrong subjects extracted, diff=%s", diff) } @@ -119,7 +120,6 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { t.Errorf("Wrong URIs extracted, diff=%s", diff) } } - }) } } @@ -129,14 +129,14 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { name string pro objects.TektonObject deepInspectionEnabled bool - wantSubjects []intoto.Subject + wantSubjects []*intoto.ResourceDescriptor wantFullURLs []string }{ { name: "deep inspection disabled", pro: createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), deepInspectionEnabled: false, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -150,7 +150,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { name: "deep inspection enabled: no duplication", pro: createProWithTaskRunResults(nil, []artifact{{uri: artifactURL2, digest: "sha256:" + artifactDigest2}}), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -167,7 +167,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -193,7 +193,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -208,12 +208,12 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { { name: "deep inspection enabled: pipelinerun and taskrun have duplicated results", pro: createProWithTaskRunResults( - createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}).(*objects.PipelineRunObjectV1), + createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), []artifact{ {uri: artifactURL1, digest: "sha256:" + artifactDigest1}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -228,12 +228,12 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { { name: "deep inspection enabled: pipelinerun and taskrun have different results", pro: createProWithTaskRunResults( - createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}).(*objects.PipelineRunObjectV1), + createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), []artifact{ {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -259,7 +259,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) gotSubjects := extract.SubjectDigests(ctx, tc.pro, &slsaconfig.SlsaConfig{DeepInspectionEnabled: tc.deepInspectionEnabled}) - if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption()); diff != "" { + if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("Wrong subjects extracted, diff=%s, %s", diff, gotSubjects) } @@ -275,7 +275,7 @@ func TestSubjectsFromBuildArtifact(t *testing.T) { tests := []struct { name string results []objects.Result - expectedSubjects []intoto.Subject + expectedSubjects []*intoto.ResourceDescriptor }{ { name: "no type-hinted build artifacts", @@ -332,7 +332,7 @@ func TestSubjectsFromBuildArtifact(t *testing.T) { }), }, }, - expectedSubjects: []intoto.Subject{ + expectedSubjects: []*intoto.ResourceDescriptor{ { Name: "gcr.io/test/img4", Digest: map[string]string{ @@ -391,7 +391,7 @@ func TestSubjectsFromBuildArtifact(t *testing.T) { ), }, }, - expectedSubjects: []intoto.Subject{ + expectedSubjects: []*intoto.ResourceDescriptor{ { Name: "gcr.io/test/img1", Digest: map[string]string{ @@ -433,14 +433,14 @@ func TestSubjectsFromBuildArtifact(t *testing.T) { t.Run(test.name, func(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) got := extract.SubjectsFromBuildArtifact(ctx, test.results) - if diff := cmp.Diff(test.expectedSubjects, got); diff != "" { + if diff := cmp.Diff(test.expectedSubjects, got, protocmp.Transform()); diff != "" { t.Errorf("Wrong subjects from build artifacts, +got -want, diff=%s", diff) } }) } } -func createTaskRunObjectWithResults(results map[string]string) objects.TektonObject { +func createTaskRunObjectWithResults(results map[string]string) *objects.TaskRunObjectV1 { trResults := []v1.TaskRunResult{} prefix := 0 for url, digest := range results { @@ -462,7 +462,7 @@ func createTaskRunObjectWithResults(results map[string]string) objects.TektonObj ) } -func createProWithPipelineResults(results map[string]string) objects.TektonObject { +func createProWithPipelineResults(results map[string]string) *objects.PipelineRunObjectV1 { prResults := []v1.PipelineRunResult{} prefix := 0 for url, digest := range results { diff --git a/pkg/chains/formats/slsa/extract/v1beta1/extract.go b/pkg/chains/formats/slsa/extract/v1beta1/extract.go index 06f6899e53..043ec0deb1 100644 --- a/pkg/chains/formats/slsa/extract/v1beta1/extract.go +++ b/pkg/chains/formats/slsa/extract/v1beta1/extract.go @@ -22,7 +22,7 @@ import ( "strings" "github.com/google/go-containerregistry/pkg/name" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" @@ -42,8 +42,8 @@ import ( // - the `*_URL` or `*_URI` fields cannot be empty. // //nolint:all -func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []intoto.Subject { - var subjects []intoto.Subject +func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { + var subjects []*intoto.ResourceDescriptor switch obj.GetObject().(type) { case *v1beta1.PipelineRun: @@ -55,7 +55,8 @@ func SubjectDigests(ctx context.Context, obj objects.TektonObject, slsaconfig *s return subjects } -func SubjectsFromPipelineRunV1Beta1(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []intoto.Subject { +// SubjectsFromPipelineRunV1Beta1 returns software artifacts produced from the PipelineRun object. +func SubjectsFromPipelineRunV1Beta1(ctx context.Context, obj objects.TektonObject, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { prSubjects := SubjectsFromTektonObjectV1Beta1(ctx, obj) // If deep inspection is not enabled, just return subjects observed on the pipelinerun level @@ -65,7 +66,7 @@ func SubjectsFromPipelineRunV1Beta1(ctx context.Context, obj objects.TektonObjec logger := logging.FromContext(ctx) // If deep inspection is enabled, collect subjects from child taskruns - var result []intoto.Subject + var result []*intoto.ResourceDescriptor pro := obj.(*objects.PipelineRunObjectV1Beta1) @@ -90,14 +91,15 @@ func SubjectsFromPipelineRunV1Beta1(ctx context.Context, obj objects.TektonObjec return result } -func SubjectsFromTektonObjectV1Beta1(ctx context.Context, obj objects.TektonObject) []intoto.Subject { +// SubjectsFromTektonObjectV1Beta1 returns software artifacts produced from the Tekton object. +func SubjectsFromTektonObjectV1Beta1(ctx context.Context, obj objects.TektonObject) []*intoto.ResourceDescriptor { logger := logging.FromContext(ctx) - var subjects []intoto.Subject + var subjects []*intoto.ResourceDescriptor imgs := artifacts.ExtractOCIImagesFromResults(ctx, obj.GetResults()) for _, i := range imgs { if d, ok := i.(name.Digest); ok { - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: d.Repository.Name(), Digest: common.DigestSet{ "sha256": strings.TrimPrefix(d.DigestStr(), "sha256:"), @@ -113,7 +115,7 @@ func SubjectsFromTektonObjectV1Beta1(ctx context.Context, obj objects.TektonObje logger.Errorf("Digest %s should be in the format of: algorthm:abc", obj.Digest) continue } - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: obj.URI, Digest: common.DigestSet{ splits[0]: splits[1], @@ -126,7 +128,7 @@ func SubjectsFromTektonObjectV1Beta1(ctx context.Context, obj objects.TektonObje splits := strings.Split(s.Digest, ":") alg := splits[0] digest := splits[1] - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: s.URI, Digest: common.DigestSet{ alg: digest, @@ -164,7 +166,7 @@ func SubjectsFromTektonObjectV1Beta1(ctx context.Context, obj objects.TektonObje } } } - subjects = artifact.AppendSubjects(subjects, intoto.Subject{ + subjects = artifact.AppendSubjects(subjects, &intoto.ResourceDescriptor{ Name: url, Digest: common.DigestSet{ "sha256": strings.TrimPrefix(digest, "sha256:"), diff --git a/pkg/chains/formats/slsa/extract/v1beta1/extract_test.go b/pkg/chains/formats/slsa/extract/v1beta1/extract_test.go index 0e1cd2dd0c..9eb1a371b9 100644 --- a/pkg/chains/formats/slsa/extract/v1beta1/extract_test.go +++ b/pkg/chains/formats/slsa/extract/v1beta1/extract_test.go @@ -23,12 +23,13 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" extractv1beta1 "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract/v1beta1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "google.golang.org/protobuf/testing/protocmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logtesting "knative.dev/pkg/logging/testing" ) @@ -45,7 +46,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { name string // a map of url:digest pairs for type hinting results results map[string]string - wantSubjects []intoto.Subject + wantSubjects []*intoto.ResourceDescriptor wantFullURLs []string }{ { @@ -54,7 +55,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { artifactURL1: "sha256:" + artifactDigest1, artifactURL2: "sha256:" + artifactDigest2, }, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -110,7 +111,7 @@ func TestSubjectDigestsAndRetrieveAllArtifactURIs(t *testing.T) { } for _, o := range runObjects { gotSubjects := extractv1beta1.SubjectDigests(ctx, o, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}) - if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption()); diff != "" { + if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("Wrong subjects extracted, diff=%s", diff) } @@ -129,14 +130,14 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { name string pro objects.TektonObject deepInspectionEnabled bool - wantSubjects []intoto.Subject + wantSubjects []*intoto.ResourceDescriptor wantFullURLs []string }{ { name: "deep inspection disabled", pro: createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), deepInspectionEnabled: false, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -150,7 +151,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { name: "deep inspection enabled: no duplication", pro: createProWithTaskRunResults(nil, []artifact{{uri: artifactURL2, digest: "sha256:" + artifactDigest2}}), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -167,7 +168,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -193,7 +194,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL2, Digest: map[string]string{ @@ -208,12 +209,12 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { { name: "deep inspection enabled: pipelinerun and taskrun have duplicated results", pro: createProWithTaskRunResults( - createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}).(*objects.PipelineRunObjectV1Beta1), + createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), []artifact{ {uri: artifactURL1, digest: "sha256:" + artifactDigest1}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -228,12 +229,12 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { { name: "deep inspection enabled: pipelinerun and taskrun have different results", pro: createProWithTaskRunResults( - createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}).(*objects.PipelineRunObjectV1Beta1), + createProWithPipelineResults(map[string]string{artifactURL1: "sha256:" + artifactDigest1}), []artifact{ {uri: artifactURL2, digest: "sha256:" + artifactDigest2}, }), deepInspectionEnabled: true, - wantSubjects: []intoto.Subject{ + wantSubjects: []*intoto.ResourceDescriptor{ { Name: artifactURL1, Digest: map[string]string{ @@ -259,7 +260,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) gotSubjects := extractv1beta1.SubjectDigests(ctx, tc.pro, &slsaconfig.SlsaConfig{DeepInspectionEnabled: tc.deepInspectionEnabled}) - if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption()); diff != "" { + if diff := cmp.Diff(tc.wantSubjects, gotSubjects, compare.SubjectCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("Wrong subjects extracted, diff=%s, %s", diff, gotSubjects) } @@ -271,7 +272,7 @@ func TestPipelineRunObserveModeForSubjects(t *testing.T) { } } -func createTaskRunObjectV1Beta1WithResults(results map[string]string) objects.TektonObject { +func createTaskRunObjectV1Beta1WithResults(results map[string]string) *objects.TaskRunObjectV1Beta1 { trResults := []v1beta1.TaskRunResult{} prefix := 0 for url, digest := range results { @@ -293,7 +294,7 @@ func createTaskRunObjectV1Beta1WithResults(results map[string]string) objects.Te ) } -func createProWithPipelineResults(results map[string]string) objects.TektonObject { +func createProWithPipelineResults(results map[string]string) *objects.PipelineRunObjectV1Beta1 { prResults := []v1beta1.PipelineRunResult{} prefix := 0 for url, digest := range results { diff --git a/pkg/chains/formats/slsa/internal/artifact/append.go b/pkg/chains/formats/slsa/internal/artifact/append.go index 5cab6e8429..dab976ef96 100644 --- a/pkg/chains/formats/slsa/internal/artifact/append.go +++ b/pkg/chains/formats/slsa/internal/artifact/append.go @@ -16,13 +16,13 @@ limitations under the License. package artifact import ( - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" ) // AppendSubjects adds new subject(s) to the original slice. // It merges the new item with an existing entry if they are duplicate instead of append. -func AppendSubjects(original []intoto.Subject, items ...intoto.Subject) []intoto.Subject { +func AppendSubjects(original []*intoto.ResourceDescriptor, items ...*intoto.ResourceDescriptor) []*intoto.ResourceDescriptor { var artifacts []artifact for _, s := range original { artifacts = append(artifacts, subjectToArtifact(s)) @@ -32,7 +32,7 @@ func AppendSubjects(original []intoto.Subject, items ...intoto.Subject) []intoto artifacts = addArtifact(artifacts, subjectToArtifact(s)) } - var result []intoto.Subject + var result []*intoto.ResourceDescriptor for _, a := range artifacts { result = append(result, artifactToSubject(a)) } @@ -99,15 +99,15 @@ func mergeMaps(m1 map[string]string, m2 map[string]string) { } } -func subjectToArtifact(s intoto.Subject) artifact { +func subjectToArtifact(s *intoto.ResourceDescriptor) artifact { return artifact{ name: s.Name, digestSet: s.Digest, } } -func artifactToSubject(a artifact) intoto.Subject { - return intoto.Subject{ +func artifactToSubject(a artifact) *intoto.ResourceDescriptor { + return &intoto.ResourceDescriptor{ Name: a.name, Digest: a.digestSet, } diff --git a/pkg/chains/formats/slsa/internal/artifact/append_test.go b/pkg/chains/formats/slsa/internal/artifact/append_test.go index 3ea0704744..e91cac650e 100644 --- a/pkg/chains/formats/slsa/internal/artifact/append_test.go +++ b/pkg/chains/formats/slsa/internal/artifact/append_test.go @@ -19,19 +19,20 @@ import ( "testing" "github.com/google/go-cmp/cmp" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" + "google.golang.org/protobuf/testing/protocmp" ) func TestAppendSubjects(t *testing.T) { tests := []struct { name string - original []intoto.Subject - toAdd []intoto.Subject - want []intoto.Subject + original []*intoto.ResourceDescriptor + toAdd []*intoto.ResourceDescriptor + want []*intoto.ResourceDescriptor }{{ name: "add a completely new subject", - original: []intoto.Subject{ + original: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -44,7 +45,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - toAdd: []intoto.Subject{ + toAdd: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", Digest: common.DigestSet{ @@ -52,7 +53,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - want: []intoto.Subject{ + want: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -72,7 +73,7 @@ func TestAppendSubjects(t *testing.T) { }, }, { name: "add a subject with same uri and digest", - original: []intoto.Subject{ + original: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -80,7 +81,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - toAdd: []intoto.Subject{ + toAdd: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -88,7 +89,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - want: []intoto.Subject{ + want: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -98,7 +99,7 @@ func TestAppendSubjects(t *testing.T) { }, }, { name: "add a subject with same uri but different digest", - original: []intoto.Subject{ + original: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -106,7 +107,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - toAdd: []intoto.Subject{ + toAdd: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -114,7 +115,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - want: []intoto.Subject{ + want: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -130,7 +131,7 @@ func TestAppendSubjects(t *testing.T) { }, { name: "add a subject with same uri, one common digest and one different digest", - original: []intoto.Subject{ + original: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -139,7 +140,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - toAdd: []intoto.Subject{ + toAdd: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -148,7 +149,7 @@ func TestAppendSubjects(t *testing.T) { }, }, }, - want: []intoto.Subject{ + want: []*intoto.ResourceDescriptor{ { Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ @@ -164,7 +165,7 @@ func TestAppendSubjects(t *testing.T) { t.Run(tc.name, func(t *testing.T) { got := AppendSubjects(tc.original, tc.toAdd...) - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("materials(): -want +got: %s", diff) } }) @@ -312,7 +313,7 @@ func TestAppendMaterials(t *testing.T) { t.Run(tc.name, func(t *testing.T) { got := AppendMaterials(tc.original, tc.toAdd...) - if diff := cmp.Diff(tc.want, got); diff != "" { + if diff := cmp.Diff(tc.want, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("materials(): -want +got: %s", diff) } }) diff --git a/pkg/chains/formats/slsa/internal/build_definition/build_definition.go b/pkg/chains/formats/slsa/internal/build_definition/build_definition.go index 655bdbf701..891b3c2dbc 100644 --- a/pkg/chains/formats/slsa/internal/build_definition/build_definition.go +++ b/pkg/chains/formats/slsa/internal/build_definition/build_definition.go @@ -15,23 +15,30 @@ package builddefinition import ( "context" + "encoding/json" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" buildtypes "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_types" externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" "github.com/tektoncd/chains/pkg/chains/objects" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" ) // GetTaskRunBuildDefinition returns the buildDefinition for the given TaskRun based on the configured buildType. This will default to the slsa buildType -func GetTaskRunBuildDefinition(ctx context.Context, tro *objects.TaskRunObjectV1, buildType string, resolveOpts resolveddependencies.ResolveOptions) (slsa.ProvenanceBuildDefinition, error) { +func GetTaskRunBuildDefinition(ctx context.Context, tro *objects.TaskRunObjectV1, buildType string, resolveOpts resolveddependencies.ResolveOptions) (slsa.BuildDefinition, error) { rd, err := resolveddependencies.TaskRun(ctx, resolveOpts, tro) if err != nil { - return slsa.ProvenanceBuildDefinition{}, err + return slsa.BuildDefinition{}, err } externalParams := externalparameters.TaskRun(tro) + structExternalParams, err := getStruct(externalParams) + if err != nil { + return slsa.BuildDefinition{}, err + } buildDefinitionType := buildType if buildDefinitionType == "" { @@ -40,13 +47,32 @@ func GetTaskRunBuildDefinition(ctx context.Context, tro *objects.TaskRunObjectV1 internalParams, err := internalparameters.GetInternalParamters(tro, buildDefinitionType) if err != nil { - return slsa.ProvenanceBuildDefinition{}, err + return slsa.BuildDefinition{}, err + } + structInternalParams, err := getStruct(internalParams) + if err != nil { + return slsa.BuildDefinition{}, err } - return slsa.ProvenanceBuildDefinition{ + return slsa.BuildDefinition{ BuildType: buildDefinitionType, - ExternalParameters: externalParams, - InternalParameters: internalParams, + ExternalParameters: structExternalParams, + InternalParameters: structInternalParams, ResolvedDependencies: rd, }, nil } + +func getStruct(data map[string]any) (*structpb.Struct, error) { + bytes, err := json.Marshal(data) + if err != nil { + return nil, err + } + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + return nil, err + } + + return protoStruct, nil +} diff --git a/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go b/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go index ab91a0835c..963d6c07b8 100644 --- a/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go +++ b/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go @@ -18,12 +18,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" ) func TestGetBuildDefinition(t *testing.T) { @@ -45,42 +47,43 @@ func TestGetBuildDefinition(t *testing.T) { tests := []struct { name string buildType string - want slsa.ProvenanceBuildDefinition + want slsa.BuildDefinition err error }{ { name: "test slsa build type", buildType: "https://tekton.dev/chains/v2/slsa", - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.TaskRun(tro), - InternalParameters: internalparameters.SLSAInternalParameters(tro), + ExternalParameters: getProtoStruct(t, externalparameters.TaskRun(tro)), + InternalParameters: getProtoStruct(t, internalparameters.SLSAInternalParameters(tro)), }, err: nil, }, { name: "test default build type", buildType: "", - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.TaskRun(tro), - InternalParameters: internalparameters.SLSAInternalParameters(tro), + ExternalParameters: getProtoStruct(t, externalparameters.TaskRun(tro)), + InternalParameters: getProtoStruct(t, internalparameters.SLSAInternalParameters(tro)), }, err: nil, }, { name: "test tekton build type", buildType: "https://tekton.dev/chains/v2/slsa-tekton", - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa-tekton", - ExternalParameters: externalparameters.TaskRun(tro), - InternalParameters: internalparameters.TektonInternalParameters(tro), + ExternalParameters: getProtoStruct(t, externalparameters.TaskRun(tro)), + InternalParameters: getProtoStruct(t, internalparameters.TektonInternalParameters(tro)), }, err: nil, }, } - for _, tc := range tests { + for i := range tests { + tc := &tests[i] t.Run(tc.name, func(t *testing.T) { rd, err := resolveddependencies.TaskRun(ctx, resolveddependencies.ResolveOptions{}, tro) if err != nil { @@ -92,8 +95,8 @@ func TestGetBuildDefinition(t *testing.T) { if err != nil { t.Fatalf("Did not expect an error but got %v", err) } - - if diff := cmp.Diff(tc.want, bd); diff != "" { + bd.ProtoReflect() + if diff := cmp.Diff(&tc.want, &bd, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("getBuildDefinition(): -want +got: %v", diff) } }) @@ -110,7 +113,17 @@ func TestUnsupportedBuildType(t *testing.T) { if err == nil { t.Error("getBuildDefinition(): expected error got nil") } - if diff := cmp.Diff(slsa.ProvenanceBuildDefinition{}, got); diff != "" { + if diff := cmp.Diff(&slsa.BuildDefinition{}, &got, protocmp.Transform()); diff != "" { t.Errorf("getBuildDefinition(): -want +got: %s", diff) } } + +func getProtoStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + protoStruct, err := getStruct(data) + if err != nil { + t.Fatalf("error getting proto struct from data: %v", err) + } + + return protoStruct +} diff --git a/pkg/chains/formats/slsa/internal/compare/slsacompare.go b/pkg/chains/formats/slsa/internal/compare/slsacompare.go index 0b7b8c2d84..674112ea96 100644 --- a/pkg/chains/formats/slsa/internal/compare/slsacompare.go +++ b/pkg/chains/formats/slsa/internal/compare/slsacompare.go @@ -17,9 +17,8 @@ package compare import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" ) // SLSAV1CompareOptions returns the comparison options for sorting some slice fields in @@ -28,12 +27,12 @@ func SLSAV1CompareOptions() []cmp.Option { // checking content + uri + digest should be sufficient here based on the fact that // a ResourceDescriptor MUST specify one of uri, digest or content at a minimum. // Source: https://github.com/in-toto/attestation/blob/main/spec/v1/resource_descriptor.md#fields - resourceDescriptorSort := func(x, y slsa.ResourceDescriptor) bool { + resourceDescriptorSort := func(x, y *intoto.ResourceDescriptor) bool { if string(x.Content) != string(y.Content) { return string(x.Content) < string(y.Content) } - if x.URI != y.URI { - return x.URI < y.URI + if x.Uri != y.Uri { + return x.Uri < y.Uri } return lessDigestSet(x.Digest, y.Digest) } @@ -47,7 +46,7 @@ func SLSAV1CompareOptions() []cmp.Option { // SubjectCompareOption returns the comparison option to sort and compare a // list of Subjects. func SubjectCompareOption() cmp.Option { - subjectSort := func(x, y in_toto.Subject) bool { + subjectSort := func(x, y *intoto.ResourceDescriptor) bool { if x.Name != y.Name { return x.Name < y.Name } diff --git a/pkg/chains/formats/slsa/internal/material/v1beta1/material.go b/pkg/chains/formats/slsa/internal/material/v1beta1/material.go index ef49f2b73c..650bb1d6dd 100644 --- a/pkg/chains/formats/slsa/internal/material/v1beta1/material.go +++ b/pkg/chains/formats/slsa/internal/material/v1beta1/material.go @@ -74,7 +74,8 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObjectV1Beta } pSpec := pro.Status.PipelineSpec if pSpec != nil { - pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) + pipelineTasks := pSpec.Tasks + pipelineTasks = append(pipelineTasks, pSpec.Finally...) for _, t := range pipelineTasks { tr := pro.GetTaskRunFromTask(t.Name) // Ignore Tasks that did not execute during the PipelineRun. @@ -269,7 +270,8 @@ func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunO // search type hinting param/results from each individual taskruns if slsaconfig.DeepInspectionEnabled { logger := logging.FromContext(ctx) - pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) + pipelineTasks := pSpec.Tasks + pipelineTasks = append(pipelineTasks, pSpec.Finally...) for _, t := range pipelineTasks { tr := pro.GetTaskRunFromTask(t.Name) // Ignore Tasks that did not execute during the PipelineRun. diff --git a/pkg/chains/formats/slsa/internal/metadata/metadata.go b/pkg/chains/formats/slsa/internal/metadata/metadata.go index ab3d411d98..7d98ad859e 100644 --- a/pkg/chains/formats/slsa/internal/metadata/metadata.go +++ b/pkg/chains/formats/slsa/internal/metadata/metadata.go @@ -14,15 +14,29 @@ limitations under the License. package metadata import ( - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" "github.com/tektoncd/chains/pkg/chains/objects" + "google.golang.org/protobuf/types/known/timestamppb" ) // GetBuildMetadata returns SLSA metadata. -func GetBuildMetadata(obj objects.TektonObject) slsa.BuildMetadata { - return slsa.BuildMetadata{ - InvocationID: string(obj.GetUID()), - StartedOn: obj.GetStartTime(), - FinishedOn: obj.GetCompletitionTime(), +func GetBuildMetadata(obj objects.TektonObject) *slsa.BuildMetadata { + var startedOn *timestamppb.Timestamp + var finishedOn *timestamppb.Timestamp + objStartTime := obj.GetStartTime() + objCompletitionTime := obj.GetCompletitionTime() + + if objStartTime != nil { + startedOn = timestamppb.New(*objStartTime) + } + + if objCompletitionTime != nil { + finishedOn = timestamppb.New(*objCompletitionTime) + } + + return &slsa.BuildMetadata{ + InvocationId: string(obj.GetUID()), + StartedOn: startedOn, + FinishedOn: finishedOn, } } diff --git a/pkg/chains/formats/slsa/internal/metadata/metadata_test.go b/pkg/chains/formats/slsa/internal/metadata/metadata_test.go index 2f0059f249..3f6cb3e7a9 100644 --- a/pkg/chains/formats/slsa/internal/metadata/metadata_test.go +++ b/pkg/chains/formats/slsa/internal/metadata/metadata_test.go @@ -18,14 +18,16 @@ import ( "time" "github.com/google/go-cmp/cmp" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" "github.com/tektoncd/chains/pkg/chains/objects" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestMetadata(t *testing.T) { - tr := &v1.TaskRun{ //nolint:staticcheck + tr := &v1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: "my-taskrun", Namespace: "my-namespace", @@ -43,20 +45,20 @@ func TestMetadata(t *testing.T) { } start := time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC) end := time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC) - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, + want := &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(start), + FinishedOn: timestamppb.New(end), } got := GetBuildMetadata(objects.NewTaskRunObjectV1(tr)) - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, cmp.Options{protocmp.Transform()}); d != "" { t.Fatalf("metadata (-want, +got):\n%s", d) } } func TestMetadataInTimeZone(t *testing.T) { tz := time.FixedZone("Test Time", int((12 * time.Hour).Seconds())) - tr := &v1.TaskRun{ //nolint:staticcheck + tr := &v1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ Name: "my-taskrun", Namespace: "my-namespace", @@ -74,13 +76,13 @@ func TestMetadataInTimeZone(t *testing.T) { } start := time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC() end := time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC() - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, + want := &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(start), + FinishedOn: timestamppb.New(end), } got := GetBuildMetadata(objects.NewTaskRunObjectV1(tr)) - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, cmp.Options{protocmp.Transform()}); d != "" { t.Fatalf("metadata (-want, +got):\n%s", d) } } diff --git a/pkg/chains/formats/slsa/internal/provenance/provenance.go b/pkg/chains/formats/slsa/internal/provenance/provenance.go index f514c03eed..45f3070d7c 100644 --- a/pkg/chains/formats/slsa/internal/provenance/provenance.go +++ b/pkg/chains/formats/slsa/internal/provenance/provenance.go @@ -14,30 +14,52 @@ limitations under the License. package provenance import ( - intoto "github.com/in-toto/in-toto-golang/in_toto" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/metadata" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" ) // GetSLSA1Statement returns a predicate in SLSA v1.0 format using the given data. -func GetSLSA1Statement(obj objects.TektonObject, sub []intoto.Subject, bd slsa.ProvenanceBuildDefinition, bp []slsa.ResourceDescriptor, slsaConfig *slsaconfig.SlsaConfig) intoto.ProvenanceStatementSLSA1 { - return intoto.ProvenanceStatementSLSA1{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: sub, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: bd, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: slsaConfig.BuilderID, - }, - BuildMetadata: metadata.GetBuildMetadata(obj), - Byproducts: bp, +func GetSLSA1Statement(obj objects.TektonObject, sub []*intoto.ResourceDescriptor, bd *slsa.BuildDefinition, bp []*intoto.ResourceDescriptor, slsaConfig *slsaconfig.SlsaConfig) (intoto.Statement, error) { + predicate := slsa.Provenance{ + BuildDefinition: bd, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: slsaConfig.BuilderID, }, + Metadata: metadata.GetBuildMetadata(obj), + Byproducts: bp, }, } + + predicateStruct, err := getProtoStruct(&predicate) + if err != nil { + return intoto.Statement{}, err + } + + return intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: sub, + Predicate: predicateStruct, + }, nil +} + +func getProtoStruct(predicate *slsa.Provenance) (*structpb.Struct, error) { + protoStruct := &structpb.Struct{} + predicateJSON, err := protojson.Marshal(predicate) + if err != nil { + return nil, err + } + + err = protojson.Unmarshal(predicateJSON, protoStruct) + if err != nil { + return nil, err + } + + return protoStruct, nil } diff --git a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go index a619d9d299..c76a818196 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go @@ -21,14 +21,15 @@ import ( "encoding/json" "fmt" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/material" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "go.uber.org/zap" + "google.golang.org/protobuf/encoding/protojson" "knative.dev/pkg/logging" ) @@ -49,7 +50,7 @@ const ( // used to toggle the fields in see AddTektonTaskDescriptor // and AddSLSATaskDescriptor -type addTaskDescriptorContent func(*objects.TaskRunObjectV1) (*slsa.ResourceDescriptor, error) //nolint:staticcheck +type addTaskDescriptorContent func(*objects.TaskRunObjectV1) (*intoto.ResourceDescriptor, error) // ResolveOptions represents the configuration to be use to resolve dependencies. type ResolveOptions struct { @@ -58,37 +59,37 @@ type ResolveOptions struct { } // ConvertMaterialToResolvedDependency converts a SLSAv0.2 Material to a resolved dependency -func ConvertMaterialsToResolvedDependencies(mats []common.ProvenanceMaterial, name string) []slsa.ResourceDescriptor { - rds := []slsa.ResourceDescriptor{} +func ConvertMaterialsToResolvedDependencies(mats []common.ProvenanceMaterial, name string) []*intoto.ResourceDescriptor { + rds := []*intoto.ResourceDescriptor{} for _, mat := range mats { - rd := slsa.ResourceDescriptor{} - rd.URI = mat.URI + rd := intoto.ResourceDescriptor{} + rd.Uri = mat.URI rd.Digest = mat.Digest if len(name) > 0 { rd.Name = name } - rds = append(rds, rd) + rds = append(rds, &rd) } return rds } // RemoveDuplicateResolvedDependencies removes duplicate resolved dependencies from the slice of resolved dependencies. // Original order of resolved dependencies is retained. -func RemoveDuplicateResolvedDependencies(resolvedDependencies []slsa.ResourceDescriptor) ([]slsa.ResourceDescriptor, error) { - out := make([]slsa.ResourceDescriptor, 0, len(resolvedDependencies)) +func RemoveDuplicateResolvedDependencies(resolvedDependencies []*intoto.ResourceDescriptor) ([]*intoto.ResourceDescriptor, error) { + out := make([]*intoto.ResourceDescriptor, 0, len(resolvedDependencies)) // make map to store seen resolved dependencies seen := map[string]bool{} for _, resolvedDependency := range resolvedDependencies { // Since resolvedDependencies contain names, we want to ignore those while checking for duplicates. // Therefore, make a copy of the resolved dependency that only contains the uri and digest fields. - rDep := slsa.ResourceDescriptor{} - rDep.URI = resolvedDependency.URI + rDep := intoto.ResourceDescriptor{} + rDep.Uri = resolvedDependency.Uri rDep.Digest = resolvedDependency.Digest // pipelinTasks store content with the slsa-tekton buildType rDep.Content = resolvedDependency.Content // This allows us to ignore dependencies that have the same uri and digest. - rd, err := json.Marshal(rDep) + rd, err := protojson.Marshal(&rDep) if err != nil { return nil, err } @@ -110,8 +111,8 @@ func RemoveDuplicateResolvedDependencies(resolvedDependencies []slsa.ResourceDes // AddTektonTaskDescriptor returns the more verbose resolved dependency content. this adds the name, uri, digest // and content if possible. -func AddTektonTaskDescriptor(tr *objects.TaskRunObjectV1) (*slsa.ResourceDescriptor, error) { //nolint:staticcheck - rd := slsa.ResourceDescriptor{} +func AddTektonTaskDescriptor(tr *objects.TaskRunObjectV1) (*intoto.ResourceDescriptor, error) { + rd := intoto.ResourceDescriptor{} storedTr, err := json.Marshal(tr) if err != nil { return nil, err @@ -120,7 +121,7 @@ func AddTektonTaskDescriptor(tr *objects.TaskRunObjectV1) (*slsa.ResourceDescrip rd.Name = PipelineTaskConfigName rd.Content = storedTr if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { - rd.URI = tr.Status.Provenance.RefSource.URI + rd.Uri = tr.Status.Provenance.RefSource.URI rd.Digest = tr.Status.Provenance.RefSource.Digest } @@ -129,11 +130,11 @@ func AddTektonTaskDescriptor(tr *objects.TaskRunObjectV1) (*slsa.ResourceDescrip // AddSLSATaskDescriptor resolves dependency content for the more generic slsa verifiers. just logs // the name, uri and digest. -func AddSLSATaskDescriptor(tr *objects.TaskRunObjectV1) (*slsa.ResourceDescriptor, error) { //nolint:staticcheck +func AddSLSATaskDescriptor(tr *objects.TaskRunObjectV1) (*intoto.ResourceDescriptor, error) { if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { - return &slsa.ResourceDescriptor{ + return &intoto.ResourceDescriptor{ Name: PipelineTaskConfigName, - URI: tr.Status.Provenance.RefSource.URI, + Uri: tr.Status.Provenance.RefSource.URI, Digest: tr.Status.Provenance.RefSource.Digest, }, nil } @@ -142,9 +143,9 @@ func AddSLSATaskDescriptor(tr *objects.TaskRunObjectV1) (*slsa.ResourceDescripto // fromPipelineTask adds the resolved dependencies from pipeline tasks // such as pipeline task uri/digest for remote pipeline tasks and step and sidecar images. -func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV1, addTasks addTaskDescriptorContent) ([]slsa.ResourceDescriptor, error) { +func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV1, addTasks addTaskDescriptorContent) ([]*intoto.ResourceDescriptor, error) { pSpec := pro.Status.PipelineSpec - resolvedDependencies := []slsa.ResourceDescriptor{} + resolvedDependencies := []*intoto.ResourceDescriptor{} if pSpec != nil { pipelineTasks := pSpec.Tasks pipelineTasks = append(pipelineTasks, pSpec.Finally...) @@ -162,7 +163,7 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV } if rd != nil { - resolvedDependencies = append(resolvedDependencies, *rd) + resolvedDependencies = append(resolvedDependencies, rd) } mats := []common.ProvenanceMaterial{} @@ -189,8 +190,8 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV } // taskDependencies gather all dependencies in a task and adds them to resolvedDependencies -func taskDependencies(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { - var resolvedDependencies []slsa.ResourceDescriptor +func taskDependencies(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + var resolvedDependencies []*intoto.ResourceDescriptor var err error mats := []common.ProvenanceMaterial{} @@ -251,18 +252,18 @@ func taskDependencies(ctx context.Context, opts ResolveOptions, tro *objects.Tas } // TaskRun constructs `predicate.resolvedDependencies` section by collecting all the artifacts that influence a taskrun such as source code repo and step&sidecar base images. -func TaskRun(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { - var resolvedDependencies []slsa.ResourceDescriptor +func TaskRun(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + var resolvedDependencies []*intoto.ResourceDescriptor var err error // add top level task config if p := tro.Status.Provenance; p != nil && p.RefSource != nil { - rd := slsa.ResourceDescriptor{ + rd := intoto.ResourceDescriptor{ Name: TaskConfigName, - URI: p.RefSource.URI, + Uri: p.RefSource.URI, Digest: p.RefSource.Digest, } - resolvedDependencies = append(resolvedDependencies, rd) + resolvedDependencies = append(resolvedDependencies, &rd) } rds, err := taskDependencies(ctx, opts, tro) @@ -275,19 +276,19 @@ func TaskRun(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjec } // PipelineRun constructs `predicate.resolvedDependencies` section by collecting all the artifacts that influence a pipeline run such as source code repo and step&sidecar base images. -func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig, addTasks addTaskDescriptorContent) ([]slsa.ResourceDescriptor, error) { +func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig, addTasks addTaskDescriptorContent) ([]*intoto.ResourceDescriptor, error) { var err error - var resolvedDependencies []slsa.ResourceDescriptor + var resolvedDependencies []*intoto.ResourceDescriptor logger := logging.FromContext(ctx) // add pipeline config to resolved dependencies if p := pro.Status.Provenance; p != nil && p.RefSource != nil { - rd := slsa.ResourceDescriptor{ + rd := intoto.ResourceDescriptor{ Name: PipelineConfigName, - URI: p.RefSource.URI, + Uri: p.RefSource.URI, Digest: p.RefSource.Digest, } - resolvedDependencies = append(resolvedDependencies, rd) + resolvedDependencies = append(resolvedDependencies, &rd) } // add resolved dependencies from pipeline tasks diff --git a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go index a32ab0db9a..e19f2eecfd 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go @@ -22,8 +22,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - v1slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" @@ -32,6 +32,7 @@ import ( v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + "google.golang.org/protobuf/testing/protocmp" logtesting "knative.dev/pkg/logging/testing" ) @@ -40,41 +41,41 @@ const digest = "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812ad func TestRemoveDuplicates(t *testing.T) { tests := []struct { name string - rds []v1slsa.ResourceDescriptor - want []v1slsa.ResourceDescriptor + rds []*intoto.ResourceDescriptor + want []*intoto.ResourceDescriptor }{{ name: "no duplicate resolvedDependencies", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/cloud-marketplace-containers/google/bazel", + Uri: "oci://gcr.io/cloud-marketplace-containers/google/bazel", Digest: common.DigestSet{ "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", Digest: common.DigestSet{ "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/cloud-marketplace-containers/google/bazel", + Uri: "oci://gcr.io/cloud-marketplace-containers/google/bazel", Digest: common.DigestSet{ "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", Digest: common.DigestSet{ "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", }, @@ -82,22 +83,22 @@ func TestRemoveDuplicates(t *testing.T) { }, }, { name: "same uri and digest", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, @@ -105,27 +106,27 @@ func TestRemoveDuplicates(t *testing.T) { }, }, { name: "same uri but different digest", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, @@ -133,27 +134,27 @@ func TestRemoveDuplicates(t *testing.T) { }, }, { name: "same uri but different digest, swap order", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, @@ -161,39 +162,39 @@ func TestRemoveDuplicates(t *testing.T) { }, }, { name: "task config must be present", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { Name: "task", - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { Name: "task", - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, @@ -201,39 +202,39 @@ func TestRemoveDuplicates(t *testing.T) { }, }, { name: "pipeline config must be present", - rds: []v1slsa.ResourceDescriptor{ + rds: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { Name: "pipeline", - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, }, - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { Name: "pipeline", - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, @@ -246,7 +247,7 @@ func TestRemoveDuplicates(t *testing.T) { if err != nil { t.Fatalf("Did not expect an error but got %v", err) } - if diff := cmp.Diff(tc.want, rds); diff != "" { + if diff := cmp.Diff(tc.want, rds, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("resolvedDependencies(): -want +got: %s", diff) } }) @@ -302,9 +303,9 @@ func tektonTaskRuns() map[string][]byte { func TestTaskRun(t *testing.T) { tests := []struct { name string - obj objects.TektonObject //nolint:staticcheck + obj objects.TektonObject resolveOpts ResolveOptions - want []v1slsa.ResourceDescriptor + want []*intoto.ResourceDescriptor }{ { name: "resolvedDependencies from pipeline resources", @@ -355,17 +356,17 @@ func TestTaskRun(t *testing.T) { }, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { Name: "inputs/result", - URI: "gcr.io/foo/bar", + Uri: "gcr.io/foo/bar", Digest: common.DigestSet{ "sha256": strings.TrimPrefix(digest, "sha256:"), }, }, { Name: "pipelineResource", - URI: "git+https://github.com/GoogleContainerTools/distroless.git", + Uri: "git+https://github.com/GoogleContainerTools/distroless.git", Digest: common.DigestSet{ "sha1": "50c56a48cfb3a5a80fa36ed91c739bdac8381cbe", }, @@ -374,7 +375,7 @@ func TestTaskRun(t *testing.T) { }, { name: "resolvedDependencies from remote task", - obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Provenance: &v1.Provenance{ @@ -388,10 +389,10 @@ func TestTaskRun(t *testing.T) { }, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { Name: "task", - URI: "git+github.com/something.git", + Uri: "git+github.com/something.git", Digest: common.DigestSet{ "sha1": "abcd1234", }, @@ -400,7 +401,7 @@ func TestTaskRun(t *testing.T) { }, { name: "git resolvedDependencies from taskrun params", - obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ Spec: v1.TaskRunSpec{ Params: []v1.Param{{ Name: "CHAINS-GIT_COMMIT", @@ -411,10 +412,10 @@ func TestTaskRun(t *testing.T) { }}, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { Name: "inputs/result", - URI: "git+github.com/something.git", + Uri: "git+github.com/something.git", Digest: common.DigestSet{ "sha1": "my-commit", }, @@ -423,7 +424,7 @@ func TestTaskRun(t *testing.T) { }, { name: "resolvedDependencies from step images", - obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Steps: []v1.StepState{{ @@ -439,15 +440,15 @@ func TestTaskRun(t *testing.T) { }, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/cloud-marketplace-containers/google/bazel", + Uri: "oci://gcr.io/cloud-marketplace-containers/google/bazel", Digest: common.DigestSet{ "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", }, @@ -456,7 +457,7 @@ func TestTaskRun(t *testing.T) { }, { name: "resolvedDependencies from step and sidecar images", - obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Steps: []v1.StepState{{ @@ -482,19 +483,19 @@ func TestTaskRun(t *testing.T) { }, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/cloud-marketplace-containers/google/bazel", + Uri: "oci://gcr.io/cloud-marketplace-containers/google/bazel", Digest: common.DigestSet{ "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", Digest: common.DigestSet{ "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", }, @@ -506,7 +507,7 @@ func TestTaskRun(t *testing.T) { resolveOpts: ResolveOptions{ WithStepActionsResults: true, }, - obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Steps: []v1.StepState{{ @@ -532,25 +533,25 @@ func TestTaskRun(t *testing.T) { }, }, }), - want: []v1slsa.ResourceDescriptor{ + want: []*intoto.ResourceDescriptor{ { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", Digest: common.DigestSet{ "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", }, }, { - URI: "oci://gcr.io/cloud-marketplace-containers/google/bazel", + Uri: "oci://gcr.io/cloud-marketplace-containers/google/bazel", Digest: common.DigestSet{ "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", }, }, { - URI: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Uri: "oci://gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", Digest: common.DigestSet{ "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", }, }, { Name: "inputs/result", - URI: "https://github.com/tektoncd/pipeline", + Uri: "https://github.com/tektoncd/pipeline", Digest: common.DigestSet{ "sha1": "7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601", }, @@ -585,7 +586,7 @@ func TestTaskRun(t *testing.T) { if err != nil { t.Fatalf("Did not expect an error but got %v", err) } - if diff := cmp.Diff(tc.want, rd); diff != "" { + if diff := cmp.Diff(tc.want, rd, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("ResolvedDependencies(): -want +got: %s", diff) } }) @@ -597,52 +598,52 @@ func TestPipelineRun(t *testing.T) { tests := []struct { name string taskDescriptor addTaskDescriptorContent - want []v1slsa.ResourceDescriptor + want []*intoto.ResourceDescriptor }{ { name: "test slsa build type", taskDescriptor: AddSLSATaskDescriptor, - want: []v1slsa.ResourceDescriptor{ - {Name: "pipeline", URI: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, - {Name: "pipelineTask", URI: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, + want: []*intoto.ResourceDescriptor{ + {Name: "pipeline", Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, + {Name: "pipelineTask", Uri: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, { - URI: "oci://gcr.io/test1/test1", + Uri: "oci://gcr.io/test1/test1", Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - {Name: "pipelineTask", URI: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, + {Name: "pipelineTask", Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, { - URI: "oci://gcr.io/test2/test2", + Uri: "oci://gcr.io/test2/test2", Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, { - URI: "oci://gcr.io/test3/test3", + Uri: "oci://gcr.io/test3/test3", Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - {Name: "inputs/result", URI: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + {Name: "inputs/result", Uri: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, + {Name: "inputs/result", Uri: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, }, }, { name: "test tekton build type", taskDescriptor: AddTektonTaskDescriptor, - want: []v1slsa.ResourceDescriptor{ - {Name: "pipeline", URI: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, - {Name: "pipelineTask", URI: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}, Content: taskRuns["git-clone"]}, + want: []*intoto.ResourceDescriptor{ + {Name: "pipeline", Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, + {Name: "pipelineTask", Uri: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}, Content: taskRuns["git-clone"]}, { - URI: "oci://gcr.io/test1/test1", + Uri: "oci://gcr.io/test1/test1", Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - {Name: "pipelineTask", URI: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}, Content: taskRuns["taskrun-build"]}, + {Name: "pipelineTask", Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}, Content: taskRuns["taskrun-build"]}, { - URI: "oci://gcr.io/test2/test2", + Uri: "oci://gcr.io/test2/test2", Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, { - URI: "oci://gcr.io/test3/test3", + Uri: "oci://gcr.io/test3/test3", Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - {Name: "inputs/result", URI: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + {Name: "inputs/result", Uri: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, + {Name: "inputs/result", Uri: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, }, }, } @@ -655,7 +656,7 @@ func TestPipelineRun(t *testing.T) { if err != nil { t.Error(err) } - if d := cmp.Diff(tc.want, got); d != "" { + if d := cmp.Diff(tc.want, got, cmp.Options{protocmp.Transform()}); d != "" { t.Errorf("PipelineRunResolvedDependencies(): -want +got: %s", got) } }) diff --git a/pkg/chains/formats/slsa/internal/results/results.go b/pkg/chains/formats/slsa/internal/results/results.go index 2fb901b411..4861363307 100644 --- a/pkg/chains/formats/slsa/internal/results/results.go +++ b/pkg/chains/formats/slsa/internal/results/results.go @@ -21,7 +21,7 @@ import ( "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/objects" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/v1" ) var imageResultsNamesSuffixs = []string{ @@ -30,8 +30,8 @@ var imageResultsNamesSuffixs = []string{ } // GetResultsWithoutBuildArtifacts returns all the results without those that are build artifacts. -func GetResultsWithoutBuildArtifacts(results []objects.Result, resultTypePrefix string) ([]slsa.ResourceDescriptor, error) { - byProd := []slsa.ResourceDescriptor{} +func GetResultsWithoutBuildArtifacts(results []objects.Result, resultTypePrefix string) ([]*slsa.ResourceDescriptor, error) { + byProd := []*slsa.ResourceDescriptor{} for _, r := range results { if isBuildArtifact, err := artifacts.IsBuildArtifact(r); err != nil || isBuildArtifact { continue @@ -46,7 +46,7 @@ func GetResultsWithoutBuildArtifacts(results []objects.Result, resultTypePrefix return nil, err } - byProd = append(byProd, slsa.ResourceDescriptor{ + byProd = append(byProd, &slsa.ResourceDescriptor{ Name: fmt.Sprintf(resultTypePrefix, r.Name), Content: content, MediaType: "application/json", diff --git a/pkg/chains/formats/slsa/internal/results/results_test.go b/pkg/chains/formats/slsa/internal/results/results_test.go index 34f423fc46..fb3392bcd5 100644 --- a/pkg/chains/formats/slsa/internal/results/results_test.go +++ b/pkg/chains/formats/slsa/internal/results/results_test.go @@ -18,9 +18,10 @@ import ( "testing" "github.com/google/go-cmp/cmp" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/objects" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/testing/protocmp" ) func TestGetResultsWithoutBuildArtifacts(t *testing.T) { @@ -28,11 +29,11 @@ func TestGetResultsWithoutBuildArtifacts(t *testing.T) { name string prefix string results []objects.Result - expected []slsa.ResourceDescriptor + expected []*slsa.ResourceDescriptor }{ { name: "no results as input", - expected: []slsa.ResourceDescriptor{}, + expected: []*slsa.ResourceDescriptor{}, }, { name: "results without build artifacts", @@ -121,7 +122,7 @@ func TestGetResultsWithoutBuildArtifacts(t *testing.T) { }, }, }, - expected: []slsa.ResourceDescriptor{ + expected: []*slsa.ResourceDescriptor{ { Name: "taskRunResults/result1", MediaType: "application/json", @@ -181,7 +182,7 @@ func TestGetResultsWithoutBuildArtifacts(t *testing.T) { t.Fatalf("Unexpected error: %v", err) } - if d := cmp.Diff(test.expected, got); d != "" { + if d := cmp.Diff(test.expected, got, cmp.Options{protocmp.Transform()}); d != "" { t.Fatalf("metadata (-want, +got):\n%s", d) } }) @@ -189,6 +190,7 @@ func TestGetResultsWithoutBuildArtifacts(t *testing.T) { } func toJSONString(t *testing.T, val v1.ParamValue) []byte { + t.Helper() res, err := json.Marshal(val) if err != nil { t.Fatalf("error converting to json string: %v", err) diff --git a/pkg/chains/formats/slsa/v1/internal/protos/protos.go b/pkg/chains/formats/slsa/v1/internal/protos/protos.go new file mode 100644 index 0000000000..14209b4307 --- /dev/null +++ b/pkg/chains/formats/slsa/v1/internal/protos/protos.go @@ -0,0 +1,25 @@ +package protos + +import ( + "encoding/json" + + slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" +) + +// GetPredicateStruct returns a protobuf struct from the given SLSAv0.2 predicate. +func GetPredicateStruct(predicate *slsa.ProvenancePredicate) (*structpb.Struct, error) { + predicateJSON, err := json.Marshal(predicate) + if err != nil { + return nil, err + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + return nil, err + } + + return predicateStruct, nil +} diff --git a/pkg/chains/formats/slsa/v1/intotoite6_test.go b/pkg/chains/formats/slsa/v1/intotoite6_test.go index 9e5605a2ee..504d7034cf 100644 --- a/pkg/chains/formats/slsa/v1/intotoite6_test.go +++ b/pkg/chains/formats/slsa/v1/intotoite6_test.go @@ -24,13 +24,16 @@ import ( "github.com/tektoncd/chains/pkg/chains/formats" "github.com/tektoncd/chains/pkg/chains/formats/slsa/attest" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/internal/protos" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/pipelinerun" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/taskrun" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/config" "github.com/tektoncd/chains/pkg/internal/objectloader" + "google.golang.org/protobuf/testing/protocmp" "github.com/google/go-cmp/cmp" + intoto "github.com/in-toto/attestation/go/v1" "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" @@ -38,8 +41,8 @@ import ( logtesting "knative.dev/pkg/logging/testing" ) -var e1BuildStart = time.Unix(1617011400, 0) -var e1BuildFinished = time.Unix(1617011415, 0) +var e1BuildStart = time.Unix(1617011400, 0).UTC() +var e1BuildFinished = time.Unix(1617011415, 0).UTC() func TestTaskRunCreatePayload1(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) @@ -54,84 +57,90 @@ func TestTaskRunCreatePayload1(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: "gcr.io/my/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, + + predicate := slsa.ProvenancePredicate{ + Metadata: &slsa.ProvenanceMetadata{ + BuildStartedOn: &e1BuildStart, + BuildFinishedOn: &e1BuildFinished, + }, + Materials: []common.ProvenanceMaterial{ + { + URI: artifacts.OCIScheme + "gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + }, + { + URI: artifacts.OCIScheme + "gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, + }, + { + URI: artifacts.OCIScheme + "gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, + {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "sha:taskrun"}}, }, - Predicate: slsa.ProvenancePredicate{ - Metadata: &slsa.ProvenanceMetadata{ - BuildStartedOn: &e1BuildStart, - BuildFinishedOn: &e1BuildFinished, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/test", + Digest: map[string]string{"sha1": "ab123"}, + EntryPoint: "build.yaml", }, - Materials: []common.ProvenanceMaterial{ + Parameters: map[string]v1beta1.ParamValue{ + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "build"}, + }, + }, + Builder: common.ProvenanceBuilder{ + ID: "test_builder-1", + }, + BuildType: "tekton.dev/v1beta1/TaskRun", + BuildConfig: taskrun.BuildConfig{ + Steps: []attest.StepAttestation{ { - URI: artifacts.OCIScheme + "gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": string("step1"), + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + }, }, { - URI: artifacts.OCIScheme + "gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": string("step2"), + "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + }, }, { - URI: artifacts.OCIScheme + "gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "sha:taskrun"}}, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/test", - Digest: map[string]string{"sha1": "ab123"}, - EntryPoint: "build.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "build"}, + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": string("step3"), + "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", + }, }, }, - Builder: common.ProvenanceBuilder{ - ID: "test_builder-1", - }, - BuildType: "tekton.dev/v1beta1/TaskRun", - BuildConfig: taskrun.BuildConfig{ - Steps: []attest.StepAttestation{ - { - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": string("step1"), - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, - { - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": string("step2"), - "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", - }, - }, - { - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": string("step3"), - "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", - }, - }, + }, + } + + predicateStruct, err := protos.GetPredicateStruct(&predicate) + if err != nil { + t.Fatalf("error creating proto struct from predicate: %v", err) + } + + expected := &intoto.Statement{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, }, }, + Predicate: predicateStruct, } i, _ := NewFormatter(cfg) @@ -141,7 +150,7 @@ func TestTaskRunCreatePayload1(t *testing.T) { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff) } } @@ -158,193 +167,71 @@ func TestPipelineRunCreatePayload(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: "test.io/test/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, + + predicate := &slsa.ProvenancePredicate{ + Metadata: &slsa.ProvenanceMetadata{ + BuildStartedOn: &e1BuildStart, + BuildFinishedOn: &e1BuildFinished, + Completeness: slsa.ProvenanceComplete{ + Parameters: false, + Environment: false, + Materials: false, }, + Reproducible: false, }, - Predicate: slsa.ProvenancePredicate{ - Metadata: &slsa.ProvenanceMetadata{ - BuildStartedOn: &e1BuildStart, - BuildFinishedOn: &e1BuildFinished, - Completeness: slsa.ProvenanceComplete{ - Parameters: false, - Environment: false, - Materials: false, - }, - Reproducible: false, + Materials: []common.ProvenanceMaterial{ + {URI: "github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, + { + URI: artifacts.OCIScheme + "gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - Materials: []common.ProvenanceMaterial{ - {URI: "github.com/test", Digest: common.DigestSet{"sha1": "28b123"}}, - { - URI: artifacts.OCIScheme + "gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - {URI: "github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, - { - URI: artifacts.OCIScheme + "gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: artifacts.OCIScheme + "gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {URI: "github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, - {URI: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, - {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/test", - Digest: map[string]string{"sha1": "28b123"}, - EntryPoint: "pipeline.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, - }, + {URI: "github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, + { + URI: artifacts.OCIScheme + "gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - Builder: common.ProvenanceBuilder{ - ID: "test_builder-1", + { + URI: artifacts.OCIScheme + "gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - BuildType: "tekton.dev/v1beta1/PipelineRun", - BuildConfig: pipelinerun.BuildConfig{ - Tasks: []pipelinerun.TaskAttestation{ - { - Name: "git-clone", - After: nil, - Ref: v1beta1.TaskRef{ - Name: "git-clone", - Kind: "ClusterTask", - }, - StartedOn: e1BuildStart, - FinishedOn: e1BuildFinished, - Status: "Succeeded", - Steps: []attest.StepAttestation{ - { - EntryPoint: "git clone", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": "step1", - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - Annotations: nil, - }, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - EntryPoint: "git-clone.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - "revision": {Type: "string", StringVal: ""}, - "url": {Type: "string", StringVal: "https://git.test.com"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "git-clone"}, - }, - }, - Results: []v1beta1.TaskRunResult{ - { - Name: "some-uri_DIGEST", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, - { - Name: "some-uri", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "pkg:deb/debian/curl@7.50.3-1", - }, - }, - }, - }, - { - Name: "build", - After: []string{"git-clone"}, - Ref: v1beta1.TaskRef{ - Name: "build", - Kind: "ClusterTask", - }, - StartedOn: e1BuildStart, - FinishedOn: e1BuildFinished, - Status: "Succeeded", - Steps: []attest.StepAttestation{ - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - "container": "step1", - }, - Annotations: nil, - }, - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", - "container": "step2", - }, - Annotations: nil, - }, - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", - "container": "step3", - }, - Annotations: nil, - }, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/test", - Digest: map[string]string{"sha1": "ab123"}, - EntryPoint: "build.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "build"}, - }, - }, - Results: []v1beta1.TaskRunResult{ - { - Name: "IMAGE_DIGEST", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, - { - Name: "IMAGE_URL", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "gcr.io/my/image", - }, - }, - }, - }, + {URI: "github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, + {URI: "abc", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}}, + {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/test", + Digest: map[string]string{"sha1": "28b123"}, + EntryPoint: "pipeline.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, + }, + }, + Builder: common.ProvenanceBuilder{ + ID: "test_builder-1", + }, + BuildType: "tekton.dev/v1beta1/PipelineRun", + BuildConfig: getBuildPipelineRun(), + } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + expected := &intoto.Statement{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, }, }, + Predicate: predicateStruct, } tr1, err := objectloader.TaskRunV1Beta1FromFile("../testdata/pipeline-v1beta1/taskrun1.json") @@ -366,7 +253,7 @@ func TestPipelineRunCreatePayload(t *testing.T) { t.Errorf("unexpected error: %s", err.Error()) } // Sort Materials since their order can vary and result in flakes - if diff := cmp.Diff(expected, got, compare.MaterialsCompareOption()); diff != "" { + if diff := cmp.Diff(expected, got, compare.MaterialsCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff) } } @@ -382,187 +269,66 @@ func TestPipelineRunCreatePayloadChildRefs(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: "test.io/test/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, + + predicate := &slsa.ProvenancePredicate{ + Metadata: &slsa.ProvenanceMetadata{ + BuildStartedOn: &e1BuildStart, + BuildFinishedOn: &e1BuildFinished, + Completeness: slsa.ProvenanceComplete{ + Parameters: false, + Environment: false, + Materials: false, }, + Reproducible: false, }, - Predicate: slsa.ProvenancePredicate{ - Metadata: &slsa.ProvenanceMetadata{ - BuildStartedOn: &e1BuildStart, - BuildFinishedOn: &e1BuildFinished, - Completeness: slsa.ProvenanceComplete{ - Parameters: false, - Environment: false, - Materials: false, - }, - Reproducible: false, + Materials: []common.ProvenanceMaterial{ + { + URI: artifacts.OCIScheme + "gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - Materials: []common.ProvenanceMaterial{ - { - URI: artifacts.OCIScheme + "gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - {URI: "github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, - { - URI: artifacts.OCIScheme + "gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: artifacts.OCIScheme + "gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {URI: "github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, - {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + {URI: "github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}}, + { + URI: artifacts.OCIScheme + "gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{}, - Parameters: map[string]v1beta1.ParamValue{ - "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, - }, + { + URI: artifacts.OCIScheme + "gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - Builder: common.ProvenanceBuilder{ - ID: "test_builder-1", + {URI: "github.com/test", Digest: common.DigestSet{"sha1": "ab123"}}, + {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{}, + Parameters: map[string]v1beta1.ParamValue{ + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, }, - BuildType: "tekton.dev/v1beta1/PipelineRun", - BuildConfig: pipelinerun.BuildConfig{ - Tasks: []pipelinerun.TaskAttestation{ - { - Name: "git-clone", - After: nil, - Ref: v1beta1.TaskRef{ - Name: "git-clone", - Kind: "ClusterTask", - }, - StartedOn: e1BuildStart, - FinishedOn: e1BuildFinished, - Status: "Succeeded", - Steps: []attest.StepAttestation{ - { - EntryPoint: "git clone", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": "step1", - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - Annotations: nil, - }, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - EntryPoint: "git-clone.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - "revision": {Type: "string", StringVal: ""}, - "url": {Type: "string", StringVal: "https://git.test.com"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "git-clone"}, - }, - }, - Results: []v1beta1.TaskRunResult{ - { - Name: "some-uri_DIGEST", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, - { - Name: "some-uri", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "pkg:deb/debian/curl@7.50.3-1", - }, - }, - }, - }, - { - Name: "build", - After: []string{"git-clone"}, - Ref: v1beta1.TaskRef{ - Name: "build", - Kind: "ClusterTask", - }, - StartedOn: e1BuildStart, - FinishedOn: e1BuildFinished, - Status: "Succeeded", - Steps: []attest.StepAttestation{ - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - "container": "step1", - }, - Annotations: nil, - }, - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", - "container": "step2", - }, - Annotations: nil, - }, - { - EntryPoint: "", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", - "container": "step3", - }, - Annotations: nil, - }, - }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/test", - Digest: map[string]string{"sha1": "ab123"}, - EntryPoint: "build.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "build"}, - }, - }, - Results: []v1beta1.TaskRunResult{ - { - Name: "IMAGE_DIGEST", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, - { - Name: "IMAGE_URL", - Value: v1beta1.ParamValue{ - Type: v1beta1.ParamTypeString, - StringVal: "gcr.io/my/image", - }, - }, - }, - }, + }, + Builder: common.ProvenanceBuilder{ + ID: "test_builder-1", + }, + BuildType: "tekton.dev/v1beta1/PipelineRun", + BuildConfig: getBuildPipelineRun(), + } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + expected := &intoto.Statement{ + + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, }, }, + Predicate: predicateStruct, } tr1, err := objectloader.TaskRunV1Beta1FromFile("../testdata/pipeline-v1beta1/taskrun1.json") @@ -583,7 +349,7 @@ func TestPipelineRunCreatePayloadChildRefs(t *testing.T) { t.Errorf("unexpected error: %s", err.Error()) } // Sort Materials since their order can vary and result in flakes - if diff := cmp.Diff(expected, got, compare.MaterialsCompareOption()); diff != "" { + if diff := cmp.Diff(expected, got, compare.MaterialsCompareOption(), protocmp.Transform()); diff != "" { t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff) } } @@ -600,65 +366,71 @@ func TestTaskRunCreatePayload2(t *testing.T) { ID: "test_builder-2", }, } - expected := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: nil, + + predicate := &slsa.ProvenancePredicate{ + Metadata: &slsa.ProvenanceMetadata{ + BuildStartedOn: &e1BuildStart, + BuildFinishedOn: &e1BuildFinished, + }, + Builder: common.ProvenanceBuilder{ + ID: "test_builder-2", }, - Predicate: slsa.ProvenancePredicate{ - Metadata: &slsa.ProvenanceMetadata{ - BuildStartedOn: &e1BuildStart, - BuildFinishedOn: &e1BuildFinished, + Materials: []common.ProvenanceMaterial{ + { + URI: artifacts.OCIScheme + "gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - Builder: common.ProvenanceBuilder{ - ID: "test_builder-2", + {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "sha:taskdefault"}}, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + EntryPoint: "git-clone.yaml", }, - Materials: []common.ProvenanceMaterial{ - { - URI: artifacts.OCIScheme + "gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - {URI: artifacts.GitSchemePrefix + "https://git.test.com.git", Digest: common.DigestSet{"sha1": "sha:taskdefault"}}, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "revision": {Type: "string"}, + "url": {Type: "string", StringVal: "https://git.test.com"}, }, - Invocation: slsa.ProvenanceInvocation{ - ConfigSource: slsa.ConfigSource{ - URI: "github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - EntryPoint: "git-clone.yaml", - }, - Parameters: map[string]v1beta1.ParamValue{ - "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, - "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, - "revision": {Type: "string"}, - "url": {Type: "string", StringVal: "https://git.test.com"}, - }, - Environment: map[string]map[string]string{ - "labels": {"tekton.dev/pipelineTask": "git-clone"}, - }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "git-clone"}, }, - BuildType: "tekton.dev/v1beta1/TaskRun", - BuildConfig: taskrun.BuildConfig{ - Steps: []attest.StepAttestation{ - { - EntryPoint: "git clone", - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": string("step1"), - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, + }, + BuildType: "tekton.dev/v1beta1/TaskRun", + BuildConfig: taskrun.BuildConfig{ + Steps: []attest.StepAttestation{ + { + EntryPoint: "git clone", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": string("step1"), + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, }, }, }, } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + expected := &intoto.Statement{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: nil, + Predicate: predicateStruct, + } i, _ := NewFormatter(cfg) got, err := i.CreatePayload(ctx, objects.NewTaskRunObjectV1Beta1(tr)) if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, protocmp.Transform()); diff != "" { t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff) } } @@ -676,51 +448,57 @@ func TestMultipleSubjects(t *testing.T) { ID: "test_builder-multiple", }, } - expected := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + + predicate := &slsa.ProvenancePredicate{ + BuildType: "tekton.dev/v1beta1/TaskRun", + Metadata: &slsa.ProvenanceMetadata{}, + Builder: common.ProvenanceBuilder{ + ID: "test_builder-multiple", + }, + Materials: []common.ProvenanceMaterial{ + { + URI: artifacts.OCIScheme + "gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + Parameters: map[string]v1beta1.ParamValue{}, + }, + BuildConfig: taskrun.BuildConfig{ + Steps: []attest.StepAttestation{ { - Name: "gcr.io/myimage1", - Digest: common.DigestSet{ - "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, { - Name: "gcr.io/myimage2", - Digest: common.DigestSet{ - "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": string("step1"), + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, }, }, }, - Predicate: slsa.ProvenancePredicate{ - BuildType: "tekton.dev/v1beta1/TaskRun", - Metadata: &slsa.ProvenanceMetadata{}, - Builder: common.ProvenanceBuilder{ - ID: "test_builder-multiple", - }, - Materials: []common.ProvenanceMaterial{ - { - URI: artifacts.OCIScheme + "gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + expected := &intoto.Statement{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/myimage1", + Digest: common.DigestSet{ + "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, - }, - Invocation: slsa.ProvenanceInvocation{ - Parameters: map[string]v1beta1.ParamValue{}, - }, - BuildConfig: taskrun.BuildConfig{ - Steps: []attest.StepAttestation{ - { - Arguments: []string(nil), - Environment: map[string]interface{}{ - "container": string("step1"), - "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, + }, { + Name: "gcr.io/myimage2", + Digest: common.DigestSet{ + "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, }, }, + Predicate: predicateStruct, } i, _ := NewFormatter(cfg) @@ -728,7 +506,7 @@ func TestMultipleSubjects(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, protocmp.Transform()); diff != "" { t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff) } } @@ -783,3 +561,135 @@ func TestCorrectPayloadType(t *testing.T) { t.Errorf("Invalid type returned: %s", i.Type()) } } + +func getBuildPipelineRun() pipelinerun.BuildConfig { + return pipelinerun.BuildConfig{ + Tasks: []pipelinerun.TaskAttestation{ + { + Name: "git-clone", + After: nil, + Ref: v1beta1.TaskRef{ + Name: "git-clone", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "git clone", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": "step1", + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + }, + Annotations: nil, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + EntryPoint: "git-clone.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "revision": {Type: "string", StringVal: ""}, + "url": {Type: "string", StringVal: "https://git.test.com"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "git-clone"}, + }, + }, + Results: []v1beta1.TaskRunResult{ + { + Name: "some-uri_DIGEST", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + }, + }, + { + Name: "some-uri", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "pkg:deb/debian/curl@7.50.3-1", + }, + }, + }, + }, + { + Name: "build", + After: []string{"git-clone"}, + Ref: v1beta1.TaskRef{ + Name: "build", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "container": "step1", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "container": "step2", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", + "container": "step3", + }, + Annotations: nil, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/test", + Digest: map[string]string{"sha1": "ab123"}, + EntryPoint: "build.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "build"}, + }, + }, + Results: []v1beta1.TaskRunResult{ + { + Name: "IMAGE_DIGEST", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "IMAGE_URL", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "gcr.io/my/image", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go index 6c552cc746..cf258c398c 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go @@ -17,13 +17,14 @@ import ( "context" "time" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "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/tektoncd/chains/pkg/chains/formats/slsa/attest" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" materialv1beta1 "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/material/v1beta1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/internal/protos" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" corev1 "k8s.io/api/core/v1" @@ -47,6 +48,8 @@ type TaskAttestation struct { Results []v1beta1.TaskRunResult `json:"results,omitempty"` } +const statementInTotoV01 = "https://in-toto.io/Statement/v0.1" + func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1Beta1, slsaConfig *slsaconfig.SlsaConfig) (interface{}, error) { subjects := extract.SubjectDigests(ctx, pro, slsaConfig) @@ -54,22 +57,28 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1Be if err != nil { return nil, err } - att := intoto.ProvenanceStatement{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: subjects, - }, - Predicate: slsa.ProvenancePredicate{ - Builder: common.ProvenanceBuilder{ - ID: slsaConfig.BuilderID, - }, - BuildType: pro.GetGVK(), - Invocation: invocation(pro), - BuildConfig: buildConfig(ctx, pro), - Metadata: metadata(pro), - Materials: mat, + + predicate := &slsa.ProvenancePredicate{ + Builder: common.ProvenanceBuilder{ + ID: slsaConfig.BuilderID, }, + BuildType: pro.GetGVK(), + Invocation: invocation(pro), + BuildConfig: buildConfig(ctx, pro), + Metadata: metadata(pro), + Materials: mat, + } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + return nil, err + } + + att := &intoto.Statement{ + Type: statementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: subjects, + Predicate: predicateStruct, } return att, nil } diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go index 53489bd430..91ddb0bd41 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go @@ -20,7 +20,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-containerregistry/pkg/name" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "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/tektoncd/chains/pkg/artifacts" @@ -31,6 +31,7 @@ import ( "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "google.golang.org/protobuf/testing/protocmp" "k8s.io/apimachinery/pkg/selection" logtesting "knative.dev/pkg/logging/testing" ) @@ -474,7 +475,7 @@ func TestMetadataInTimeZone(t *testing.T) { var ignore = []cmp.Option{cmpopts.IgnoreUnexported(name.Registry{}, name.Repository{}, name.Digest{})} func TestSubjectDigests(t *testing.T) { - wantSubjects := []intoto.Subject{ + wantSubjects := []*intoto.ResourceDescriptor{ { Name: "test.io/test/image", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, @@ -483,7 +484,8 @@ func TestSubjectDigests(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) gotSubjects := extract.SubjectDigests(ctx, pro, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}) - opts := append(ignore, compare.SubjectCompareOption()) + opts := ignore + opts = append(opts, compare.SubjectCompareOption(), protocmp.Transform()) if diff := cmp.Diff(gotSubjects, wantSubjects, opts...); diff != "" { t.Errorf("Differences in subjects: -want +got: %s", diff) } diff --git a/pkg/chains/formats/slsa/v1/taskrun/provenance_test.go b/pkg/chains/formats/slsa/v1/taskrun/provenance_test.go index f36e5ae360..979f342f4e 100644 --- a/pkg/chains/formats/slsa/v1/taskrun/provenance_test.go +++ b/pkg/chains/formats/slsa/v1/taskrun/provenance_test.go @@ -22,11 +22,12 @@ import ( "testing" "time" + intoto "github.com/in-toto/attestation/go/v1" "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" + "google.golang.org/protobuf/testing/protocmp" "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" @@ -294,7 +295,7 @@ func TestGetSubjectDigests(t *testing.T) { }, } - want := []in_toto.Subject{ + want := []*intoto.ResourceDescriptor{ { Name: "com.google.guava:guava:1.0-jre.pom", Digest: common.DigestSet{ @@ -336,7 +337,7 @@ func TestGetSubjectDigests(t *testing.T) { tro := objects.NewTaskRunObjectV1Beta1(tr) got := extract.SubjectDigests(ctx, tro, nil) - if d := cmp.Diff(want, got, compare.SubjectCompareOption()); d != "" { + if d := cmp.Diff(want, got, compare.SubjectCompareOption(), protocmp.Transform()); d != "" { t.Errorf("Wrong subjects extracted, diff=%s", d) } } diff --git a/pkg/chains/formats/slsa/v1/taskrun/taskrun.go b/pkg/chains/formats/slsa/v1/taskrun/taskrun.go index 89b523e171..e1496bca93 100644 --- a/pkg/chains/formats/slsa/v1/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v1/taskrun/taskrun.go @@ -16,17 +16,20 @@ package taskrun import ( "context" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "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/tektoncd/chains/pkg/chains/formats/slsa/attest" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" materialv1beta1 "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/material/v1beta1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/internal/protos" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" ) +const statementInTotoV01 = "https://in-toto.io/Statement/v0.1" + func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1Beta1, slsaConfig *slsaconfig.SlsaConfig) (interface{}, error) { subjects := extract.SubjectDigests(ctx, tro, slsaConfig) @@ -34,24 +37,29 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1Beta1, if err != nil { return nil, err } - att := intoto.ProvenanceStatement{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: subjects, - }, - Predicate: slsa.ProvenancePredicate{ - Builder: common.ProvenanceBuilder{ - ID: slsaConfig.BuilderID, - }, - BuildType: tro.GetGVK(), - Invocation: invocation(tro), - BuildConfig: buildConfig(tro), - Metadata: Metadata(tro), - Materials: mat, + + predicate := &slsa.ProvenancePredicate{ + Builder: common.ProvenanceBuilder{ + ID: slsaConfig.BuilderID, }, + BuildType: tro.GetGVK(), + Invocation: invocation(tro), + BuildConfig: buildConfig(tro), + Metadata: Metadata(tro), + Materials: mat, + } + + predicateStruct, err := protos.GetPredicateStruct(predicate) + if err != nil { + return nil, err } - return att, nil + + return &intoto.Statement{ + Type: statementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: subjects, + Predicate: predicateStruct, + }, nil } // invocation describes the event that kicked off the build diff --git a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go index 3c24410991..12f02567e5 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go @@ -18,8 +18,8 @@ import ( "encoding/json" "fmt" - intoto "github.com/in-toto/in-toto-golang/in_toto" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" buildtypes "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_types" externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" @@ -27,6 +27,9 @@ import ( resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" ) const ( @@ -47,50 +50,61 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, return nil, err } - att := intoto.ProvenanceStatementSLSA1{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: extract.SubjectDigests(ctx, pro, slsaconfig), - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: bd, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: slsaconfig.BuilderID, - }, - BuildMetadata: metadata(pro), - Byproducts: bp, + predicate := &slsa.Provenance{ + BuildDefinition: &bd, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: slsaconfig.BuilderID, }, + Metadata: metadata(pro), + Byproducts: bp, }, } + + predicateStruct := &structpb.Struct{} + predicateJSON, err := protojson.Marshal(predicate) + if err != nil { + return nil, err + } + + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + return nil, err + } + + att := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: extract.SubjectDigests(ctx, pro, slsaconfig), + Predicate: predicateStruct, + } return att, nil } -func metadata(pro *objects.PipelineRunObjectV1) slsa.BuildMetadata { - m := slsa.BuildMetadata{ - InvocationID: string(pro.ObjectMeta.UID), +func metadata(pro *objects.PipelineRunObjectV1) *slsa.BuildMetadata { + m := &slsa.BuildMetadata{ + InvocationId: string(pro.ObjectMeta.UID), } if pro.Status.StartTime != nil { utc := pro.Status.StartTime.Time.UTC() - m.StartedOn = &utc + m.StartedOn = timestamppb.New(utc) } if pro.Status.CompletionTime != nil { utc := pro.Status.CompletionTime.Time.UTC() - m.FinishedOn = &utc + m.FinishedOn = timestamppb.New(utc) } return m } // byproducts contains the pipelineRunResults -func byproducts(pro *objects.PipelineRunObjectV1) ([]slsa.ResourceDescriptor, error) { - byProd := []slsa.ResourceDescriptor{} +func byproducts(pro *objects.PipelineRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + byProd := []*intoto.ResourceDescriptor{} for _, key := range pro.Status.Results { content, err := json.Marshal(key.Value) if err != nil { return nil, err } - bp := slsa.ResourceDescriptor{ + bp := &intoto.ResourceDescriptor{ Name: fmt.Sprintf(pipelineRunResults, key.Name), Content: content, MediaType: JsonMediaType, @@ -101,7 +115,7 @@ func byproducts(pro *objects.PipelineRunObjectV1) ([]slsa.ResourceDescriptor, er } // getBuildDefinition get the buildDefinition based on the configured buildType. This will default to the slsa buildType -func getBuildDefinition(ctx context.Context, slsaconfig *slsaconfig.SlsaConfig, pro *objects.PipelineRunObjectV1) (slsa.ProvenanceBuildDefinition, error) { +func getBuildDefinition(ctx context.Context, slsaconfig *slsaconfig.SlsaConfig, pro *objects.PipelineRunObjectV1) (slsa.BuildDefinition, error) { // if buildType is not set in the chains-config, default to slsa build type buildDefinitionType := slsaconfig.BuildType if slsaconfig.BuildType == "" { @@ -112,26 +126,61 @@ func getBuildDefinition(ctx context.Context, slsaconfig *slsaconfig.SlsaConfig, case buildtypes.SlsaBuildType: rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveddependencies.AddSLSATaskDescriptor) if err != nil { - return slsa.ProvenanceBuildDefinition{}, err + return slsa.BuildDefinition{}, err + } + extParamsStruct, err := getStruct(externalparameters.PipelineRun(pro)) + if err != nil { + return slsa.BuildDefinition{}, err } - return slsa.ProvenanceBuildDefinition{ + + intParamsStruct, err := getStruct(internalparameters.SLSAInternalParameters(pro)) + if err != nil { + return slsa.BuildDefinition{}, err + } + + return slsa.BuildDefinition{ BuildType: buildDefinitionType, - ExternalParameters: externalparameters.PipelineRun(pro), - InternalParameters: internalparameters.SLSAInternalParameters(pro), + ExternalParameters: extParamsStruct, + InternalParameters: intParamsStruct, ResolvedDependencies: rd, }, nil case buildtypes.TektonBuildType: rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveddependencies.AddTektonTaskDescriptor) if err != nil { - return slsa.ProvenanceBuildDefinition{}, err + return slsa.BuildDefinition{}, err } - return slsa.ProvenanceBuildDefinition{ + extParamsStruct, err := getStruct(externalparameters.PipelineRun(pro)) + if err != nil { + return slsa.BuildDefinition{}, err + } + + intParamsStruct, err := getStruct(internalparameters.TektonInternalParameters(pro)) + if err != nil { + return slsa.BuildDefinition{}, err + } + + return slsa.BuildDefinition{ BuildType: buildDefinitionType, - ExternalParameters: externalparameters.PipelineRun(pro), - InternalParameters: internalparameters.TektonInternalParameters(pro), + ExternalParameters: extParamsStruct, + InternalParameters: intParamsStruct, ResolvedDependencies: rd, }, nil default: - return slsa.ProvenanceBuildDefinition{}, fmt.Errorf("unsupported buildType %v", buildDefinitionType) + return slsa.BuildDefinition{}, fmt.Errorf("unsupported buildType %v", buildDefinitionType) + } +} + +func getStruct(data map[string]any) (*structpb.Struct, error) { + bytes, err := json.Marshal(data) + if err != nil { + return nil, err + } + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + return nil, err } + + return protoStruct, nil } diff --git a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go index 229caf69de..a363b1b86a 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go @@ -23,11 +23,9 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" - v1resourcedescriptor "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" - "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" @@ -35,12 +33,16 @@ import ( "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logtesting "knative.dev/pkg/logging/testing" ) func TestMetadata(t *testing.T) { - pr := &v1.PipelineRun{ //nolint:staticcheck + pr := &v1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "my-taskrun", Namespace: "my-namespace", @@ -58,20 +60,20 @@ func TestMetadata(t *testing.T) { } start := time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC) end := time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC) - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, + want := &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(start), + FinishedOn: timestamppb.New(end), } got := metadata(objects.NewPipelineRunObjectV1(pr)) - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, protocmp.Transform()); d != "" { t.Fatalf("metadata (-want, +got):\n%s", d) } } func TestMetadataInTimeZone(t *testing.T) { tz := time.FixedZone("Test Time", int((12 * time.Hour).Seconds())) - pr := &v1.PipelineRun{ //nolint:staticcheck + pr := &v1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ Name: "my-taskrun", Namespace: "my-namespace", @@ -89,20 +91,20 @@ func TestMetadataInTimeZone(t *testing.T) { } start := time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC() end := time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC() - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, + want := &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(start), + FinishedOn: timestamppb.New(end), } got := metadata(objects.NewPipelineRunObjectV1(pr)) - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, protocmp.Transform()); d != "" { t.Fatalf("metadata (-want, +got):\n%s", d) } } func TestByProducts(t *testing.T) { resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} - pr := &v1.PipelineRun{ //nolint:staticcheck + pr := &v1.PipelineRun{ Status: v1.PipelineRunStatus{ PipelineRunStatusFields: v1.PipelineRunStatusFields{ Results: []v1.PipelineRunResult{ @@ -119,7 +121,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not marshal results: %s", err) } - want := []slsa.ResourceDescriptor{ + want := []*intoto.ResourceDescriptor{ { Name: "pipelineRunResults/result-name", Content: resultBytes, @@ -130,7 +132,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not extract byproducts: %s", err) } - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, protocmp.Transform()); d != "" { t.Fatalf("byproducts (-want, +got):\n%s", d) } } @@ -161,106 +163,123 @@ func TestGenerateAttestation(t *testing.T) { e1BuildStart := time.Unix(1617011400, 0) e1BuildFinished := time.Unix(1617011415, 0) - want := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + externalParams := map[string]any{ + "runSpec": pr.Spec, + } + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getProtoStruct(t, externalParams), + InternalParameters: getProtoStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "test.io/test/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "28b123"}, + Name: "pipeline", }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": pr.Spec, + { + Uri: "git+https://github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + Name: "pipelineTask", }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "28b123"}, - Name: "pipeline", - }, - { - URI: "git+https://github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - Name: "pipelineTask", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "pipelineTask", - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - { - URI: "abc", - Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, - Name: "inputs/result", - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", + { + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "pipelineTask", }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "pipelineRunResults/CHAINS-GIT_COMMIT", - Content: []uint8(`"abcd"`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/CHAINS-GIT_URL", - Content: []uint8(`"https://git.test.com"`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/IMAGE_URL", - Content: []uint8(`"test.io/test/image"`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/IMAGE_DIGEST", - Content: []uint8(`"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/img-ARTIFACT_INPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/img2-ARTIFACT_OUTPUTS", - Content: []uint8(`{"digest":"sha256:","uri":"def"}`), - MediaType: JsonMediaType, - }, { - Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), - MediaType: JsonMediaType, - }, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, + }, + { + Uri: "abc", + Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + Name: "inputs/result", + }, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "abcd"}, + }, + }, + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "pipelineRunResults/CHAINS-GIT_COMMIT", + Content: []uint8(`"abcd"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/CHAINS-GIT_URL", + Content: []uint8(`"https://git.test.com"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/IMAGE_URL", + Content: []uint8(`"test.io/test/image"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/IMAGE_DIGEST", + Content: []uint8(`"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img2-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:","uri":"def"}`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), + MediaType: JsonMediaType, }, }, }, } + predicateJSON, err := protojson.Marshal(&slsaPredicate) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + want := intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + }, + Predicate: predicateStruct, + } + got, err := GenerateAttestation(ctx, pr, &slsaconfig.SlsaConfig{ BuilderID: "test_builder-1", DeepInspectionEnabled: false, @@ -270,16 +289,17 @@ func TestGenerateAttestation(t *testing.T) { if err != nil { t.Errorf("unwant error: %s", err.Error()) } - if diff := cmp.Diff(want, got, compare.SLSAV1CompareOptions()...); diff != "" { + + if diff := cmp.Diff(&want, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("GenerateAttestation(): -want +got: %s", diff) } } -func getResolvedDependencies(addTasks func(*objects.TaskRunObjectV1) (*v1resourcedescriptor.ResourceDescriptor, error)) []v1resourcedescriptor.ResourceDescriptor { //nolint:staticcheck +func getResolvedDependencies(addTasks func(*objects.TaskRunObjectV1) (*intoto.ResourceDescriptor, error)) []*intoto.ResourceDescriptor { pr := createPro("../../../testdata/slsa-v2alpha3/pipelinerun1.json") rd, err := resolveddependencies.PipelineRun(context.Background(), pr, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}, addTasks) if err != nil { - return []v1resourcedescriptor.ResourceDescriptor{} + return nil } return rd } @@ -294,18 +314,18 @@ func TestGetBuildDefinition(t *testing.T) { } tests := []struct { name string - taskContent func(*objects.TaskRunObjectV1) (*v1resourcedescriptor.ResourceDescriptor, error) //nolint:staticcheck + taskContent func(*objects.TaskRunObjectV1) (*intoto.ResourceDescriptor, error) config *slsaconfig.SlsaConfig - want slsa.ProvenanceBuildDefinition + want slsa.BuildDefinition }{ { name: "test slsa build type", taskContent: resolveddependencies.AddSLSATaskDescriptor, config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.SLSAInternalParameters(pr), + ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)), + InternalParameters: getProtoStruct(t, internalparameters.SLSAInternalParameters(pr)), ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddSLSATaskDescriptor), }, }, @@ -313,10 +333,10 @@ func TestGetBuildDefinition(t *testing.T) { name: "test tekton build type", config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa-tekton"}, taskContent: resolveddependencies.AddSLSATaskDescriptor, - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa-tekton", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.TektonInternalParameters(pr), + ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)), + InternalParameters: getProtoStruct(t, internalparameters.TektonInternalParameters(pr)), ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddTektonTaskDescriptor), }, }, @@ -324,23 +344,24 @@ func TestGetBuildDefinition(t *testing.T) { name: "test default build type", config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, taskContent: resolveddependencies.AddSLSATaskDescriptor, - want: slsa.ProvenanceBuildDefinition{ + want: slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.SLSAInternalParameters(pr), + ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)), + InternalParameters: getProtoStruct(t, internalparameters.SLSAInternalParameters(pr)), ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddSLSATaskDescriptor), }, }, } - for _, tc := range tests { + for i := range tests { + tc := &tests[i] t.Run(tc.name, func(t *testing.T) { bd, err := getBuildDefinition(context.Background(), tc.config, pr) if err != nil { t.Fatalf("Did not expect an error but got %v", err) } - if diff := cmp.Diff(tc.want, bd); diff != "" { + if diff := cmp.Diff(&tc.want, &bd, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("getBuildDefinition(): -want +got: %v", diff) } }) @@ -354,7 +375,17 @@ func TestUnsupportedBuildType(t *testing.T) { if err == nil { t.Error("getBuildDefinition(): expected error got nil") } - if diff := cmp.Diff(slsa.ProvenanceBuildDefinition{}, got); diff != "" { + if diff := cmp.Diff(&slsa.BuildDefinition{}, &got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("getBuildDefinition(): -want +got: %s", diff) } } + +func getProtoStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + protoStruct, err := getStruct(data) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + return protoStruct +} diff --git a/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun.go b/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun.go index f3d56f2772..246a1546f8 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun.go @@ -18,7 +18,7 @@ import ( "encoding/json" "fmt" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" builddefinition "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_definition" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/provenance" @@ -43,23 +43,23 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsa sub := extract.SubjectDigests(ctx, tro, slsaConfig) - return provenance.GetSLSA1Statement(tro, sub, bd, bp, slsaConfig), nil + return provenance.GetSLSA1Statement(tro, sub, &bd, bp, slsaConfig) } // byproducts contains the taskRunResults -func byproducts(tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { - byProd := []slsa.ResourceDescriptor{} +func byproducts(tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + byProd := []*intoto.ResourceDescriptor{} for _, key := range tro.Status.Results { content, err := json.Marshal(key.Value) if err != nil { return nil, err } - bp := slsa.ResourceDescriptor{ + bp := intoto.ResourceDescriptor{ Name: fmt.Sprintf(taskRunResults, key.Name), Content: content, MediaType: "application/json", } - byProd = append(byProd, bp) + byProd = append(byProd, &bp) } return byProd, nil } diff --git a/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun_test.go b/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun_test.go index 6e3bd7940e..b221a72409 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun_test.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/taskrun/taskrun_test.go @@ -17,30 +17,31 @@ limitations under the License. package taskrun import ( - "context" "encoding/json" "testing" "time" "github.com/google/go-cmp/cmp" - "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/v1" - v1resourcedescriptor "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" - resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" "github.com/tektoncd/pipeline/pkg/apis/config" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" logtesting "knative.dev/pkg/logging/testing" ) func TestByProducts(t *testing.T) { resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} - tr := &v1.TaskRun{ //nolint:staticcheck + tr := &v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Results: []v1.TaskRunResult{ @@ -57,7 +58,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not marshal results: %s", err) } - want := []slsa.ResourceDescriptor{ + want := []*intoto.ResourceDescriptor{ { Name: "taskRunResults/result-name", Content: resultBytes, @@ -68,7 +69,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not extract byproducts: %s", err) } - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, cmp.Options{protocmp.Transform()}); d != "" { t.Fatalf("byproducts (-want, +got):\n%s", d) } } @@ -82,6 +83,13 @@ func TestTaskRunGenerateAttestation(t *testing.T) { e1BuildStart := time.Unix(1617011400, 0) e1BuildFinished := time.Unix(1617011415, 0) + externalParams := map[string]any{ + "runSpec": tr.Spec, + } + internalParams := map[string]any{ + "tekton-pipelines-feature-flags": config.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + } + resultValue := v1.ResultValue{Type: "string", StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"} resultBytesDigest, err := json.Marshal(resultValue) if err != nil { @@ -93,72 +101,83 @@ func TestTaskRunGenerateAttestation(t *testing.T) { t.Fatalf("Could not marshal results: %s", err) } - want := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, externalParams), + InternalParameters: getStruct(t, internalParams), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/my/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "task", }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - InternalParameters: map[string]any{ - "tekton-pipelines-feature-flags": config.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "taskrun"}}, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, + }, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "taskrun"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "taskRunResults/IMAGE_DIGEST", + Content: resultBytesDigest, + MediaType: pipelinerun.JsonMediaType, }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Name: "taskRunResults/IMAGE_URL", + Content: resultBytesUri, + MediaType: pipelinerun.JsonMediaType, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "taskRunResults/IMAGE_DIGEST", - Content: resultBytesDigest, - MediaType: pipelinerun.JsonMediaType, - }, - { - Name: "taskRunResults/IMAGE_URL", - Content: resultBytesUri, - MediaType: pipelinerun.JsonMediaType, - }, + }, + }, + } + + predicateJSON, err := protojson.Marshal(&slsaPredicate) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + want := intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, }, }, + Predicate: predicateStruct, } got, err := GenerateAttestation(ctx, objects.NewTaskRunObjectV1(tr), &slsaconfig.SlsaConfig{ @@ -169,15 +188,23 @@ func TestTaskRunGenerateAttestation(t *testing.T) { if err != nil { t.Errorf("unwant error: %s", err.Error()) } - if diff := cmp.Diff(want, got); diff != "" { + if diff := cmp.Diff(&want, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("GenerateAttestation(): -want +got: %s", diff) } } -func getResolvedDependencies(tro *objects.TaskRunObjectV1) []v1resourcedescriptor.ResourceDescriptor { - rd, err := resolveddependencies.TaskRun(context.Background(), resolveddependencies.ResolveOptions{}, tro) +func getStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + bytes, err := json.Marshal(data) if err != nil { - return []v1resourcedescriptor.ResourceDescriptor{} + t.Fatalf("error getting proto struct: %v", err) } - return rd + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + return protoStruct } diff --git a/pkg/chains/formats/slsa/v2alpha3/slsav2_test.go b/pkg/chains/formats/slsa/v2alpha3/slsav2_test.go index 09f3f956a6..6d1de44767 100644 --- a/pkg/chains/formats/slsa/v2alpha3/slsav2_test.go +++ b/pkg/chains/formats/slsa/v2alpha3/slsav2_test.go @@ -22,16 +22,19 @@ import ( "time" "github.com/tektoncd/chains/pkg/chains/formats" - "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/config" "github.com/tektoncd/chains/pkg/internal/objectloader" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" pipelineConfig "github.com/tektoncd/pipeline/pkg/apis/config" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" logtesting "knative.dev/pkg/logging/testing" @@ -77,13 +80,10 @@ func TestCreatePayloadError(t *testing.T) { } if err == nil { t.Errorf("Expected error") - } else { - if err.Error() != "intoto does not support type: not a task ref" { - t.Errorf("wrong error returned: '%s'", err.Error()) - } + } else if err.Error() != "intoto does not support type: not a task ref" { + t.Errorf("wrong error returned: '%s'", err.Error()) } }) - } func TestCorrectPayloadType(t *testing.T) { @@ -117,72 +117,82 @@ func TestTaskRunCreatePayload1(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + + extParams := map[string]any{ + "runSpec": tr.Spec, + } + intParams := map[string]any{ + "tekton-pipelines-feature-flags": pipelineConfig.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + } + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, extParams), + InternalParameters: getStruct(t, intParams), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/my/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "task", }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + }, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - InternalParameters: map[string]any{ - "tekton-pipelines-feature-flags": pipelineConfig.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "taskrun"}}, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "taskrun"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "taskRunResults/IMAGE_DIGEST", + Content: resultBytesDigest, + MediaType: pipelinerun.JsonMediaType, }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Name: "taskRunResults/IMAGE_URL", + Content: resultBytesUri, + MediaType: pipelinerun.JsonMediaType, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "taskRunResults/IMAGE_DIGEST", - Content: resultBytesDigest, - MediaType: pipelinerun.JsonMediaType, - }, - { - Name: "taskRunResults/IMAGE_URL", - Content: resultBytesUri, - MediaType: pipelinerun.JsonMediaType, - }, + }, + }, + } + + predicateStruct := getPredicateStruct(t, &slsaPredicate) + + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, }, }, + Predicate: predicateStruct, } i, _ := NewFormatter(cfg) @@ -192,7 +202,7 @@ func TestTaskRunCreatePayload1(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } @@ -220,64 +230,73 @@ func TestTaskRunCreatePayload2(t *testing.T) { ID: "test_builder-2", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: nil, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + + extParams := map[string]any{ + "runSpec": tr.Spec, + } + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, extParams), + InternalParameters: getStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ + { + Uri: "git+https://github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + Name: "task", + }, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "sha:taskdefault"}}, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "sha:taskdefault"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-2", - }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-2", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "taskRunResults/some-uri_DIGEST", + Content: resultBytesDigest, + MediaType: pipelinerun.JsonMediaType, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "taskRunResults/some-uri_DIGEST", - Content: resultBytesDigest, - MediaType: pipelinerun.JsonMediaType, - }, - { - Name: "taskRunResults/some-uri", - Content: resultBytesUri, - MediaType: pipelinerun.JsonMediaType, - }, + { + Name: "taskRunResults/some-uri", + Content: resultBytesUri, + MediaType: pipelinerun.JsonMediaType, }, }, }, } + predicateStruct := getPredicateStruct(t, &slsaPredicate) + + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: nil, + Predicate: predicateStruct, + } + i, _ := NewFormatter(cfg) got, err := i.CreatePayload(ctx, objects.NewTaskRunObjectV1(tr)) if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } @@ -303,52 +322,58 @@ func TestMultipleSubjects(t *testing.T) { ID: "test_builder-multiple", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + + extParams := map[string]any{ + "runSpec": tr.Spec, + } + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, extParams), + InternalParameters: getStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/myimage1", - Digest: common.DigestSet{ - "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, { - Name: "gcr.io/myimage2", - Digest: common.DigestSet{ - "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", - }, + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, }, }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, - }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-multiple", + }, + Metadata: &slsa.BuildMetadata{}, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "taskRunResults/IMAGES", + Content: resultBytes, + MediaType: pipelinerun.JsonMediaType, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-multiple", + }, + } + + predicateStruct := getPredicateStruct(t, &slsaPredicate) + + expected := &intoto.Statement{ + + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/myimage1", + Digest: common.DigestSet{ + "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, - BuildMetadata: slsa.BuildMetadata{}, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "taskRunResults/IMAGES", - Content: resultBytes, - MediaType: pipelinerun.JsonMediaType, - }, + }, { + Name: "gcr.io/myimage2", + Digest: common.DigestSet{ + "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, }, }, + Predicate: predicateStruct, } i, _ := NewFormatter(cfg) @@ -356,7 +381,7 @@ func TestMultipleSubjects(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } @@ -390,104 +415,111 @@ func TestPipelineRunCreatePayload1(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, map[string]any{ + "runSpec": pr.Spec, + }), + InternalParameters: getStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "test.io/test/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "28b123"}, + Name: "pipeline", }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": pr.Spec, + { + Uri: "git+https://github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + Name: "pipelineTask", }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "28b123"}, - Name: "pipeline", - }, - { - URI: "git+https://github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - Name: "pipelineTask", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "pipelineTask", - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - { - URI: "abc", - Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, - Name: "inputs/result", - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", + { + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "pipelineTask", + }, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "pipelineRunResults/CHAINS-GIT_COMMIT", - Content: []uint8(`"abcd"`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/CHAINS-GIT_URL", - Content: []uint8(`"https://git.test.com"`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/IMAGE_URL", - Content: []uint8(`"test.io/test/image"`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/IMAGE_DIGEST", - Content: []uint8(`"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/img-ARTIFACT_INPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/img2-ARTIFACT_OUTPUTS", - Content: []uint8(`{"digest":"sha256:","uri":"def"}`), - MediaType: pipelinerun.JsonMediaType, - }, { - Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), - MediaType: pipelinerun.JsonMediaType, - }, + { + Uri: "abc", + Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + Name: "inputs/result", + }, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "abcd"}, }, }, }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "pipelineRunResults/CHAINS-GIT_COMMIT", + Content: []uint8(`"abcd"`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/CHAINS-GIT_URL", + Content: []uint8(`"https://git.test.com"`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/IMAGE_URL", + Content: []uint8(`"test.io/test/image"`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/IMAGE_DIGEST", + Content: []uint8(`"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/img-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/img2-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:","uri":"def"}`), + MediaType: pipelinerun.JsonMediaType, + }, { + Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), + MediaType: pipelinerun.JsonMediaType, + }, + }, + }, + } + + predicateStruct := getPredicateStruct(t, &slsaPredicate) + + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + }, + Predicate: predicateStruct, } i, _ := NewFormatter(cfg) @@ -497,7 +529,39 @@ func TestPipelineRunCreatePayload1(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got, compare.SLSAV1CompareOptions()...); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } + +func getStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + bytes, err := json.Marshal(data) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + return protoStruct +} + +func getPredicateStruct(t *testing.T, slsaPredicate *slsa.Provenance) *structpb.Struct { + t.Helper() + predicateJSON, err := protojson.Marshal(slsaPredicate) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + return predicateStruct +} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go index dff00786aa..8b41acb0be 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go @@ -16,7 +16,7 @@ package taskrun import ( "context" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" builddefinition "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_definition" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/provenance" @@ -47,11 +47,11 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsa results := append(tro.GetResults(), tro.GetStepResults()...) sub := extract.SubjectsFromBuildArtifact(ctx, results) - return provenance.GetSLSA1Statement(tro, sub, bd, bp, slsaConfig), nil + return provenance.GetSLSA1Statement(tro, sub, &bd, bp, slsaConfig) } -func byproducts(tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { - byProd := []slsa.ResourceDescriptor{} +func byproducts(tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + byProd := []*intoto.ResourceDescriptor{} res, err := results.GetResultsWithoutBuildArtifacts(tro.GetResults(), taskRunResults) if err != nil { diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go index 46bca6a05a..9938287048 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go @@ -22,9 +22,9 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" @@ -32,6 +32,10 @@ import ( "github.com/tektoncd/chains/pkg/internal/objectloader" "github.com/tektoncd/pipeline/pkg/apis/config" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" logtesting "knative.dev/pkg/logging/testing" ) @@ -39,7 +43,7 @@ const jsonMediaType = "application/json" func TestByProducts(t *testing.T) { resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} - tr := &v1.TaskRun{ //nolint:staticcheck + tr := &v1.TaskRun{ Status: v1.TaskRunStatus{ TaskRunStatusFields: v1.TaskRunStatusFields{ Results: []v1.TaskRunResult{ @@ -56,7 +60,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not marshal results: %s", err) } - want := []slsa.ResourceDescriptor{ + want := []*intoto.ResourceDescriptor{ { Name: "taskRunResults/result-name", Content: resultBytes, @@ -67,7 +71,7 @@ func TestByProducts(t *testing.T) { if err != nil { t.Fatalf("Could not extract byproducts: %s", err) } - if d := cmp.Diff(want, got); d != "" { + if d := cmp.Diff(want, got, cmp.Options{protocmp.Transform()}); d != "" { t.Fatalf("byproducts (-want, +got):\n%s", d) } } @@ -81,78 +85,87 @@ func TestTaskRunGenerateAttestation(t *testing.T) { e1BuildStart := time.Unix(1617011400, 0) e1BuildFinished := time.Unix(1617011415, 0) - want := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + externalParams := map[string]any{ + "runSpec": tr.Spec, + } + internalParams := map[string]any{ + "tekton-pipelines-feature-flags": config.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + } + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, externalParams), + InternalParameters: getStruct(t, internalParams), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/my/image/fromstep3", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "task", }, { - Name: "gcr.io/my/image", - Digest: common.DigestSet{ - "sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466", - }, + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - InternalParameters: map[string]any{ - "tekton-pipelines-feature-flags": config.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "taskrun"}}, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "taskrun"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "stepResults/step1_result1", + MediaType: "application/json", + Content: []uint8(`"result-value"`), }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Name: "stepResults/step1_result1-ARTIFACT_OUTPUTS", + MediaType: "application/json", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"gcr.io/my/image/fromstep2"}`), }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "stepResults/step1_result1", - MediaType: "application/json", - Content: []uint8(`"result-value"`), - }, - { - Name: "stepResults/step1_result1-ARTIFACT_OUTPUTS", - MediaType: "application/json", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"gcr.io/my/image/fromstep2"}`), - }, + }, + }, + } + + predicateStruct := getPredicateStruct(t, &slsaPredicate) + + want := intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/my/image/fromstep3", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466", }, }, }, + Predicate: predicateStruct, } got, err := GenerateAttestation(ctx, objects.NewTaskRunObjectV1(tr), &slsaconfig.SlsaConfig{ @@ -163,7 +176,39 @@ func TestTaskRunGenerateAttestation(t *testing.T) { if err != nil { t.Errorf("unwant error: %s", err.Error()) } - if diff := cmp.Diff(want, got); diff != "" { + if diff := cmp.Diff(&want, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("GenerateAttestation(): -want +got: %s", diff) } } + +func getStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + bytes, err := json.Marshal(data) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + return protoStruct +} + +func getPredicateStruct(t *testing.T, slsaPredicate *slsa.Provenance) *structpb.Struct { + t.Helper() + predicateJSON, err := protojson.Marshal(slsaPredicate) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + return predicateStruct +} diff --git a/pkg/chains/formats/slsa/v2alpha4/slsav2.go b/pkg/chains/formats/slsa/v2alpha4/slsav2.go index 6080df5119..3db3fbeeff 100644 --- a/pkg/chains/formats/slsa/v2alpha4/slsav2.go +++ b/pkg/chains/formats/slsa/v2alpha4/slsav2.go @@ -42,7 +42,7 @@ type Slsa struct { } // NewFormatter returns a new v2alpha4 payloader. -func NewFormatter(cfg config.Config) (formats.Payloader, error) { +func NewFormatter(cfg config.Config) (formats.Payloader, error) { //nolint:ireturn return &Slsa{ slsaConfig: &slsaconfig.SlsaConfig{ BuilderID: cfg.Builder.ID, diff --git a/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go b/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go index a3df5df51a..3c82def5a9 100644 --- a/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go @@ -25,11 +25,15 @@ import ( "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/config" "github.com/tektoncd/chains/pkg/internal/objectloader" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" pipelineConfig "github.com/tektoncd/pipeline/pkg/apis/config" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" logtesting "knative.dev/pkg/logging/testing" @@ -118,76 +122,81 @@ func TestTaskRunCreatePayload1(t *testing.T) { ID: "test_builder-1", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, map[string]any{ + "runSpec": tr.Spec, + }), + InternalParameters: getStruct(t, map[string]any{ + "tekton-pipelines-feature-flags": pipelineConfig.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + }), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/my/image/fromstep3", - Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + Uri: "git+https://github.com/test", + Digest: common.DigestSet{"sha1": "ab123"}, + Name: "task", }, { - Name: "gcr.io/my/image", - Digest: common.DigestSet{"sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466"}, + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + { + Uri: "oci://gcr.io/test2/test2", + Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, }, - InternalParameters: map[string]any{ - "tekton-pipelines-feature-flags": pipelineConfig.FeatureFlags{EnableAPIFields: "beta", ResultExtractionMethod: "termination-message"}, + { + Uri: "oci://gcr.io/test3/test3", + Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, }, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/test", - Digest: common.DigestSet{"sha1": "ab123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - URI: "oci://gcr.io/test2/test2", - Digest: common.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"}, - }, - { - URI: "oci://gcr.io/test3/test3", - Digest: common.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"}, - }, - {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "taskrun"}}, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "taskrun"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-1", - }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-1", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "stepResults/step1_result1", + Content: resultBytesStepResult, + MediaType: jsonMediaType, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "stepResults/step1_result1", - Content: resultBytesStepResult, - MediaType: jsonMediaType, - }, - { - Name: "stepResults/step1_result1-ARTIFACT_OUTPUTS", - Content: resultBytesStepResultObj, - MediaType: jsonMediaType, - }, + { + Name: "stepResults/step1_result1-ARTIFACT_OUTPUTS", + Content: resultBytesStepResultObj, + MediaType: jsonMediaType, }, }, }, } + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/my/image/fromstep3", + Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + }, + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{"sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466"}, + }, + }, + Predicate: getPredicateStruct(t, &slsaPredicate), + } + i, _ := NewFormatter(cfg) got, err := i.CreatePayload(ctx, objects.NewTaskRunObjectV1(tr)) @@ -195,7 +204,7 @@ func TestTaskRunCreatePayload1(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } @@ -231,78 +240,79 @@ func TestTaskRunCreatePayload2(t *testing.T) { ID: "test_builder-2", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: nil, - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, map[string]any{ + "runSpec": tr.Spec, + }), + InternalParameters: getStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ + { + Uri: "git+https://github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + Name: "task", + }, + { + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, + }, + { + Name: "inputs/result", + Uri: "https://github.com/tektoncd/pipeline", + Digest: common.DigestSet{"sha1": "7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601"}, }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "git+https://github.com/catalog", - Digest: common.DigestSet{"sha1": "x123"}, - Name: "task", - }, - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, - { - Name: "inputs/result", - URI: "https://github.com/tektoncd/pipeline", - Digest: common.DigestSet{"sha1": "7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601"}, - }, - { - Name: "inputs/result", - URI: "git+https://git.test.com.git", - Digest: common.DigestSet{"sha1": "sha:taskdefault"}, - }, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "sha:taskdefault"}, }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-2", + }, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-2", + }, + Metadata: &slsa.BuildMetadata{ + InvocationId: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: timestamppb.New(e1BuildStart), + FinishedOn: timestamppb.New(e1BuildFinished), + }, + Byproducts: []*intoto.ResourceDescriptor{ + { + Name: "taskRunResults/some-uri_DIGEST", + Content: resultBytesDigest, + MediaType: jsonMediaType, }, - BuildMetadata: slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &e1BuildStart, - FinishedOn: &e1BuildFinished, + { + Name: "taskRunResults/some-uri", + Content: resultBytesURI, + MediaType: jsonMediaType, }, - Byproducts: []slsa.ResourceDescriptor{ - { - Name: "taskRunResults/some-uri_DIGEST", - Content: resultBytesDigest, - MediaType: jsonMediaType, - }, - { - Name: "taskRunResults/some-uri", - Content: resultBytesURI, - MediaType: jsonMediaType, - }, - { - Name: "stepResults/step1_result1-ARTIFACT_INPUTS", - Content: resultBytesObj, - MediaType: jsonMediaType, - }, + { + Name: "stepResults/step1_result1-ARTIFACT_INPUTS", + Content: resultBytesObj, + MediaType: jsonMediaType, }, }, }, } + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: nil, + Predicate: getPredicateStruct(t, &slsaPredicate), + } + i, _ := NewFormatter(cfg) got, err := i.CreatePayload(ctx, objects.NewTaskRunObjectV1(tr)) if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } @@ -320,59 +330,60 @@ func TestMultipleSubjects(t *testing.T) { ID: "test_builder-multiple", }, } - expected := in_toto.ProvenanceStatementSLSA1{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: "gcr.io/foo/bar", - Digest: common.DigestSet{ - "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", - }, - }, - { - Name: "gcr.io/myimage2", - Digest: common.DigestSet{ - "sha256": "9f036c6170dd7aba07e45cf2fe414c7ca792e5ede3bc3a78609e3aab4fa2ff2d", - }, - }, - { - Name: "gcr.io/myimage1", - Digest: common.DigestSet{ - "sha256": "db546e77d11cf34199d965d28b1107f98bcbb7630182b7d847cc31d5d21b47b0", - }, - }, + + slsaPredicate := slsa.Provenance{ + BuildDefinition: &slsa.BuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: getStruct(t, map[string]any{ + "runSpec": tr.Spec, + }), + InternalParameters: getStruct(t, map[string]any{}), + ResolvedDependencies: []*intoto.ResourceDescriptor{ { - Name: "gcr.io/myimage3", - Digest: common.DigestSet{ - "sha256": "8d14f5ded713f263742d371279586b264bde42ee8de97b808d1f5e205f376ade", - }, + Uri: "oci://gcr.io/test1/test1", + Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, }, }, }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: map[string]any{ - "runSpec": tr.Spec, + RunDetails: &slsa.RunDetails{ + Builder: &slsa.Builder{ + Id: "test_builder-multiple", + }, + Metadata: &slsa.BuildMetadata{}, + Byproducts: []*intoto.ResourceDescriptor{}, + }, + } + + expected := &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: "https://slsa.dev/provenance/v1", + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/foo/bar", + Digest: common.DigestSet{ + "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, - InternalParameters: map[string]any{}, - ResolvedDependencies: []slsa.ResourceDescriptor{ - { - URI: "oci://gcr.io/test1/test1", - Digest: common.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"}, - }, + }, + { + Name: "gcr.io/myimage2", + Digest: common.DigestSet{ + "sha256": "9f036c6170dd7aba07e45cf2fe414c7ca792e5ede3bc3a78609e3aab4fa2ff2d", + }, + }, + { + Name: "gcr.io/myimage1", + Digest: common.DigestSet{ + "sha256": "db546e77d11cf34199d965d28b1107f98bcbb7630182b7d847cc31d5d21b47b0", }, }, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: "test_builder-multiple", + { + Name: "gcr.io/myimage3", + Digest: common.DigestSet{ + "sha256": "8d14f5ded713f263742d371279586b264bde42ee8de97b808d1f5e205f376ade", }, - BuildMetadata: slsa.BuildMetadata{}, - Byproducts: []slsa.ResourceDescriptor{}, }, }, + Predicate: getPredicateStruct(t, &slsaPredicate), } i, _ := NewFormatter(cfg) @@ -380,7 +391,39 @@ func TestMultipleSubjects(t *testing.T) { if err != nil { t.Errorf("unexpected error: %s", err.Error()) } - if diff := cmp.Diff(expected, got); diff != "" { + if diff := cmp.Diff(expected, got, cmp.Options{protocmp.Transform()}); diff != "" { t.Errorf("Slsa.CreatePayload(): -want +got: %s", diff) } } + +func getStruct(t *testing.T, data map[string]any) *structpb.Struct { + t.Helper() + bytes, err := json.Marshal(data) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + protoStruct := &structpb.Struct{} + err = protojson.Unmarshal(bytes, protoStruct) + if err != nil { + t.Fatalf("error getting proto struct: %v", err) + } + + return protoStruct +} + +func getPredicateStruct(t *testing.T, slsaPredicate *slsa.Provenance) *structpb.Struct { + t.Helper() + predicateJSON, err := protojson.Marshal(slsaPredicate) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting SLSA predicate proto struct: %v", err) + } + + return predicateStruct +} diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index f20bd93794..0b6169d2b8 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -310,7 +310,7 @@ func (pro *PipelineRunObjectV1) AppendTaskRun(tr *v1.TaskRun) { } // Append TaskRuns to this PipelineRun -func (pro *PipelineRunObjectV1) GetTaskRuns() []*v1.TaskRun { //nolint:staticcheck +func (pro *PipelineRunObjectV1) GetTaskRuns() []*v1.TaskRun { return pro.taskRuns } diff --git a/pkg/chains/storage/grafeas/grafeas_test.go b/pkg/chains/storage/grafeas/grafeas_test.go index 7f9c976daf..57809aee31 100644 --- a/pkg/chains/storage/grafeas/grafeas_test.go +++ b/pkg/chains/storage/grafeas/grafeas_test.go @@ -23,7 +23,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - intoto "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "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/tektoncd/chains/pkg/chains/formats" @@ -34,7 +34,9 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -83,14 +85,12 @@ var ( } // clone taskrun provenance - cloneTaskRunProvenance = intoto.ProvenanceStatement{ - Predicate: slsa.ProvenancePredicate{ - Materials: []common.ProvenanceMaterial{ - { - URI: repoURL, - Digest: common.DigestSet{ - "sha1": commitSHA, - }, + cloneTaskRunPredicate = slsa.ProvenancePredicate{ + Materials: []common.ProvenanceMaterial{ + { + URI: repoURL, + Digest: common.DigestSet{ + "sha1": commitSHA, }, }, }, @@ -119,20 +119,18 @@ var ( } // artifact built taskrun provenance - buildTaskRunProvenance = intoto.ProvenanceStatement{ - StatementHeader: intoto.StatementHeader{ - Subject: []intoto.Subject{ - { - Name: artifactURL1, - Digest: common.DigestSet{ - "sha256": artifactDigest1, - }, + buildTaskRunProvenance = intoto.Statement{ + Subject: []*intoto.ResourceDescriptor{ + { + Name: artifactURL1, + Digest: common.DigestSet{ + "sha256": artifactDigest1, }, - { - Name: artifactURL2, - Digest: common.DigestSet{ - "sha256": artifactDigest2, - }, + }, + { + Name: artifactURL2, + Digest: common.DigestSet{ + "sha256": artifactDigest2, }, }, }, @@ -162,11 +160,8 @@ var ( } // ci pipelinerun provenance - ciPipelineRunProvenance = intoto.ProvenanceStatement{ - StatementHeader: buildTaskRunProvenance.StatementHeader, - Predicate: slsa.ProvenancePredicate{ - Materials: cloneTaskRunProvenance.Predicate.Materials, - }, + ciPipelineRunPredicate = slsa.ProvenancePredicate{ + Materials: cloneTaskRunPredicate.Materials, } ) @@ -257,6 +252,15 @@ func TestBackend_ListOccurrences(t *testing.T) { // - if the StorePayload function can create correct occurrences and store them into grafeas server // - if the RetrievePayloads and RetrieveSignatures functions work properly to fetch correct payloads and signatures func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { + cloneTaskRunProvenance := intoto.Statement{ + Predicate: getPredicateStruct(t, &cloneTaskRunPredicate), + } + + ciPipelineRunProvenance := intoto.Statement{ + Subject: buildTaskRunProvenance.Subject, + Predicate: getPredicateStruct(t, &ciPipelineRunPredicate), + } + tests := []testConfig{ { name: "intoto for clone taskrun, no error, no occurrences created because no artifacts were built.", @@ -264,7 +268,7 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { runObject: &objects.TaskRunObjectV1Beta1{ TaskRun: cloneTaskRun, }, - payload: getRawPayload(t, cloneTaskRunProvenance), + payload: getRawPayload(t, &cloneTaskRunProvenance), signature: "clone taskrun signatures", opts: config.StorageOpts{PayloadFormat: formats.PayloadTypeSlsav1}, }, @@ -277,7 +281,7 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { runObject: &objects.TaskRunObjectV1Beta1{ TaskRun: buildTaskRun, }, - payload: getRawPayload(t, buildTaskRunProvenance), + payload: getRawPayload(t, &buildTaskRunProvenance), signature: "build taskrun signature", opts: config.StorageOpts{PayloadFormat: formats.PayloadTypeSlsav1}, }, @@ -298,12 +302,12 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { wantErr: false, }, { - name: "intoto for the ci pipeline, no error, 2 occurences should be created for the pipelinerun for the 2 artifact generated.", + name: "intoto for the ci pipeline, no error, 2 occurrences should be created for the pipelinerun for the 2 artifact generated.", args: args{ runObject: &objects.PipelineRunObjectV1Beta1{ PipelineRun: ciPipeline, }, - payload: getRawPayload(t, ciPipelineRunProvenance), + payload: getRawPayload(t, &ciPipelineRunProvenance), signature: "ci pipelinerun signature", opts: config.StorageOpts{PayloadFormat: formats.PayloadTypeSlsav1}, }, @@ -333,10 +337,9 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { } defer conn.Close() - // collect all the occurences expected to be created in the server + // collect all the occurrences expected to be created in the server allOccurrencesInServer := []*pb.Occurrence{} for _, test := range tests { - // run the test t.Run(test.name, func(t *testing.T) { ctx := logging.WithLogger(ctx, logtesting.TestLogger(t)) @@ -387,6 +390,7 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { // test attestation storage and retrieval func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConfig, backend Backend) { + t.Helper() if err := backend.StorePayload(ctx, test.args.runObject, test.args.payload, test.args.signature, test.args.opts); (err != nil) != test.wantErr { t.Fatalf("Backend.StorePayload() failed. error:%v, wantErr:%v", err, test.wantErr) } @@ -444,6 +448,7 @@ func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConf // ------------------ occurrences for taskruns and pipelineruns -------------- // BUILD Occurrence for the build taskrun that stores the slsa provenance func getTaskRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { + t.Helper() return &pb.Occurrence{ Name: identifier, ResourceUri: identifier, @@ -472,7 +477,7 @@ func getTaskRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { }, }, Envelope: &pb.Envelope{ - Payload: getRawPayload(t, buildTaskRunProvenance), + Payload: getRawPayload(t, &buildTaskRunProvenance), PayloadType: "application/vnd.in-toto+json", Signatures: []*pb.EnvelopeSignature{ {Sig: []byte("build taskrun signature")}, @@ -483,6 +488,7 @@ func getTaskRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { // ATTESTATION Occurrence for the build taskrun that stores the image attestation func getTaskRunAttestationOcc(t *testing.T, identifier string) *pb.Occurrence { + t.Helper() return &pb.Occurrence{ Name: identifier, ResourceUri: identifier, @@ -506,6 +512,12 @@ func getTaskRunAttestationOcc(t *testing.T, identifier string) *pb.Occurrence { } func getPipelineRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { + t.Helper() + ciPipelineRunProvenance := intoto.Statement{ + Subject: buildTaskRunProvenance.Subject, + Predicate: getPredicateStruct(t, &ciPipelineRunPredicate), + } + return &pb.Occurrence{ Name: identifier, ResourceUri: identifier, @@ -540,7 +552,7 @@ func getPipelineRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { }, }, Envelope: &pb.Envelope{ - Payload: getRawPayload(t, ciPipelineRunProvenance), + Payload: getRawPayload(t, &ciPipelineRunProvenance), PayloadType: "application/vnd.in-toto+json", Signatures: []*pb.EnvelopeSignature{ {Sig: []byte("ci pipelinerun signature")}, @@ -550,6 +562,7 @@ func getPipelineRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { } func getRawPayload(t *testing.T, in interface{}) []byte { + t.Helper() rawPayload, err := json.Marshal(in) if err != nil { t.Errorf("Unable to marshal the provenance: %v", in) @@ -559,7 +572,7 @@ func getRawPayload(t *testing.T, in interface{}) []byte { // set up the connection between grafeas server and client // and return the client object to the caller -func setupConnection() (*grpc.ClientConn, pb.GrafeasClient, error) { +func setupConnection() (*grpc.ClientConn, pb.GrafeasClient, error) { //nolint:ireturn serv := grpc.NewServer() pb.RegisterGrafeasServer(serv, &mockGrafeasServer{}) @@ -613,7 +626,7 @@ func (s *mockGrafeasServer) CreateOccurrence(ctx context.Context, req *pb.Create occ := req.GetOccurrence() noteName := req.GetOccurrence().NoteName resourceUri := req.GetOccurrence().ResourceUri - occ.Name = resourceUri // mock how the occurrence ID (name) is outputed. + occ.Name = resourceUri // mock how the occurrence ID (name) is outputted. if note, ok := s.entries[noteName]; ok { if _, ok := note.occurrences[resourceUri]; ok { @@ -700,7 +713,6 @@ func (s *mockGrafeasServer) getOccurrencesByFilter(filter string, occurrences [] // mock how uri filter works uris := parseURIFilterString(filter) - // result result result := []*pb.Occurrence{} for _, occ := range occurrences { @@ -735,3 +747,19 @@ func parseURIFilterString(filter string) []string { return results } + +func getPredicateStruct(t *testing.T, predicate *slsa.ProvenancePredicate) *structpb.Struct { + t.Helper() + predicateJSON, err := json.Marshal(predicate) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + predicateStruct := &structpb.Struct{} + err = protojson.Unmarshal(predicateJSON, predicateStruct) + if err != nil { + t.Fatalf("error getting predicate struct: %v", err) + } + + return predicateStruct +} diff --git a/pkg/chains/storage/oci/attestation.go b/pkg/chains/storage/oci/attestation.go index 0fd6709f33..5856c6d0fe 100644 --- a/pkg/chains/storage/oci/attestation.go +++ b/pkg/chains/storage/oci/attestation.go @@ -19,7 +19,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/pkg/errors" "github.com/sigstore/cosign/v2/pkg/oci/mutate" ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote" @@ -30,7 +30,7 @@ import ( ) var ( - _ api.Storer[name.Digest, in_toto.Statement] = &AttestationStorer{} + _ api.Storer[name.Digest, *intoto.Statement] = &AttestationStorer{} ) // AttestationStorer stores in-toto Attestation payloads in OCI registries. @@ -52,7 +52,8 @@ func NewAttestationStorer(opts ...AttestationStorerOption) (*AttestationStorer, return s, nil } -func (s *AttestationStorer) Store(ctx context.Context, req *api.StoreRequest[name.Digest, in_toto.Statement]) (*api.StoreResponse, error) { +// Store saves the given statement. +func (s *AttestationStorer) Store(ctx context.Context, req *api.StoreRequest[name.Digest, *intoto.Statement]) (*api.StoreResponse, error) { logger := logging.FromContext(ctx) repo := req.Artifact.Repository diff --git a/pkg/chains/storage/oci/legacy.go b/pkg/chains/storage/oci/legacy.go index 4cb0d05875..fdf355067f 100644 --- a/pkg/chains/storage/oci/legacy.go +++ b/pkg/chains/storage/oci/legacy.go @@ -26,7 +26,7 @@ import ( "knative.dev/pkg/logging" - "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/google/go-containerregistry/pkg/authn/k8schain" @@ -92,7 +92,7 @@ func (b *Backend) StorePayload(ctx context.Context, obj objects.TektonObject, ra } if _, ok := formats.IntotoAttestationSet[storageOpts.PayloadFormat]; ok { - attestation := in_toto.Statement{} + attestation := intoto.Statement{} if err := json.Unmarshal(rawPayload, &attestation); err != nil { return errors.Wrap(err, "unmarshal attestation") } @@ -106,7 +106,7 @@ func (b *Backend) StorePayload(ctx context.Context, obj objects.TektonObject, ra return nil } - return b.uploadAttestation(ctx, attestation, signature, storageOpts, auth) + return b.uploadAttestation(ctx, &attestation, signature, storageOpts, auth) } // Fallback in case unsupported payload format is used or the deprecated "tekton" format @@ -152,7 +152,7 @@ func (b *Backend) uploadSignature(ctx context.Context, format simple.SimpleConta return nil } -func (b *Backend) uploadAttestation(ctx context.Context, attestation in_toto.Statement, signature string, storageOpts config.StorageOpts, remoteOpts ...remote.Option) error { +func (b *Backend) uploadAttestation(ctx context.Context, attestation *intoto.Statement, signature string, storageOpts config.StorageOpts, remoteOpts ...remote.Option) error { logger := logging.FromContext(ctx) // upload an attestation for each subject logger.Info("Starting to upload attestations to OCI ...") @@ -176,7 +176,7 @@ func (b *Backend) uploadAttestation(ctx context.Context, attestation in_toto.Sta } // TODO: make these creation opts. store.remoteOpts = remoteOpts - if _, err := store.Store(ctx, &api.StoreRequest[name.Digest, in_toto.Statement]{ + if _, err := store.Store(ctx, &api.StoreRequest[name.Digest, *intoto.Statement]{ Object: nil, Artifact: ref, Payload: attestation, diff --git a/pkg/chains/storage/oci/oci_test.go b/pkg/chains/storage/oci/oci_test.go index e7e9b01150..5e142297c9 100644 --- a/pkg/chains/storage/oci/oci_test.go +++ b/pkg/chains/storage/oci/oci_test.go @@ -25,11 +25,14 @@ import ( "github.com/tektoncd/chains/pkg/chains/formats/simple" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/config" + "google.golang.org/protobuf/types/known/structpb" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/registry" "github.com/google/go-containerregistry/pkg/v1/remote" + intoto "github.com/in-toto/attestation/go/v1" "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/payload" @@ -84,20 +87,18 @@ func TestBackend_StorePayload(t *testing.T) { }, } - intotoStatement := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: u.Host + "/task/" + tr.Name, - Digest: common.DigestSet{ - algo: hex, - }, + intotoStatement := &intoto.Statement{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: u.Host + "/task/" + tr.Name, + Digest: common.DigestSet{ + algo: hex, }, }, }, - Predicate: slsa.ProvenancePredicate{}, + Predicate: &structpb.Struct{}, } type fields struct { @@ -145,7 +146,7 @@ func TestBackend_StorePayload(t *testing.T) { object: objects.NewTaskRunObjectV1Beta1(tr), }, args: args{ - payload: in_toto.Statement{}, + payload: intoto.Statement{}, signature: "", storageOpts: config.StorageOpts{ PayloadFormat: formats.PayloadTypeSlsav1, @@ -210,7 +211,7 @@ func TestBackend_StorePayload(t *testing.T) { object: objects.NewPipelineRunObjectV1Beta1(pr), }, args: args{ - payload: in_toto.Statement{}, + payload: intoto.Statement{}, signature: "", storageOpts: config.StorageOpts{ PayloadFormat: formats.PayloadTypeSlsav1, diff --git a/pkg/chains/storage/pubsub/pubsub_test.go b/pkg/chains/storage/pubsub/pubsub_test.go index a7505674ba..9b4eab8741 100644 --- a/pkg/chains/storage/pubsub/pubsub_test.go +++ b/pkg/chains/storage/pubsub/pubsub_test.go @@ -19,7 +19,7 @@ import ( "log" "testing" - "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/config" @@ -32,7 +32,10 @@ import ( func TestBackend_StorePayload(t *testing.T) { // pretty much anything that has no Subject - sampleIntotoStatementBytes, _ := json.Marshal(in_toto.Statement{}) + sampleIntotoStatementBytes, err := json.Marshal(intoto.Statement{}) + if err != nil { + t.Fatalf("error getting statement: %v", err) + } logger := logtesting.TestLogger(t) type fields struct { diff --git a/pkg/chains/storage/tekton/tekton.go b/pkg/chains/storage/tekton/tekton.go index 0849032382..f3a7b9c46d 100644 --- a/pkg/chains/storage/tekton/tekton.go +++ b/pkg/chains/storage/tekton/tekton.go @@ -18,7 +18,7 @@ import ( "encoding/base64" "fmt" - "github.com/in-toto/in-toto-golang/in_toto" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/chains/signing" "github.com/tektoncd/chains/pkg/chains/storage/api" @@ -59,7 +59,7 @@ func (b *Backend) StorePayload(ctx context.Context, obj objects.TektonObject, ra client: b.pipelineclientset, key: opts.ShortKey, } - if _, err := store.Store(ctx, &api.StoreRequest[objects.TektonObject, *in_toto.Statement]{ + if _, err := store.Store(ctx, &api.StoreRequest[objects.TektonObject, *intoto.Statement]{ Object: obj, Artifact: obj, // We don't actually use payload - we store the raw bundle values directly. @@ -89,7 +89,7 @@ func (b *Backend) retrieveAnnotationValue(ctx context.Context, obj objects.Tekto var annotationValue string annotations, err := obj.GetLatestAnnotations(ctx, b.pipelineclientset) if err != nil { - return "", fmt.Errorf("error retrieving the annotation value for the key %q: %s", annotationKey, err) + return "", fmt.Errorf("error retrieving the annotation value for the key %q: %w", annotationKey, err) } val, ok := annotations[annotationKey] @@ -99,7 +99,7 @@ func (b *Backend) retrieveAnnotationValue(ctx context.Context, obj objects.Tekto if decode { decodedAnnotation, err := base64.StdEncoding.DecodeString(val) if err != nil { - return "", fmt.Errorf("error decoding the annotation value for the key %q: %s", annotationKey, err) + return "", fmt.Errorf("error decoding the annotation value for the key %q: %w", annotationKey, err) } annotationValue = string(decodedAnnotation) } else { @@ -153,11 +153,11 @@ type Storer struct { } var ( - _ api.Storer[objects.TektonObject, *in_toto.Statement] = &Storer{} + _ api.Storer[objects.TektonObject, *intoto.Statement] = &Storer{} ) // Store stores the statement in the TaskRun metadata as an annotation. -func (s *Storer) Store(ctx context.Context, req *api.StoreRequest[objects.TektonObject, *in_toto.Statement]) (*api.StoreResponse, error) { +func (s *Storer) Store(ctx context.Context, req *api.StoreRequest[objects.TektonObject, *intoto.Statement]) (*api.StoreResponse, error) { logger := logging.FromContext(ctx) obj := req.Object diff --git a/test/e2e_test.go b/test/e2e_test.go index 585c47ff95..4e1af98906 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -27,7 +27,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io" "os" "strings" "testing" @@ -37,6 +36,7 @@ import ( "cloud.google.com/go/storage" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/sigstore/pkg/cryptoutils" @@ -49,6 +49,7 @@ import ( v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + "google.golang.org/protobuf/encoding/protojson" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logtesting "knative.dev/pkg/logging/testing" ) @@ -396,6 +397,7 @@ func TestFulcio(t *testing.T) { } func base64Decode(t *testing.T, s string) []byte { + t.Helper() b, err := base64.StdEncoding.DecodeString(s) if err != nil { b, err = base64.URLEncoding.DecodeString(s) @@ -406,17 +408,6 @@ func base64Decode(t *testing.T, s string) []byte { return b } -// DSSE messages contain the signature and payload in one object, but our interface expects a signature and payload -// This means we need to use one field and ignore the other. The DSSE verifier upstream uses the signature field and ignores -// The message field, but we want the reverse here. -type reverseDSSEVerifier struct { - signature.Verifier -} - -func (w *reverseDSSEVerifier) VerifySignature(s io.Reader, m io.Reader, opts ...signature.VerifyOption) error { - return w.Verifier.VerifySignature(m, nil, opts...) -} - func TestOCIStorage(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) c, ns, cleanup := setup(ctx, t, setupOpts{registry: true}) @@ -691,14 +682,6 @@ func getTaskRunObjectV1(ns string) objects.TektonObject { return o } -func getTaskRunObjectV1WithParams(ns string, params []v1.Param) objects.TektonObject { - tr, _ := taskRunFromFile("testdata/type-hinting/taskrun.json") - o := objects.NewTaskRunObjectV1(tr) - o.Namespace = ns - o.Spec.Params = params - return o -} - var imagePipelineRun = v1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "image-pipelinerun", @@ -804,11 +787,16 @@ func TestProvenanceMaterials(t *testing.T) { if err != nil { t.Error(err) } - var actualProvenance in_toto.Statement + var actualProvenance intoto.Statement if err := json.Unmarshal(bodyBytes, &actualProvenance); err != nil { t.Error(err) } - predicateBytes, err := json.Marshal(actualProvenance.Predicate) + + predicateBytes, err := protojson.Marshal(actualProvenance.Predicate) + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(bodyBytes, &actualProvenance); err != nil { t.Error(err) } diff --git a/test/examples_test.go b/test/examples_test.go index 8dcb2e85ea..334c07cad9 100644 --- a/test/examples_test.go +++ b/test/examples_test.go @@ -39,9 +39,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - intoto "github.com/in-toto/in-toto-golang/in_toto" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" - slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/structpb" + + slsa1 "github.com/in-toto/attestation/go/predicates/provenance/v1" + intoto "github.com/in-toto/attestation/go/v1" + slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/tektoncd/chains/pkg/chains/objects" @@ -172,7 +175,7 @@ func runInTotoFormatterTests(ctx context.Context, t *testing.T, ns string, c *cl // TODO: Commenting this out for now. Causes race condition where tests write and revert the chains-config // and signing-secrets out of order // t.Parallel() - + t.Helper() for path, obj := range test.getExampleObjects(t, ns) { obj := obj t.Run(path, func(t *testing.T) { @@ -192,39 +195,57 @@ func runInTotoFormatterTests(ctx context.Context, t *testing.T, ns string, c *cl if test.predicate == "slsav1.0" { // make sure provenance is correct - var gotProvenance intoto.ProvenanceStatementSLSA1 + var gotProvenance intoto.Statement if err := json.Unmarshal(payload, &gotProvenance); err != nil { t.Fatal(err) } - expected := expectedProvenanceSLSA1(t, ctx, path, completed, test.outputLocation, ns, c) + expected := expectedProvenanceSLSA1(ctx, t, path, completed, test.outputLocation, ns, c) + + expPredicateStruct := expected.Predicate + expected.Predicate = nil + + gotPredicateStruct := gotProvenance.Predicate + gotProvenance.Predicate = nil opts := []cmp.Option{ // Annotations and labels may contain release specific information. Ignore // those to avoid brittle tests. - cmpopts.IgnoreFields(slsa1.ProvenanceBuildDefinition{}, "InternalParameters"), + cmpopts.IgnoreFields(slsa1.BuildDefinition{}, "InternalParameters"), cmpopts.IgnoreMapEntries(ignoreEnvironmentAnnotationsAndLabels), + protocmp.Transform(), } - if diff := cmp.Diff(expected, gotProvenance, opts...); diff != "" { + if diff := cmp.Diff(&expected, &gotProvenance, opts...); diff != "" { t.Errorf("provenance dont match: -want +got: %s", diff) } + + comparePredicates[slsa1.Provenance](t, expPredicateStruct, gotPredicateStruct, opts) } else { - var gotProvenance intoto.ProvenanceStatement + var gotProvenance intoto.Statement if err := json.Unmarshal(payload, &gotProvenance); err != nil { t.Fatal(err) } - expected := expectedProvenance(t, ctx, path, completed, test.outputLocation, ns, c) + expected := expectedProvenance(ctx, t, path, completed, test.outputLocation, ns, c) + + expPredicateStruct := expected.Predicate + expected.Predicate = nil + + gotPredicateStruct := gotProvenance.Predicate + gotProvenance.Predicate = nil opts := []cmp.Option{ // Annotations and labels may contain release specific information. Ignore // those to avoid brittle tests. - cmpopts.IgnoreFields(slsa.ProvenanceInvocation{}, "Environment"), + cmpopts.IgnoreFields(slsa02.ProvenanceInvocation{}, "Environment"), cmpopts.IgnoreMapEntries(ignoreEnvironmentAnnotationsAndLabels), + protocmp.Transform(), } - if diff := cmp.Diff(expected, gotProvenance, opts...); diff != "" { + if diff := cmp.Diff(&expected, &gotProvenance, opts...); diff != "" { t.Errorf("provenance dont match: -want +got: %s", diff) } + + comparePredicates[slsa02.ProvenancePredicate](t, expPredicateStruct, gotPredicateStruct, opts) } // verify signature @@ -271,32 +292,34 @@ func (v *verifier) Public() crypto.PublicKey { return v.pub } -func expectedProvenanceSLSA1(t *testing.T, ctx context.Context, example string, obj objects.TektonObject, outputLocation string, ns string, c *clients) intoto.ProvenanceStatementSLSA1 { +func expectedProvenanceSLSA1(ctx context.Context, t *testing.T, example string, obj objects.TektonObject, outputLocation string, ns string, c *clients) intoto.Statement { + t.Helper() switch obj.(type) { case *objects.TaskRunObjectV1: f := expectedTaskRunProvenanceFormat(t, example, obj, outputLocation) return expectedAttestationSLSA1(t, example, f, outputLocation) case *objects.PipelineRunObjectV1: - f := expectedPipelineRunProvenanceFormat(t, ctx, example, obj, outputLocation, ns, c) + f := expectedPipelineRunProvenanceFormat(ctx, t, obj, ns, c) return expectedAttestationSLSA1(t, example, f, outputLocation) default: t.Error("Unexpected type trying to get provenance") } - return intoto.ProvenanceStatementSLSA1{} + return intoto.Statement{} } -func expectedProvenance(t *testing.T, ctx context.Context, example string, obj objects.TektonObject, outputLocation string, ns string, c *clients) intoto.ProvenanceStatement { +func expectedProvenance(ctx context.Context, t *testing.T, example string, obj objects.TektonObject, outputLocation string, ns string, c *clients) intoto.Statement { + t.Helper() switch obj.(type) { case *objects.TaskRunObjectV1: f := expectedTaskRunProvenanceFormat(t, example, obj, outputLocation) return expectedAttestation(t, example, f, outputLocation) case *objects.PipelineRunObjectV1: - f := expectedPipelineRunProvenanceFormat(t, ctx, example, obj, outputLocation, ns, c) + f := expectedPipelineRunProvenanceFormat(ctx, t, obj, ns, c) return expectedAttestation(t, example, f, outputLocation) default: t.Error("Unexpected type trying to get provenance") } - return intoto.ProvenanceStatement{} + return intoto.Statement{} } type URIDigestPair struct { @@ -317,6 +340,7 @@ type Format struct { } func expectedTaskRunProvenanceFormat(t *testing.T, example string, obj objects.TektonObject, outputLocation string) Format { + t.Helper() tr := obj.GetObject().(*v1.TaskRun) name := tr.Name @@ -352,7 +376,8 @@ func expectedTaskRunProvenanceFormat(t *testing.T, example string, obj objects.T } } -func expectedPipelineRunProvenanceFormat(t *testing.T, ctx context.Context, example string, obj objects.TektonObject, outputLocation string, ns string, c *clients) Format { +func expectedPipelineRunProvenanceFormat(ctx context.Context, t *testing.T, obj objects.TektonObject, ns string, c *clients) Format { + t.Helper() pr := obj.GetObject().(*v1.PipelineRun) buildStartTimes := []string{} @@ -399,17 +424,20 @@ func expectedPipelineRunProvenanceFormat(t *testing.T, ctx context.Context, exam } } -func expectedAttestationSLSA1(t *testing.T, example string, f Format, outputLocation string) intoto.ProvenanceStatementSLSA1 { +func expectedAttestationSLSA1(t *testing.T, example string, f Format, outputLocation string) intoto.Statement { + t.Helper() b := readExpectedAttestationBytes(t, example, f, outputLocation) return readExpectedAttestationSLSA1(t, b) } -func expectedAttestation(t *testing.T, example string, f Format, outputLocation string) intoto.ProvenanceStatement { +func expectedAttestation(t *testing.T, example string, f Format, outputLocation string) intoto.Statement { + t.Helper() b := readExpectedAttestationBytes(t, example, f, outputLocation) return readExpectedAttestation(t, b) } func readExpectedAttestationBytes(t *testing.T, example string, f Format, outputLocation string) *bytes.Buffer { + t.Helper() path := filepath.Join("testdata", outputLocation, strings.Replace(filepath.Base(example), ".yaml", ".json", 1)) t.Logf("Reading expected provenance from %s", path) contents, err := ioutil.ReadFile(path) @@ -429,16 +457,18 @@ func readExpectedAttestationBytes(t *testing.T, example string, f Format, output return b } -func readExpectedAttestationSLSA1(t *testing.T, b *bytes.Buffer) intoto.ProvenanceStatementSLSA1 { - var expected intoto.ProvenanceStatementSLSA1 +func readExpectedAttestationSLSA1(t *testing.T, b *bytes.Buffer) intoto.Statement { + t.Helper() + var expected intoto.Statement if err := json.Unmarshal(b.Bytes(), &expected); err != nil { t.Fatal(err) } return expected } -func readExpectedAttestation(t *testing.T, b *bytes.Buffer) intoto.ProvenanceStatement { - var expected intoto.ProvenanceStatement +func readExpectedAttestation(t *testing.T, b *bytes.Buffer) intoto.Statement { + t.Helper() + var expected intoto.Statement if err := json.Unmarshal(b.Bytes(), &expected); err != nil { t.Fatal(err) } @@ -446,6 +476,7 @@ func readExpectedAttestation(t *testing.T, b *bytes.Buffer) intoto.ProvenanceSta } func getTaskRunExamples(t *testing.T, ns string) map[string]objects.TektonObject { + t.Helper() examples := make(map[string]objects.TektonObject) for _, example := range getExamplePaths(t, taskRunExamplesPath) { examples[example] = taskRunFromExample(t, ns, example) @@ -454,6 +485,7 @@ func getTaskRunExamples(t *testing.T, ns string) map[string]objects.TektonObject } func getTaskRunWithTypeHintedResultsExamples(t *testing.T, ns string) map[string]objects.TektonObject { + t.Helper() path := "../examples/v2alpha4/task-with-object-type-hinting.yaml" trs := make(map[string]objects.TektonObject) trs[path] = taskRunFromExample(t, ns, path) @@ -461,6 +493,7 @@ func getTaskRunWithTypeHintedResultsExamples(t *testing.T, ns string) map[string } func getPipelineRunExamples(t *testing.T, ns string) map[string]objects.TektonObject { + t.Helper() examples := make(map[string]objects.TektonObject) for _, example := range getExamplePaths(t, pipelineRunExamplesPath) { examples[example] = pipelineRunFromExample(t, ns, example) @@ -469,6 +502,7 @@ func getPipelineRunExamples(t *testing.T, ns string) map[string]objects.TektonOb } func getExamplePaths(t *testing.T, dir string) []string { + t.Helper() var examplePaths []string err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -491,6 +525,7 @@ func getExamplePaths(t *testing.T, dir string) []string { } func taskRunFromExample(t *testing.T, ns, example string) objects.TektonObject { + t.Helper() contents, err := ioutil.ReadFile(example) if err != nil { t.Fatal(err) @@ -504,6 +539,7 @@ func taskRunFromExample(t *testing.T, ns, example string) objects.TektonObject { } func pipelineRunFromExample(t *testing.T, ns, example string) objects.TektonObject { + t.Helper() contents, err := ioutil.ReadFile(example) if err != nil { t.Fatal(err) @@ -530,3 +566,32 @@ func ignoreEnvironmentAnnotationsAndLabels(key string, value any) bool { } return false } + +func comparePredicates[T any](t *testing.T, expPredicateStruct, gotPredicateStruct *structpb.Struct, opts []cmp.Option) { + t.Helper() + expJSON, err := expPredicateStruct.MarshalJSON() + if err != nil { + t.Fatalf("error getting predicate json: %v", err) + } + + gotJSON, err := gotPredicateStruct.MarshalJSON() + if err != nil { + t.Fatalf("error getting predicate json: %v", err) + } + + var expectedPredicate T + json.Unmarshal(expJSON, &expectedPredicate) + if err != nil { + t.Fatalf("error getting predicate original struct: %v", err) + } + + var gotPredicate T + json.Unmarshal(gotJSON, &gotPredicate) + if err != nil { + t.Fatalf("error getting predicate original struct: %v", err) + } + + if diff := cmp.Diff(&expectedPredicate, &gotPredicate, opts...); diff != "" { + t.Errorf("predicates dont match: -want +got: %s", diff) + } +} diff --git a/test/test_utils.go b/test/test_utils.go index a798bf278f..e46ca3cd35 100644 --- a/test/test_utils.go +++ b/test/test_utils.go @@ -130,6 +130,7 @@ var simpleTaskRun = v1.TaskRun{ func makeBucket(t *testing.T, client *storage.Client) (string, func()) { // Make a bucket + t.Helper() rand.Seed(time.Now().UnixNano()) testBucketName := fmt.Sprintf("tekton-chains-e2e-%d", rand.Intn(1000)) @@ -161,6 +162,7 @@ func makeBucket(t *testing.T, client *storage.Client) (string, func()) { } func readObj(t *testing.T, bucket, name string, client *storage.Client) io.Reader { + t.Helper() ctx := context.Background() reader, err := client.Bucket(bucket).Object(name).NewReader(ctx) if err != nil { @@ -170,6 +172,7 @@ func readObj(t *testing.T, bucket, name string, client *storage.Client) io.Reade } func setConfigMap(ctx context.Context, t *testing.T, c *clients, data map[string]string) func() { + t.Helper() // Change the config to be GCS storage with this bucket. // Note(rgreinho): This comment does not look right... clean := updateConfigMap(ctx, t, c, data, namespace, "chains-config") @@ -183,6 +186,7 @@ func setConfigMap(ctx context.Context, t *testing.T, c *clients, data map[string } func setupPipelinesFeatureFlags(ctx context.Context, t *testing.T, c *clients, data map[string]string) func() { + t.Helper() pipelinesNs := "tekton-pipelines" clean := updateConfigMap(ctx, t, c, data, pipelinesNs, "feature-flags") @@ -196,6 +200,7 @@ func setupPipelinesFeatureFlags(ctx context.Context, t *testing.T, c *clients, d } func updateConfigMap(ctx context.Context, t *testing.T, c *clients, data map[string]string, ns, configMapName string) func() { + t.Helper() cm, err := c.KubeClient.CoreV1().ConfigMaps(ns).Get(ctx, configMapName, metav1.GetOptions{}) if err != nil { t.Fatal(err) @@ -233,6 +238,7 @@ func updateConfigMap(ctx context.Context, t *testing.T, c *clients, data map[str } func printDebugging(t *testing.T, obj objects.TektonObject) { + t.Helper() kind := obj.GetObjectKind().GroupVersionKind().Kind t.Logf("============================== %s logs ==============================", obj.GetGVK()) @@ -249,6 +255,7 @@ func printDebugging(t *testing.T, obj objects.TektonObject) { } func verifySignature(ctx context.Context, t *testing.T, c *clients, obj objects.TektonObject) { + t.Helper() // Retrieve the configuration. chainsConfig, err := c.KubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, "chains-config", metav1.GetOptions{}) if err != nil { diff --git a/test/testdata/slsa/v1/pipeline-output-image.json b/test/testdata/slsa/v1/pipeline-output-image.json index 2c57b0090b..5a303c06c5 100644 --- a/test/testdata/slsa/v1/pipeline-output-image.json +++ b/test/testdata/slsa/v1/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v0.2", + "type": "https://in-toto.io/Statement/v0.1", + "predicate_type": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v1/task-output-image.json b/test/testdata/slsa/v1/task-output-image.json index 6f50533549..c816782574 100644 --- a/test/testdata/slsa/v1/task-output-image.json +++ b/test/testdata/slsa/v1/task-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v0.2", + "type": "https://in-toto.io/Statement/v0.1", + "predicate_type": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2/task-output-image.json b/test/testdata/slsa/v2/task-output-image.json index 46bcf76a57..aa4ebf9575 100644 --- a/test/testdata/slsa/v2/task-output-image.json +++ b/test/testdata/slsa/v2/task-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v0.2", + "type": "https://in-toto.io/Statement/v0.1", + "predicate_type": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha3/pipeline-output-image.json b/test/testdata/slsa/v2alpha3/pipeline-output-image.json index 168426c439..74ffc938d4 100644 --- a/test/testdata/slsa/v2alpha3/pipeline-output-image.json +++ b/test/testdata/slsa/v2alpha3/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v1", + "type": "https://in-toto.io/Statement/v1", + "predicate_type": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", @@ -105,7 +105,7 @@ "id": "https://tekton.dev/chains/v2" }, "metadata": { - "invocationID": "{{.UID}}", + "invocationId": "{{.UID}}", "startedOn": "{{.PipelineStartedOn}}", "finishedOn": "{{.PipelineFinishedOn}}" }, diff --git a/test/testdata/slsa/v2alpha3/task-output-image.json b/test/testdata/slsa/v2alpha3/task-output-image.json index 696ce8d528..c8e8956401 100644 --- a/test/testdata/slsa/v2alpha3/task-output-image.json +++ b/test/testdata/slsa/v2alpha3/task-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v1", + "type": "https://in-toto.io/Statement/v1", + "predicate_type": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", @@ -53,7 +53,7 @@ "id": "https://tekton.dev/chains/v2" }, "metadata": { - "invocationID": "{{.UID}}", + "invocationId": "{{.UID}}", "startedOn": "{{index .BuildStartTimes 0}}", "finishedOn": "{{index .BuildFinishedTimes 0}}" }, diff --git a/test/testdata/slsa/v2alpha4/task-output-image.json b/test/testdata/slsa/v2alpha4/task-output-image.json index 33deb9711d..440ea595f3 100644 --- a/test/testdata/slsa/v2alpha4/task-output-image.json +++ b/test/testdata/slsa/v2alpha4/task-output-image.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v1", + "type": "https://in-toto.io/Statement/v1", + "predicate_type": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", @@ -53,7 +53,7 @@ "id": "https://tekton.dev/chains/v2" }, "metadata": { - "invocationID": "{{.UID}}", + "invocationId": "{{.UID}}", "startedOn": "{{index .BuildStartTimes 0}}", "finishedOn": "{{index .BuildFinishedTimes 0}}" } diff --git a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json index 0965545364..9f835644a3 100644 --- a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json +++ b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json @@ -1,6 +1,6 @@ { - "_type": "https://in-toto.io/Statement/v0.1", - "predicateType": "https://slsa.dev/provenance/v1", + "type": "https://in-toto.io/Statement/v1", + "predicate_type": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/img2", @@ -139,7 +139,7 @@ "id": "https://tekton.dev/chains/v2" }, "metadata": { - "invocationID": "{{.UID}}", + "invocationId": "{{.UID}}", "startedOn": "{{index .BuildStartTimes 0}}", "finishedOn": "{{index .BuildFinishedTimes 0}}" }, diff --git a/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.go b/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.go new file mode 100644 index 0000000000..fede805045 --- /dev/null +++ b/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.go @@ -0,0 +1,138 @@ +/* +Validator APIs for SLSA Provenance v1 protos. +*/ +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("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 { + // check valid timestamps + s := m.GetStartedOn() + if s != nil { + if err := s.CheckValid(); err != nil { + return fmt.Errorf("buildMetadata.startedOn error: %w", err) + } + } + + f := m.GetFinishedOn() + if f != nil { + if err := f.CheckValid(); err != nil { + return fmt.Errorf("buildMetadata.finishedOn error: %w", err) + } + } + + return nil +} + +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() + 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 + ext := b.GetExternalParameters() + if ext == nil || proto.Equal(ext, &structpb.Struct{}) { + return ErrExternalParamsRequired + } + + // check that all resolvedDependencies are valid RDs + resolvedDeps := b.GetResolvedDependencies() + 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 || proto.Equal(builder, &Builder{}) { + return ErrBuilderRequired + } + + // check the Builder + if err := builder.Validate(); err != nil { + return fmt.Errorf("runDetails.builder error: %w", err) + } + + // check the Metadata, if present + metadata := r.GetMetadata() + if metadata != nil && !proto.Equal(metadata, &BuildMetadata{}) { + if err := metadata.Validate(); err != nil { + return fmt.Errorf("Invalid RunDetails.Metadata: %w", err) + } + } + + // check that all byproducts are valid RDs + byproducts := r.GetByproducts() + 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 || proto.Equal(buildDef, &BuildDefinition{}) { + return ErrBuildDefinitionRequired + } + + // check the BuildDefinition + if err := buildDef.Validate(); err != nil { + return fmt.Errorf("provenance.buildDefinition error: %w", err) + } + + // the runDetails field is required for SLSA Build L1 + runDetails := p.GetRunDetails() + if runDetails == nil || proto.Equal(runDetails, &RunDetails{}) { + return ErrRunDetailsRequired + } + + // check the RunDetails + if err := runDetails.Validate(); err != nil { + return fmt.Errorf("provenance.runDetails error: %w", err) + } + + return nil +} diff --git a/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.pb.go b/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.pb.go new file mode 100644 index 0000000000..fae536efd2 --- /dev/null +++ b/vendor/github.com/in-toto/attestation/go/predicates/provenance/v1/provenance.pb.go @@ -0,0 +1,577 @@ +// Keep in sync with schema at https://github.com/slsa-framework/slsa/blob/main/docs/provenance/schema/v1/provenance.proto + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: in_toto_attestation/predicates/provenance/v1/provenance.proto + +package v1 + +import ( + v1 "github.com/in-toto/attestation/go/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Proto representation of predicate type https://slsa.dev/provenance/v1 +// Validation of all fields is left to the users of this proto. +type Provenance struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BuildDefinition *BuildDefinition `protobuf:"bytes,1,opt,name=build_definition,json=buildDefinition,proto3" json:"build_definition,omitempty"` + RunDetails *RunDetails `protobuf:"bytes,2,opt,name=run_details,json=runDetails,proto3" json:"run_details,omitempty"` +} + +func (x *Provenance) Reset() { + *x = Provenance{} + if protoimpl.UnsafeEnabled { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Provenance) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Provenance) ProtoMessage() {} + +func (x *Provenance) ProtoReflect() protoreflect.Message { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Provenance.ProtoReflect.Descriptor instead. +func (*Provenance) Descriptor() ([]byte, []int) { + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP(), []int{0} +} + +func (x *Provenance) GetBuildDefinition() *BuildDefinition { + if x != nil { + return x.BuildDefinition + } + return nil +} + +func (x *Provenance) GetRunDetails() *RunDetails { + if x != nil { + return x.RunDetails + } + return nil +} + +type BuildDefinition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BuildType string `protobuf:"bytes,1,opt,name=build_type,json=buildType,proto3" json:"build_type,omitempty"` + ExternalParameters *structpb.Struct `protobuf:"bytes,2,opt,name=external_parameters,json=externalParameters,proto3" json:"external_parameters,omitempty"` + InternalParameters *structpb.Struct `protobuf:"bytes,3,opt,name=internal_parameters,json=internalParameters,proto3" json:"internal_parameters,omitempty"` + ResolvedDependencies []*v1.ResourceDescriptor `protobuf:"bytes,4,rep,name=resolved_dependencies,json=resolvedDependencies,proto3" json:"resolved_dependencies,omitempty"` +} + +func (x *BuildDefinition) Reset() { + *x = BuildDefinition{} + if protoimpl.UnsafeEnabled { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BuildDefinition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuildDefinition) ProtoMessage() {} + +func (x *BuildDefinition) ProtoReflect() protoreflect.Message { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuildDefinition.ProtoReflect.Descriptor instead. +func (*BuildDefinition) Descriptor() ([]byte, []int) { + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP(), []int{1} +} + +func (x *BuildDefinition) GetBuildType() string { + if x != nil { + return x.BuildType + } + return "" +} + +func (x *BuildDefinition) GetExternalParameters() *structpb.Struct { + if x != nil { + return x.ExternalParameters + } + return nil +} + +func (x *BuildDefinition) GetInternalParameters() *structpb.Struct { + if x != nil { + return x.InternalParameters + } + return nil +} + +func (x *BuildDefinition) GetResolvedDependencies() []*v1.ResourceDescriptor { + if x != nil { + return x.ResolvedDependencies + } + return nil +} + +type RunDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Builder *Builder `protobuf:"bytes,1,opt,name=builder,proto3" json:"builder,omitempty"` + Metadata *BuildMetadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + Byproducts []*v1.ResourceDescriptor `protobuf:"bytes,3,rep,name=byproducts,proto3" json:"byproducts,omitempty"` +} + +func (x *RunDetails) Reset() { + *x = RunDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunDetails) ProtoMessage() {} + +func (x *RunDetails) ProtoReflect() protoreflect.Message { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunDetails.ProtoReflect.Descriptor instead. +func (*RunDetails) Descriptor() ([]byte, []int) { + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP(), []int{2} +} + +func (x *RunDetails) GetBuilder() *Builder { + if x != nil { + return x.Builder + } + return nil +} + +func (x *RunDetails) GetMetadata() *BuildMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *RunDetails) GetByproducts() []*v1.ResourceDescriptor { + if x != nil { + return x.Byproducts + } + return nil +} + +type Builder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Version map[string]string `protobuf:"bytes,2,rep,name=version,proto3" json:"version,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + BuilderDependencies []*v1.ResourceDescriptor `protobuf:"bytes,3,rep,name=builder_dependencies,json=builderDependencies,proto3" json:"builder_dependencies,omitempty"` +} + +func (x *Builder) Reset() { + *x = Builder{} + if protoimpl.UnsafeEnabled { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Builder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Builder) ProtoMessage() {} + +func (x *Builder) ProtoReflect() protoreflect.Message { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Builder.ProtoReflect.Descriptor instead. +func (*Builder) Descriptor() ([]byte, []int) { + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP(), []int{3} +} + +func (x *Builder) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Builder) GetVersion() map[string]string { + if x != nil { + return x.Version + } + return nil +} + +func (x *Builder) GetBuilderDependencies() []*v1.ResourceDescriptor { + if x != nil { + return x.BuilderDependencies + } + return nil +} + +type BuildMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InvocationId string `protobuf:"bytes,1,opt,name=invocation_id,json=invocationId,proto3" json:"invocation_id,omitempty"` + StartedOn *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=started_on,json=startedOn,proto3" json:"started_on,omitempty"` + FinishedOn *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=finished_on,json=finishedOn,proto3" json:"finished_on,omitempty"` +} + +func (x *BuildMetadata) Reset() { + *x = BuildMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BuildMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuildMetadata) ProtoMessage() {} + +func (x *BuildMetadata) ProtoReflect() protoreflect.Message { + mi := &file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuildMetadata.ProtoReflect.Descriptor instead. +func (*BuildMetadata) Descriptor() ([]byte, []int) { + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP(), []int{4} +} + +func (x *BuildMetadata) GetInvocationId() string { + if x != nil { + return x.InvocationId + } + return "" +} + +func (x *BuildMetadata) GetStartedOn() *timestamppb.Timestamp { + if x != nil { + return x.StartedOn + } + return nil +} + +func (x *BuildMetadata) GetFinishedOn() *timestamppb.Timestamp { + if x != nil { + return x.FinishedOn + } + return nil +} + +var File_in_toto_attestation_predicates_provenance_v1_provenance_proto protoreflect.FileDescriptor + +var file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDesc = []byte{ + 0x0a, 0x3d, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x2c, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x30, 0x69, + 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, + 0x01, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x68, 0x0a, + 0x10, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, + 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, + 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x66, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x59, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x5f, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x69, + 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x66, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x48, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x48, 0x0a, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5f, 0x0a, 0x15, 0x72, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x69, 0x6e, 0x5f, 0x74, 0x6f, + 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x6f, 0x72, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x82, 0x02, 0x0a, 0x0a, 0x52, + 0x75, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x4f, 0x0a, 0x07, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x69, 0x6e, 0x5f, + 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x57, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x69, + 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x0a, 0x62, 0x79, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, + 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x62, 0x79, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x22, + 0x92, 0x02, 0x0a, 0x07, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x5c, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x69, + 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x5d, 0x0a, 0x14, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, + 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x52, 0x13, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, + 0x6e, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x4f, 0x6e, 0x42, 0x73, 0x0a, 0x35, 0x69, 0x6f, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x69, 0x6e, 0x74, 0x6f, 0x74, 0x6f, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x5a, 0x3a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6e, 0x2d, 0x74, 0x6f, 0x74, 0x6f, + 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, + 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x65, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescOnce sync.Once + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescData = file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDesc +) + +func file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescGZIP() []byte { + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescOnce.Do(func() { + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescData = protoimpl.X.CompressGZIP(file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescData) + }) + return file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDescData +} + +var file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_in_toto_attestation_predicates_provenance_v1_provenance_proto_goTypes = []interface{}{ + (*Provenance)(nil), // 0: in_toto_attestation.predicates.provenance.v1.Provenance + (*BuildDefinition)(nil), // 1: in_toto_attestation.predicates.provenance.v1.BuildDefinition + (*RunDetails)(nil), // 2: in_toto_attestation.predicates.provenance.v1.RunDetails + (*Builder)(nil), // 3: in_toto_attestation.predicates.provenance.v1.Builder + (*BuildMetadata)(nil), // 4: in_toto_attestation.predicates.provenance.v1.BuildMetadata + nil, // 5: in_toto_attestation.predicates.provenance.v1.Builder.VersionEntry + (*structpb.Struct)(nil), // 6: google.protobuf.Struct + (*v1.ResourceDescriptor)(nil), // 7: in_toto_attestation.v1.ResourceDescriptor + (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp +} +var file_in_toto_attestation_predicates_provenance_v1_provenance_proto_depIdxs = []int32{ + 1, // 0: in_toto_attestation.predicates.provenance.v1.Provenance.build_definition:type_name -> in_toto_attestation.predicates.provenance.v1.BuildDefinition + 2, // 1: in_toto_attestation.predicates.provenance.v1.Provenance.run_details:type_name -> in_toto_attestation.predicates.provenance.v1.RunDetails + 6, // 2: in_toto_attestation.predicates.provenance.v1.BuildDefinition.external_parameters:type_name -> google.protobuf.Struct + 6, // 3: in_toto_attestation.predicates.provenance.v1.BuildDefinition.internal_parameters:type_name -> google.protobuf.Struct + 7, // 4: in_toto_attestation.predicates.provenance.v1.BuildDefinition.resolved_dependencies:type_name -> in_toto_attestation.v1.ResourceDescriptor + 3, // 5: in_toto_attestation.predicates.provenance.v1.RunDetails.builder:type_name -> in_toto_attestation.predicates.provenance.v1.Builder + 4, // 6: in_toto_attestation.predicates.provenance.v1.RunDetails.metadata:type_name -> in_toto_attestation.predicates.provenance.v1.BuildMetadata + 7, // 7: in_toto_attestation.predicates.provenance.v1.RunDetails.byproducts:type_name -> in_toto_attestation.v1.ResourceDescriptor + 5, // 8: in_toto_attestation.predicates.provenance.v1.Builder.version:type_name -> in_toto_attestation.predicates.provenance.v1.Builder.VersionEntry + 7, // 9: in_toto_attestation.predicates.provenance.v1.Builder.builder_dependencies:type_name -> in_toto_attestation.v1.ResourceDescriptor + 8, // 10: in_toto_attestation.predicates.provenance.v1.BuildMetadata.started_on:type_name -> google.protobuf.Timestamp + 8, // 11: in_toto_attestation.predicates.provenance.v1.BuildMetadata.finished_on:type_name -> google.protobuf.Timestamp + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_in_toto_attestation_predicates_provenance_v1_provenance_proto_init() } +func file_in_toto_attestation_predicates_provenance_v1_provenance_proto_init() { + if File_in_toto_attestation_predicates_provenance_v1_provenance_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Provenance); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BuildDefinition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Builder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BuildMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_in_toto_attestation_predicates_provenance_v1_provenance_proto_goTypes, + DependencyIndexes: file_in_toto_attestation_predicates_provenance_v1_provenance_proto_depIdxs, + MessageInfos: file_in_toto_attestation_predicates_provenance_v1_provenance_proto_msgTypes, + }.Build() + File_in_toto_attestation_predicates_provenance_v1_provenance_proto = out.File + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_rawDesc = nil + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_goTypes = nil + file_in_toto_attestation_predicates_provenance_v1_provenance_proto_depIdxs = nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7f1efa1922..d5a4bb1f46 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1114,6 +1114,7 @@ github.com/hexops/gotextdiff/span github.com/imdario/mergo # github.com/in-toto/attestation v1.0.1 ## explicit; go 1.20 +github.com/in-toto/attestation/go/predicates/provenance/v1 github.com/in-toto/attestation/go/v1 # github.com/in-toto/in-toto-golang v0.9.1-0.20240317085821-8e2966059a09 ## explicit; go 1.20