diff --git a/pkg/artifacts/signable.go b/pkg/artifacts/signable.go index cd3108209d..2f50d20094 100644 --- a/pkg/artifacts/signable.go +++ b/pkg/artifacts/signable.go @@ -299,14 +299,7 @@ func ExtractStructuredTargetFromResults(ctx context.Context, objResults []object } // TODO(#592): support structured results using Run - results := []objects.Result{} for _, res := range objResults { - results = append(results, objects.Result{ - Name: res.Name, - Value: res.Value, - }) - } - for _, res := range results { if strings.HasSuffix(res.Name, categoryMarker) { valid, err := isStructuredResult(res, categoryMarker) if err != nil { diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go index b4130c186c..ca504a4006 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go @@ -51,23 +51,18 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, return provenance.GetSLSA1Statement(pro, sub, &bd, bp, slsaconfig) } -// byproducts contains the pipelineRunResults +// byproducts contains the pipelineRunResults that are not subjects. func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) ([]*intoto.ResourceDescriptor, error) { - byProd := []*intoto.ResourceDescriptor{} - - res, err := results.GetResultsWithoutBuildArtifacts(pro.GetResults(), pipelineRunResults) + byProd, err := results.GetResultsWithoutBuildArtifacts(pro.GetResults(), pipelineRunResults) if err != nil { return nil, err } - byProd = append(byProd, res...) if !slsaconfig.DeepInspectionEnabled { return byProd, nil } - tros := pro.GetExecutedTasks() - - for _, tro := range tros { + for _, tro := range pro.GetExecutedTasks() { taskProds, err := taskrun.ByProducts(tro) if err != nil { return nil, err @@ -79,11 +74,15 @@ func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaCon } func subjectDigests(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { - results := pro.GetResults() + subjects := extract.SubjectsFromBuildArtifact(ctx, pro.GetResults()) + + if !slsaconfig.DeepInspectionEnabled { + return subjects + } - if slsaconfig.DeepInspectionEnabled { - results = append(results, pro.GetTaskAndStepResults()...) + for _, task := range pro.GetExecutedTasks() { + subjects = append(subjects, taskrun.SubjectDigests(ctx, task)...) } - return extract.SubjectsFromBuildArtifact(ctx, results) + return subjects } diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go index b4fc739ca0..c4728ac8d8 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go @@ -80,121 +80,276 @@ func TestGenerateAttestation(t *testing.T) { e1BuildStart := time.Unix(1617011400, 0) e1BuildFinished := time.Unix(1617011415, 0) - predicate := &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{ - { - 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"}, - }, - }, - }, - 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/img-ARTIFACT_INPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), - MediaType: JSONMediaType, - }, { - Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", - Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), - MediaType: JSONMediaType, + tests := []struct { + name string + expectedStatement *intoto.Statement + withDeepInspection bool + }{ + { + name: "attestation without deepinspection", + expectedStatement: &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: slsaprov.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "abc", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, }, + Predicate: getPredicateStruct(t, &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{ + { + 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"}, + }, + }, + }, + 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/img-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), + MediaType: JSONMediaType, + }, { + Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), + MediaType: JSONMediaType, + }, + }, + }, + }), }, }, - } - - predicateStruct := getPredicateStruct(t, predicate) - - want := &intoto.Statement{ - Type: intoto.StatementTypeUri, - PredicateType: slsaprov.PredicateSLSAProvenance, - Subject: []*intoto.ResourceDescriptor{ - { - Name: "abc", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", - }, - }, - { - Name: "test.io/test/image", - Digest: common.DigestSet{ - "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + { + name: "attestation with deepinspection", + withDeepInspection: true, + expectedStatement: &intoto.Statement{ + Type: intoto.StatementTypeUri, + PredicateType: slsaprov.PredicateSLSAProvenance, + Subject: []*intoto.ResourceDescriptor{ + { + Name: "abc", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/my/image/fromstep3", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466", + }, + }, }, + Predicate: getPredicateStruct(t, &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{ + { + 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"}, + }, + { + Name: "inputs/result", + Uri: "https://github.com/tektoncd/pipeline", + Digest: common.DigestSet{"sha1": "7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601"}, + }, + { + Uri: "abc", + Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + Name: "inputs/result", + }, + { + 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": "taskrun"}, + }, + { + 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/img-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), + MediaType: JSONMediaType, + }, { + Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), + MediaType: JSONMediaType, + }, { + Name: "taskRunResults/some-uri_DIGEST", + Content: []uint8(`"sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"`), + MediaType: JSONMediaType, + }, { + Name: "taskRunResults/some-uri", + Content: []uint8(`"pkg:deb/debian/curl@7.50.3-1"`), + MediaType: JSONMediaType, + }, { + Name: "stepResults/step1_result1-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha1:7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601","uri":"https://github.com/tektoncd/pipeline"}`), + MediaType: JSONMediaType, + }, { + Name: "stepResults/step1_result1", + Content: []uint8(`"result-value"`), + MediaType: JSONMediaType, + }, { + Name: "stepResults/step1_result1-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"gcr.io/my/image/fromstep2"}`), + MediaType: JSONMediaType, + }, + }, + }, + }), }, }, - Predicate: predicateStruct, } - got, err := GenerateAttestation(ctx, pr, &slsaconfig.SlsaConfig{ - BuilderID: "test_builder-1", - DeepInspectionEnabled: false, - BuildType: "https://tekton.dev/chains/v2/slsa", - }) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := GenerateAttestation(ctx, pr, &slsaconfig.SlsaConfig{ + BuilderID: "test_builder-1", + DeepInspectionEnabled: test.withDeepInspection, + BuildType: "https://tekton.dev/chains/v2/slsa", + }) - if err != nil { - t.Errorf("unwant error: %s", err.Error()) - } + if err != nil { + t.Errorf("unwant error: %s", err.Error()) + } - opts := compare.SLSAV1CompareOptions() - opts = append(opts, protocmp.Transform()) + opts := compare.SLSAV1CompareOptions() + opts = append(opts, protocmp.Transform()) - if diff := cmp.Diff(want, got, opts...); diff != "" { - t.Errorf("GenerateAttestation(): -want +got: %s", diff) + if diff := cmp.Diff(test.expectedStatement, got, opts...); diff != "" { + t.Errorf("GenerateAttestation(): -want +got: %s", diff) + } + }) } } diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go index 10577b347e..dd419398d6 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go @@ -18,12 +18,14 @@ import ( 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/artifact" builddefinition "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_definition" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/provenance" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/results" "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" ) const ( @@ -44,8 +46,7 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsa return nil, err } - results := append(tro.GetResults(), tro.GetStepResults()...) - sub := extract.SubjectsFromBuildArtifact(ctx, results) + sub := SubjectDigests(ctx, tro) return provenance.GetSLSA1Statement(tro, sub, &bd, bp, slsaConfig) } @@ -68,3 +69,28 @@ func ByProducts(tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, err return byProd, nil } + +// SubjectDigests returns the subjects detected in the given TaskRun. It takes into account taskrun and step results. +func SubjectDigests(ctx context.Context, tro *objects.TaskRunObjectV1) []*intoto.ResourceDescriptor { + var subjects []*intoto.ResourceDescriptor + for _, step := range tro.Status.Steps { + res := getObjectResults(step.Results) + stepSubjects := extract.SubjectsFromBuildArtifact(ctx, res) + subjects = artifact.AppendSubjects(subjects, stepSubjects...) + } + + taskSubjects := extract.SubjectsFromBuildArtifact(ctx, tro.GetResults()) + subjects = artifact.AppendSubjects(subjects, taskSubjects...) + + return subjects +} + +func getObjectResults(tresults []v1.TaskRunResult) (res []objects.Result) { + for _, r := range tresults { + res = append(res, objects.Result{ + Name: r.Name, + Value: r.Value, + }) + } + return +} diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index 8c24fca546..0d89bec6bf 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -294,16 +294,6 @@ func (pro *PipelineRunObjectV1) GetResults() []Result { return res } -// GetTaskAndStepResults returns the results of the associated TaskRuns of the PipelineRun. -func (pro *PipelineRunObjectV1) GetTaskAndStepResults() (results []Result) { - execTasks := pro.GetExecutedTasks() - for _, task := range execTasks { - results = append(results, task.GetResults()...) - results = append(results, task.GetStepResults()...) - } - return -} - // Get the ServiceAccount declared in the PipelineRun func (pro *PipelineRunObjectV1) GetServiceAccountName() string { return pro.Spec.TaskRunTemplate.ServiceAccountName