From 0c67ea9a152bcc6ced8827a5a9f34d9508959814 Mon Sep 17 00:00:00 2001 From: Jawed khelil Date: Tue, 30 Jul 2024 13:54:38 +0200 Subject: [PATCH] fix pipelunerun provenance when multiple tasks have the same name (case of use of matrix to fan out tasks on a pipeline) --- pkg/chains/formats/slsa/extract/extract.go | 19 ++- .../formats/slsa/extract/v1beta1/extract.go | 18 +- .../slsa/internal/material/material.go | 70 ++++---- .../internal/material/v1beta1/material.go | 69 ++++---- .../resolved_dependencies.go | 66 ++++---- .../pipelinerun-childrefs.json | 2 +- .../pipeline-v1beta1/pipelinerun1.json | 84 +++++++++- .../testdata/pipeline-v1beta1/taskrun1.json | 2 +- .../testdata/pipeline-v1beta1/taskrun3.json | 136 +++++++++++++++ .../slsa/v1/pipelinerun/pipelinerun.go | 110 ++++++------ .../slsa/v1/pipelinerun/provenance_test.go | 157 ++++++++++++++++++ pkg/chains/objects/objects.go | 26 +-- pkg/chains/objects/objects_test.go | 13 +- 13 files changed, 598 insertions(+), 174 deletions(-) create mode 100644 pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun3.json diff --git a/pkg/chains/formats/slsa/extract/extract.go b/pkg/chains/formats/slsa/extract/extract.go index a47d558f7c..706fc57183 100644 --- a/pkg/chains/formats/slsa/extract/extract.go +++ b/pkg/chains/formats/slsa/extract/extract.go @@ -80,15 +80,20 @@ func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsa 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. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - - trSubjects := subjectsFromTektonObject(ctx, tr) - result = artifact.AppendSubjects(result, trSubjects...) + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not found for task %s", t.Name) + continue + } + trSubjects := subjectsFromTektonObject(ctx, tr) + result = artifact.AppendSubjects(result, trSubjects...) + } } } diff --git a/pkg/chains/formats/slsa/extract/v1beta1/extract.go b/pkg/chains/formats/slsa/extract/v1beta1/extract.go index 043ec0deb1..293cb168f7 100644 --- a/pkg/chains/formats/slsa/extract/v1beta1/extract.go +++ b/pkg/chains/formats/slsa/extract/v1beta1/extract.go @@ -74,14 +74,20 @@ func SubjectsFromPipelineRunV1Beta1(ctx context.Context, obj objects.TektonObjec if pSpec != nil { pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) for _, t := range pipelineTasks { - tr := pro.GetTaskRunFromTask(t.Name) - // Ignore Tasks that did not execute during the PipelineRun. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - trSubjects := SubjectsFromTektonObjectV1Beta1(ctx, tr) - result = artifact.AppendSubjects(result, trSubjects...) + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not found for task %s", t.Name) + continue + } + trSubjects := SubjectsFromTektonObjectV1Beta1(ctx, tr) + result = artifact.AppendSubjects(result, trSubjects...) + } } } diff --git a/pkg/chains/formats/slsa/internal/material/material.go b/pkg/chains/formats/slsa/internal/material/material.go index 4cbff0b9d2..79d9d823be 100644 --- a/pkg/chains/formats/slsa/internal/material/material.go +++ b/pkg/chains/formats/slsa/internal/material/material.go @@ -81,33 +81,37 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObjectV1, sl if pSpec != nil { pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) for _, t := range pipelineTasks { - tr := pro.GetTaskRunFromTask(t.Name) - // Ignore Tasks that did not execute during the PipelineRun. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - - stepMaterials, err := FromStepImages(tr) - if err != nil { - return mats, err - } - mats = artifact.AppendMaterials(mats, stepMaterials...) - - // add sidecar images - sidecarMaterials, err := FromSidecarImages(tr) - if err != nil { - return nil, err - } - mats = artifact.AppendMaterials(mats, sidecarMaterials...) - - // add remote task configsource information in materials - if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { - m := common.ProvenanceMaterial{ - URI: tr.Status.Provenance.RefSource.URI, - Digest: tr.Status.Provenance.RefSource.Digest, + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not found for task %s", t.Name) + continue + } + stepMaterials, err := FromStepImages(tr) + if err != nil { + return mats, err + } + mats = artifact.AppendMaterials(mats, stepMaterials...) + // add sidecar images + sidecarMaterials, err := FromSidecarImages(tr) + if err != nil { + return nil, err + } + mats = artifact.AppendMaterials(mats, sidecarMaterials...) + + // add remote task configsource information in materials + if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { + m := common.ProvenanceMaterial{ + URI: tr.Status.Provenance.RefSource.URI, + Digest: tr.Status.Provenance.RefSource.Digest, + } + mats = artifact.AppendMaterials(mats, m) } - mats = artifact.AppendMaterials(mats, m) } } } @@ -304,14 +308,20 @@ func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunO logger := logging.FromContext(ctx) pipelineTasks := append(pSpec.Tasks, pSpec.Finally...) for _, t := range pipelineTasks { - tr := pro.GetTaskRunFromTask(t.Name) - // Ignore Tasks that did not execute during the PipelineRun. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun is not found or not completed for the task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - materialsFromTasks := FromTaskParamsAndResults(ctx, tr) - mats = artifact.AppendMaterials(mats, materialsFromTasks...) + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun is not found or not completed for the task %s", t.Name) + continue + } + materialsFromTasks := FromTaskParamsAndResults(ctx, tr) + mats = artifact.AppendMaterials(mats, materialsFromTasks...) + } } } diff --git a/pkg/chains/formats/slsa/internal/material/v1beta1/material.go b/pkg/chains/formats/slsa/internal/material/v1beta1/material.go index 650bb1d6dd..33837c6b41 100644 --- a/pkg/chains/formats/slsa/internal/material/v1beta1/material.go +++ b/pkg/chains/formats/slsa/internal/material/v1beta1/material.go @@ -77,33 +77,38 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObjectV1Beta 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. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not found for task %s", t.Name) + continue + } + stepMaterials, err := FromStepImages(tr) + if err != nil { + return mats, err + } + mats = artifact.AppendMaterials(mats, stepMaterials...) - stepMaterials, err := FromStepImages(tr) - if err != nil { - return mats, err - } - mats = artifact.AppendMaterials(mats, stepMaterials...) - - // add sidecar images - sidecarMaterials, err := FromSidecarImages(tr) - if err != nil { - return nil, err - } - mats = artifact.AppendMaterials(mats, sidecarMaterials...) - - // add remote task configsource information in materials - if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { - m := common.ProvenanceMaterial{ - URI: tr.Status.Provenance.RefSource.URI, - Digest: tr.Status.Provenance.RefSource.Digest, + // add sidecar images + sidecarMaterials, err := FromSidecarImages(tr) + if err != nil { + return nil, err + } + mats = artifact.AppendMaterials(mats, sidecarMaterials...) + + // add remote task configsource information in materials + if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { + m := common.ProvenanceMaterial{ + URI: tr.Status.Provenance.RefSource.URI, + Digest: tr.Status.Provenance.RefSource.Digest, + } + mats = artifact.AppendMaterials(mats, m) } - mats = artifact.AppendMaterials(mats, m) } } } @@ -273,14 +278,20 @@ func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunO 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. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun is not found or not completed for the task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - materialsFromTasks := FromTaskParamsAndResults(ctx, tr) - mats = artifact.AppendMaterials(mats, materialsFromTasks...) + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun is not found or not completed for the task %s", t.Name) + continue + } + materialsFromTasks := FromTaskParamsAndResults(ctx, tr) + mats = artifact.AppendMaterials(mats, materialsFromTasks...) + } } } 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 6bb14a4176..5ac4ac00f3 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go @@ -150,40 +150,46 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV 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. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - rd, err := addTasks(tr) - if err != nil { - logger.Errorf("error storing taskRun %s, error: %s", t.Name, err) - continue - } - - if rd != nil { - resolvedDependencies = append(resolvedDependencies, rd) - } + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr == nil || tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not found for task %s", t.Name) + continue + } + rd, err := addTasks(tr) + if err != nil { + logger.Errorf("error storing taskRun %s, error: %s", t.Name, err) + continue + } + if rd != nil { + resolvedDependencies = append(resolvedDependencies, rd) + } + + mats := []common.ProvenanceMaterial{} + + // add step images + stepMaterials, err := material.FromStepImages(tr) + if err != nil { + return nil, err + } + mats = append(mats, stepMaterials...) + + // add sidecar images + sidecarMaterials, err := material.FromSidecarImages(tr) + if err != nil { + return nil, err + } + mats = append(mats, sidecarMaterials...) + + // convert materials to resolved dependencies + resolvedDependencies = append(resolvedDependencies, ConvertMaterialsToResolvedDependencies(mats, "")...) - mats := []common.ProvenanceMaterial{} - - // add step images - stepMaterials, err := material.FromStepImages(tr) - if err != nil { - return nil, err - } - mats = append(mats, stepMaterials...) - - // add sidecar images - sidecarMaterials, err := material.FromSidecarImages(tr) - if err != nil { - return nil, err } - mats = append(mats, sidecarMaterials...) - - // convert materials to resolved dependencies - resolvedDependencies = append(resolvedDependencies, ConvertMaterialsToResolvedDependencies(mats, "")...) } } return resolvedDependencies, nil diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun-childrefs.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun-childrefs.json index 32030fa0aa..4f72d58ff5 100644 --- a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun-childrefs.json +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun-childrefs.json @@ -121,7 +121,7 @@ { "apiVersion": "tekton.dev/v1beta1", "kind": "TaskRun", - "name": "taskrun-build", + "name": "taskrun-build-0", "pipelineTaskName": "build" } ] diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun1.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun1.json index 879e8b1d84..3b687f130f 100644 --- a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun1.json +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/pipelinerun1.json @@ -210,7 +210,89 @@ } } }, - "taskrun-build": { + "taskrun-build-0": { + "pipelineTaskName": "build", + "status": { + "completionTime": "2021-03-29T09:50:15Z", + "conditions": [ + { + "lastTransitionTime": "2021-03-29T09:50:15Z", + "message": "All Steps have completed executing", + "reason": "Succeeded", + "status": "True", + "type": "Succeeded" + } + ], + "podName": "build-pod", + "startTime": "2021-03-29T09:50:00Z", + "steps": [ + { + "container": "step-build", + "imageID": "test.io/test/build-image", + "name": "build", + "terminated": { + "exitCode": 0, + "finishedAt": "2022-05-31T19:17:30Z", + "reason": "Completed", + "startedAt": "2021-03-29T09:50:00Z" + } + } + ], + "taskResults": [ + { + "name": "IMAGE_DIGEST", + "value": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + }, + { + "name": "IMAGE_URL", + "value": "test.io/test/image\n" + } + ], + "taskSpec": { + "params": [ + { + "description": "Git CHAINS URL", + "name": "CHAINS-GIT_URL", + "type": "string" + }, + { + "description": "Git CHAINS Commit", + "name": "CHAINS-GIT_COMMIT", + "type": "string" + } + ], + "results": [ + { + "description": "Digest of the image just built.", + "name": "IMAGE_DIGEST" + }, + { + "description": "URL of the image just built.", + "name": "IMAGE_URL" + } + ], + "steps": [ + { + "command": [ + "buildah", + "build" + ], + "image": "test.io/test/build-image", + "name": "generate" + }, + { + "command": [ + "buildah", + "push" + ], + "image": "test.io/test/build-image", + "name": "push" + } + ] + } + } + }, + "taskrun-build-1": { "pipelineTaskName": "build", "status": { "completionTime": "2021-03-29T09:50:15Z", diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun1.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun1.json index a686452516..1e37bd915e 100644 --- a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun1.json +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun1.json @@ -1,6 +1,6 @@ { "metadata": { - "name": "taskrun-build", + "name": "taskrun-build-0", "labels": { "tekton.dev/pipelineTask": "build" } diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun3.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun3.json new file mode 100644 index 0000000000..4a27e9bd94 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun3.json @@ -0,0 +1,136 @@ +{ + "metadata": { + "name": "taskrun-build-1", + "labels": { + "tekton.dev/pipelineTask": "build" + } + }, + "spec": { + "params": [ + { + "name": "IMAGE", + "value": "test.io/test/image" + }, + { + "name": "CHAINS-GIT_COMMIT", + "value": "sha:taskrun" + }, + { + "name": "CHAINS-GIT_URL", + "value": "https://git.test.com" + } + ], + "taskRef": { + "name": "build", + "kind": "Task" + }, + "serviceAccountName": "default" + }, + "status": { + "startTime": "2021-03-29T09:50:00Z", + "completionTime": "2021-03-29T09:50:15Z", + "conditions": [ + { + "type": "Succeeded", + "status": "True", + "lastTransitionTime": "2021-03-29T09:50:15Z", + "reason": "Succeeded", + "message": "All Steps have completed executing" + } + ], + "podName": "test-pod-name", + "steps": [ + { + "name": "step1", + "container": "step-step1", + "imageID": "docker-pullable://gcr.io/test4/test4@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6" + }, + { + "name": "step2", + "container": "step-step2", + "imageID": "docker-pullable://gcr.io/test5/test5@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac" + }, + { + "name": "step3", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test6/test6@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478" + } + ], + "taskResults": [ + { + "name": "IMAGE_DIGEST", + "value": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + }, + { + "name": "IMAGE_URL", + "value": "gcr.io/my/image" + } + ], + "taskSpec": { + "params": [ + { + "name": "IMAGE", + "type": "string" + }, + { + "name": "filename", + "type": "string" + }, + { + "name": "DOCKERFILE", + "type": "string" + }, + { + "name": "CONTEXT", + "type": "string" + }, + { + "name": "EXTRA_ARGS", + "type": "string" + }, + { + "name": "BUILDER_IMAGE", + "type": "string" + }, { + "name": "CHAINS-GIT_COMMIT", + "type": "string", + "default": "sha:task" + }, { + "name": "CHAINS-GIT_URL", + "type": "string", + "default": "https://defaultgit.test.com" + } + ], + "steps": [ + { + "name": "step1" + }, + { + "name": "step2" + }, + { + "name": "step3" + } + ], + "results": [ + { + "name": "IMAGE_DIGEST", + "description": "Digest of the image just built." + }, + { + "name": "filename_DIGEST", + "description": "Digest of the file just built." + } + ] + }, + "provenance": { + "refSource": { + "uri": "github.com/test", + "digest": { + "sha1": "ab123" + }, + "entryPoint": "build.yaml" + } + } + } +} diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go index bca58915de..363658277f 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go @@ -104,70 +104,74 @@ func buildConfig(ctx context.Context, pro *objects.PipelineRunObjectV1Beta1) Bui var last string for i, t := range pipelineTasks { - tr := pro.GetTaskRunFromTask(t.Name) - - // Ignore Tasks that did not execute during the PipelineRun. - if tr == nil || tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not found for task %s", t.Name) + taskRuns := pro.GetTaskRunsFromTask(t.Name) + if len(taskRuns) == 0 { + logger.Infof("no taskruns found for task %s", t.Name) continue } - steps := []attest.StepAttestation{} - for i, stepState := range tr.Status.Steps { - step := tr.Status.TaskSpec.Steps[i] - steps = append(steps, attest.Step(&step, &stepState)) - } - after := t.RunAfter - - // Establish task order by retrieving all task's referenced - // in the "when" and "params" fields - refs := v1beta1.PipelineTaskResultRefs(&t) - for _, ref := range refs { - - // Ensure task doesn't already exist in after - found := false - for _, at := range after { - if at == ref.PipelineTask { - found = true - } + for _, tr := range taskRuns { + // Ignore Tasks that did not execute during the PipelineRun. + if tr.Status.CompletionTime == nil { + logger.Infof("taskrun status not complete for task %s", tr.Name) + continue } - if !found { - after = append(after, ref.PipelineTask) + + steps := []attest.StepAttestation{} + for i, stepState := range tr.Status.Steps { + step := tr.Status.TaskSpec.Steps[i] + steps = append(steps, attest.Step(&step, &stepState)) } - } - // tr is a finally task without an explicit runAfter value. It must have executed - // after the last non-finally task, if any non-finally tasks were executed. - if len(after) == 0 && i >= len(pSpec.Tasks) && last != "" { - after = append(after, last) - } + after := t.RunAfter + // Establish task order by retrieving all task's referenced + // in the "when" and "params" fields + refs := v1beta1.PipelineTaskResultRefs(&t) + for _, ref := range refs { + // Ensure task doesn't already exist in after + found := false + for _, at := range after { + if at == ref.PipelineTask { + found = true + } + } + if !found { + after = append(after, ref.PipelineTask) + } + } - params := tr.Spec.Params - var paramSpecs []v1beta1.ParamSpec - if tr.Status.TaskSpec != nil { - paramSpecs = tr.Status.TaskSpec.Params - } else { - paramSpecs = []v1beta1.ParamSpec{} - } + // tr is a finally task without an explicit runAfter value. It must have executed + // after the last non-finally task, if any non-finally tasks were executed. + if len(after) == 0 && i >= len(pSpec.Tasks) && last != "" { + after = append(after, last) + } - task := TaskAttestation{ - Name: t.Name, - After: after, - StartedOn: tr.Status.StartTime.Time.UTC(), - FinishedOn: tr.Status.CompletionTime.Time.UTC(), - ServiceAccountName: pro.Spec.ServiceAccountName, - Status: getStatus(tr.Status.Conditions), - Steps: steps, - Invocation: attest.Invocation(tr, params, paramSpecs), - Results: tr.Status.TaskRunResults, - } + params := tr.Spec.Params + var paramSpecs []v1beta1.ParamSpec + if tr.Status.TaskSpec != nil { + paramSpecs = tr.Status.TaskSpec.Params + } else { + paramSpecs = []v1beta1.ParamSpec{} + } - if t.TaskRef != nil { - task.Ref = *t.TaskRef + task := TaskAttestation{ + Name: t.Name, + After: after, + StartedOn: tr.Status.StartTime.Time.UTC(), + FinishedOn: tr.Status.CompletionTime.Time.UTC(), + ServiceAccountName: pro.Spec.ServiceAccountName, + Status: getStatus(tr.Status.Conditions), + Steps: steps, + Invocation: attest.Invocation(tr, params, paramSpecs), + Results: tr.Status.TaskRunResults, + } + if t.TaskRef != nil { + task.Ref = *t.TaskRef + } + tasks = append(tasks, task) } - tasks = append(tasks, task) if i < len(pSpec.Tasks) { - last = task.Name + last = t.Name } } return BuildConfig{Tasks: tasks} diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go index 1a8c27592c..3c08ef6f0f 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go @@ -61,9 +61,14 @@ func createPro(path string) *objects.PipelineRunObjectV1Beta1 { if err != nil { panic(err) } + tr3, err := objectloader.TaskRunV1Beta1FromFile("../../testdata/pipeline-v1beta1/taskrun3.json") + if err != nil { + panic(err) + } p := objects.NewPipelineRunObjectV1Beta1(pr) p.AppendTaskRun(tr1) p.AppendTaskRun(tr2) + p.AppendTaskRun(tr3) return p } @@ -142,6 +147,7 @@ func TestBuildConfig(t *testing.T) { }, }, }, + //nolint:dupl { Name: "build", After: []string{"git-clone"}, @@ -214,6 +220,79 @@ func TestBuildConfig(t *testing.T) { }, }, }, + //nolint:dupl + { + Name: "build", + After: []string{"git-clone"}, + Ref: v1beta1.TaskRef{ + Name: "build", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + ServiceAccountName: "pipeline", + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test4/test4@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "container": "step1", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test5/test5@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "container": "step2", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test6/test6@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", + }, + }, + }, + }, }, } ctx := logtesting.TestContextWithLogger(t) @@ -337,6 +416,7 @@ func TestBuildConfigTaskOrder(t *testing.T) { }, }, }, + //nolint:dupl { Name: "build", After: []string{"git-clone"}, @@ -413,6 +493,83 @@ func TestBuildConfigTaskOrder(t *testing.T) { }, }, }, + //nolint:dupl + { + Name: "build", + After: []string{"git-clone"}, + Ref: v1beta1.TaskRef{ + Name: "build", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + ServiceAccountName: "pipeline", + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test4/test4@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "container": "step1", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test5/test5@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "container": "step2", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test6/test6@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{ + // TODO: Is this right? + // "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "abcd"}, + "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", + }, + }, + }, + }, }, } pt := v1beta1.PipelineTask{ diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index 0d89bec6bf..559e3c5ba0 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -315,14 +315,15 @@ func (pro *PipelineRunObjectV1) GetTaskRuns() []*v1.TaskRun { } // Get the associated TaskRun via the Task name -func (pro *PipelineRunObjectV1) GetTaskRunFromTask(taskName string) *TaskRunObjectV1 { +func (pro *PipelineRunObjectV1) GetTaskRunsFromTask(taskName string) []*TaskRunObjectV1 { + var taskRuns []*TaskRunObjectV1 for _, tr := range pro.taskRuns { val, ok := tr.Labels[PipelineTaskLabel] if ok && val == taskName { - return NewTaskRunObjectV1(tr) + taskRuns = append(taskRuns, NewTaskRunObjectV1(tr)) } } - return nil + return taskRuns } // Get the imgPullSecrets from the pod template @@ -388,13 +389,17 @@ func (pro *PipelineRunObjectV1) GetExecutedTasks() (tro []*TaskRunObjectV1) { tasks := pSpec.Tasks tasks = append(tasks, pSpec.Finally...) for _, task := range tasks { - tr := pro.GetTaskRunFromTask(task.Name) - - if tr == nil || tr.Status.CompletionTime == nil { + taskRuns := pro.GetTaskRunsFromTask(task.Name) + if len(taskRuns) == 0 { continue } + for _, tr := range taskRuns { + if tr == nil || tr.Status.CompletionTime == nil { + continue + } - tro = append(tro, tr) + tro = append(tro, tr) + } } return @@ -514,14 +519,15 @@ func (pro *PipelineRunObjectV1Beta1) AppendTaskRun(tr *v1beta1.TaskRun) { //noli } // Get the associated TaskRun via the Task name -func (pro *PipelineRunObjectV1Beta1) GetTaskRunFromTask(taskName string) *TaskRunObjectV1Beta1 { +func (pro *PipelineRunObjectV1Beta1) GetTaskRunsFromTask(taskName string) []*TaskRunObjectV1Beta1 { + var taskRuns []*TaskRunObjectV1Beta1 for _, tr := range pro.taskRuns { val, ok := tr.Labels[PipelineTaskLabel] if ok && val == taskName { - return NewTaskRunObjectV1Beta1(tr) + taskRuns = append(taskRuns, NewTaskRunObjectV1Beta1(tr)) } } - return nil + return taskRuns } // Get the imgPullSecrets from the pod template diff --git a/pkg/chains/objects/objects_test.go b/pkg/chains/objects/objects_test.go index 0ae36ff044..76ec02f495 100644 --- a/pkg/chains/objects/objects_test.go +++ b/pkg/chains/objects/objects_test.go @@ -358,16 +358,17 @@ func TestNewTektonObject(t *testing.T) { assert.ErrorContains(t, err, "unrecognized type") } -func TestPipelineRun_GetTaskRunFromTask(t *testing.T) { +func TestPipelineRun_GetTaskRunsFromTask(t *testing.T) { pro := NewPipelineRunObjectV1(getPipelineRun()) - assert.Nil(t, pro.GetTaskRunFromTask("missing")) - assert.Nil(t, pro.GetTaskRunFromTask("foo-task")) + assert.Nil(t, pro.GetTaskRunsFromTask("missing")) + assert.Nil(t, pro.GetTaskRunsFromTask("foo-task")) pro.AppendTaskRun(getTaskRun()) - assert.Nil(t, pro.GetTaskRunFromTask("missing")) - tr := pro.GetTaskRunFromTask("foo-task") - assert.Equal(t, "foo", tr.Name) + assert.Nil(t, pro.GetTaskRunsFromTask("missing")) + taskRuns := pro.GetTaskRunsFromTask("foo-task") + assert.NotEmpty(t, taskRuns) + assert.Equal(t, "foo", taskRuns[0].Name) } func TestProvenanceExists(t *testing.T) {