From 4d1550a4cdffdd9a65a9cd6246b95c856feb7b04 Mon Sep 17 00:00:00 2001 From: Naomi Gelman Date: Tue, 6 Oct 2026 17:01:18 +0300 Subject: [PATCH] feat(formats): clarify SLSA resource names Include stable pipeline task and step names in byProducts, and name resolved dependencies by the pipeline task that uses them. Co-authored-by: Cursor Signed-off-by: Naomi Gelman --- docs/predicate/slsa/v2.md | 5 +- docs/slsa-provenance.md | 4 ++ .../resolved_dependencies.go | 21 ++++++-- .../resolved_dependencies_test.go | 54 +++++++++++++++++++ .../formats/slsa/internal/results/results.go | 28 ++++++++++ .../internal/pipelinerun/pipelinerun.go | 35 +++++++++++- .../internal/pipelinerun/pipelinerun_test.go | 27 ++++++---- .../slsa/v2alpha4/internal/taskrun/taskrun.go | 14 +++-- .../v2alpha4/internal/taskrun/taskrun_test.go | 4 +- .../formats/slsa/v2alpha4/slsav2_test.go | 6 +-- pkg/chains/objects/objects.go | 20 +++++-- .../pipeline-with-repeated-results.json | 2 +- 12 files changed, 187 insertions(+), 33 deletions(-) diff --git a/docs/predicate/slsa/v2.md b/docs/predicate/slsa/v2.md index 91f635f781..bfece9a194 100644 --- a/docs/predicate/slsa/v2.md +++ b/docs/predicate/slsa/v2.md @@ -57,7 +57,8 @@ Name | Description -----|------------- **pipeline**|Resolved (immutable) reference to the remote pipeline. **task**|Resolved (immutable) reference to remote tasks. This name is used for a build performed via a direct `TaskRun`. -**pipelineTask**|Resolved (immutable) reference to remote `PipelineTasks`. +**pipelineTask**|Resolved (immutable) reference to a remote Task used by a PipelineTask in `v2alpha3`. +**pipelineTask/{pipelineTaskName}**|Resolved (immutable) reference to a remote Task used by a PipelineTask in `v2alpha4`. The name comes from the `tekton.dev/pipelineTask` label, or the TaskRun name when that label is absent. N/A|Resolved reference to `step` and `sidecar container images`.
  • Even if the images are referenced by a tag in the spec it will be resolved and its digest will be included.
  • This resolved dependency is not named because it does not add anything of value that is needed right now from step/sidecar images.
  • **input/results**|Input Artifacts used for the build, e.g. `Source code`. These are type-hinted results emitted by the task. @@ -120,6 +121,8 @@ Parameter | Type | Description -----------|------|------------ results| object | [Optional] Results produced by the pipeline/task. Content will be stored as bytes. +Starting with `v2alpha4`, step result byProduct names use the format `stepResults/{pipelineTaskName}/{stepName}/{resultName}`, where `pipelineTaskName` comes from the `tekton.dev/pipelineTask` label (or the TaskRun name for standalone runs). + # Examples ## Provenance Example for referenced build config diff --git a/docs/slsa-provenance.md b/docs/slsa-provenance.md index 1ba7786567..f481afab8d 100644 --- a/docs/slsa-provenance.md +++ b/docs/slsa-provenance.md @@ -321,10 +321,14 @@ spec: ## `v2alpha4` formatter +Starting with version `v2alpha4`, pipeline task resolvedDependencies use `pipelineTask/{pipelineTaskName}` instead of `pipelineTask`. The name comes from the `tekton.dev/pipelineTask` label, falling back to the TaskRun name when that label is absent. + Starting with version `v2alpha4`, the type-hinted object results value now can include a new boolean flag called `isBuildArtifact`. When set to `true`, this flag indicates the output artifact should be considered as `subject` in the executed TaskRun/PipelineRun. The `isBuildArtifact` can be set in results whose type-hint uses the `*ARTIFACT_OUTPUTS` format. Results using the `IMAGES` and `*IMAGE_URL` / `*IMAGE_DIGEST` type-hint format will still be considered as `subject` automatically; all other results will be classified as `byProduct` +Starting with version `v2alpha4`, byProduct names for step results include the step name and use the `tekton.dev/pipelineTask` label instead of the Kubernetes-generated TaskRun name: `stepResults/{pipelineTaskName}/{stepName}/{resultName}`. + For instance, in the following TaskRun: ```yaml 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 f661a84958..ca95766d6e 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go @@ -55,6 +55,9 @@ type AddTaskDescriptorContent func(*objects.TaskRunObjectV1) (*intoto.ResourceDe type ResolveOptions struct { // Indicates if StepActions type-hinted results should be read to resolve dependecies. WithStepActionsResults bool + // UseQualifiedTaskNames controls whether resolved dependency names for pipeline tasks + // include the task identifier (e.g. "pipelineTask/my-task" instead of "pipelineTask"). + UseQualifiedTaskNames bool } // ConvertMaterialsToResolvedDependencies converts a SLSAv0.2 Material to a resolved dependency @@ -80,14 +83,17 @@ func RemoveDuplicateResolvedDependencies(resolvedDependencies []*intoto.Resource // 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. + // Ignore names for existing dependency types, but keep qualified pipeline task names + // so distinct tasks using the same resource remain in the provenance. rDep := intoto.ResourceDescriptor{} rDep.Uri = resolvedDependency.Uri rDep.Digest = resolvedDependency.Digest - // pipelinTasks store content with the slsa-tekton buildType + // Pipeline tasks store content with the slsa-tekton buildType. rDep.Content = resolvedDependency.Content - // This allows us to ignore dependencies that have the same uri and digest. + if strings.HasPrefix(resolvedDependency.Name, PipelineTaskConfigName+"/") { + rDep.Name = resolvedDependency.Name + } + // This allows us to ignore dependencies with the same identifying fields. rd, err := protojson.Marshal(&rDep) if err != nil { return nil, err @@ -167,6 +173,13 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV continue } if rd != nil { + if opts.UseQualifiedTaskNames && rd.Name == PipelineTaskConfigName { + name := tr.TaskIdentifier() + if len(taskRuns) > 1 { + name = fmt.Sprintf("%s/%s", name, tr.GetName()) + } + rd.Name = fmt.Sprintf("%s/%s", PipelineTaskConfigName, name) + } resolvedDependencies = append(resolvedDependencies, rd) } 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 bf1af6ca09..58db2a2edc 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 @@ -251,6 +251,60 @@ func TestRemoveDuplicates(t *testing.T) { } } +func TestRemoveDuplicatesQualifiedPipelineTasks(t *testing.T) { + dependency := func(name, content string) *intoto.ResourceDescriptor { + var data []byte + if content != "" { + data = []byte(content) + } + return &intoto.ResourceDescriptor{ + Name: name, + Uri: "git+https://example.com/task", + Digest: common.DigestSet{"sha256": "abc123"}, + Content: data, + } + } + + tests := []struct { + name string + rds []*intoto.ResourceDescriptor + want []*intoto.ResourceDescriptor + }{ + { + name: "identical qualified tasks collapse", + rds: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", ""), dependency("pipelineTask/build", "")}, + want: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", "")}, + }, + { + name: "different task names remain distinct", + rds: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", ""), dependency("pipelineTask/test", "")}, + want: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", ""), dependency("pipelineTask/test", "")}, + }, + { + name: "different task content remains distinct", + rds: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", "first"), dependency("pipelineTask/build", "second")}, + want: []*intoto.ResourceDescriptor{dependency("pipelineTask/build", "first"), dependency("pipelineTask/build", "second")}, + }, + { + name: "legacy pipeline task names still collapse", + rds: []*intoto.ResourceDescriptor{dependency("pipelineTask", ""), dependency("pipelineTask", "")}, + want: []*intoto.ResourceDescriptor{dependency("pipelineTask", "")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := RemoveDuplicateResolvedDependencies(tc.rds) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { + t.Errorf("RemoveDuplicateResolvedDependencies() mismatch (-want +got): %s", diff) + } + }) + } +} + func createPro(path string) *objects.PipelineRunObjectV1 { var err error pr, err := objectloader.PipelineRunV1FromFile(path) diff --git a/pkg/chains/formats/slsa/internal/results/results.go b/pkg/chains/formats/slsa/internal/results/results.go index 0c4b168a1c..74b8a7c018 100644 --- a/pkg/chains/formats/slsa/internal/results/results.go +++ b/pkg/chains/formats/slsa/internal/results/results.go @@ -56,6 +56,34 @@ func GetResultsWithoutBuildArtifacts(objName string, results []objects.Result, r return byProd, nil } +// GetStepResultsWithoutBuildArtifacts returns step results that are not build artifacts, +// using the step name from each Result to produce a 3-segment byProduct name. +func GetStepResultsWithoutBuildArtifacts(taskName string, 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 + } + + if isOCIImage(r.Name) { + continue + } + + content, err := json.Marshal(r.Value) + if err != nil { + return nil, err + } + + byProd = append(byProd, &slsa.ResourceDescriptor{ + Name: fmt.Sprintf(resultTypePrefix, taskName, r.StepName, r.Name), + Content: content, + MediaType: "application/json", + }) + } + + return byProd, nil +} + func isOCIImage(resName string) bool { for _, suffix := range imageResultsNamesSuffixs { if strings.HasSuffix(resName, suffix) { diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go index f5b279d141..3026767d01 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go @@ -15,6 +15,7 @@ package pipelinerun import ( "context" + "fmt" intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" @@ -41,7 +42,7 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, return nil, err } - opts := resolveddependencies.ResolveOptions{WithStepActionsResults: true} + opts := resolveddependencies.ResolveOptions{WithStepActionsResults: true, UseQualifiedTaskNames: true} bd, err := builddefinition.GetPipelineRunBuildDefinition(ctx, pro, slsaconfig, opts) if err != nil { return nil, err @@ -63,8 +64,13 @@ func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaCon return byProd, nil } + trCounts := taskRunCountByPipelineTask(pro) for _, tro := range pro.GetExecutedTasks() { - taskProds, err := taskrun.ByProducts(tro) + taskName := tro.TaskIdentifier() + if trCounts[taskName] > 1 { + taskName = fmt.Sprintf("%s/%s", taskName, tro.GetName()) + } + taskProds, err := taskrun.ByProductsWithTaskName(tro, taskName) if err != nil { return nil, err } @@ -74,6 +80,31 @@ func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaCon return byProd, nil } +// taskRunCountByPipelineTask returns a map of pipelineTask name to the number of +// TaskRuns associated with it. Used to detect matrix fan-out. +func taskRunCountByPipelineTask(pro *objects.PipelineRunObjectV1) map[string]int { + counts := map[string]int{} + pSpec := pro.Status.PipelineSpec + if pSpec == nil { + return counts + } + tasks := pSpec.Tasks + tasks = append(tasks, pSpec.Finally...) + for _, task := range tasks { + taskRuns := pro.GetTaskRunsFromTask(task.Name) + count := 0 + for _, tr := range taskRuns { + if tr != nil && tr.Status.CompletionTime != nil { + count++ + } + } + if count > 0 { + counts[task.Name] = count + } + } + return counts +} + // SubjectDigests calculates the subjects associated with the given PipelineRun. func SubjectDigests(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { subjects := extract.SubjectsFromBuildArtifact(ctx, pro.GetResults()) 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 76303c9f41..140d1f3e1b 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go @@ -39,6 +39,11 @@ import ( logtesting "knative.dev/pkg/logging/testing" ) +const ( + pipelineTaskGitClone = "pipelineTask/git-clone" + pipelineTaskBuild = "pipelineTask/build" +) + func TestByProducts(t *testing.T) { resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} pr := &v1.PipelineRun{ @@ -125,7 +130,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}, - Name: "pipelineTask", + Name: pipelineTaskGitClone, }, { Uri: "oci://gcr.io/test1/test1", @@ -134,7 +139,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}, - Name: "pipelineTask", + Name: pipelineTaskBuild, }, { Uri: "oci://gcr.io/test2/test2", @@ -220,7 +225,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}, - Name: "pipelineTask", + Name: pipelineTaskGitClone, }, { Uri: "oci://gcr.io/test1/test1", @@ -229,7 +234,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}, - Name: "pipelineTask", + Name: pipelineTaskBuild, }, { Uri: "oci://gcr.io/test2/test2", @@ -291,15 +296,15 @@ func TestGenerateAttestation(t *testing.T) { Content: []uint8(`"pkg:deb/debian/curl@7.50.3-1"`), MediaType: JSONMediaType, }, { - Name: "stepResults/git-clone/step1_result1-ARTIFACT_INPUTS", + Name: "stepResults/git-clone/step1/step1_result1-ARTIFACT_INPUTS", Content: []uint8(`{"digest":"sha1:7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601","uri":"https://github.com/tektoncd/pipeline"}`), MediaType: JSONMediaType, }, { - Name: "stepResults/taskrun-build/step1_result1", + Name: "stepResults/build/step1/step1_result1", Content: []uint8(`"result-value"`), MediaType: JSONMediaType, }, { - Name: "stepResults/taskrun-build/step1_result1-ARTIFACT_OUTPUTS", + Name: "stepResults/build/step2/step1_result1-ARTIFACT_OUTPUTS", Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"gcr.io/my/image/fromstep2"}`), MediaType: JSONMediaType, }, @@ -368,7 +373,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/catalog", Digest: common.DigestSet{"sha1": "x123"}, - Name: "pipelineTask", + Name: pipelineTaskGitClone, }, { Uri: "oci://gcr.io/test1/test1", @@ -377,7 +382,7 @@ func TestGenerateAttestation(t *testing.T) { { Uri: "git+https://github.com/test", Digest: common.DigestSet{"sha1": "ab123"}, - Name: "pipelineTask", + Name: pipelineTaskBuild, }, { Uri: "oci://gcr.io/test2/test2", @@ -434,11 +439,11 @@ func TestGenerateAttestation(t *testing.T) { Content: []uint8(`"pkg:deb/debian/curl@7.50.3-1"`), MediaType: JSONMediaType, }, { - Name: "stepResults/git-clone/step1_result1-ARTIFACT_INPUTS", + Name: "stepResults/git-clone/step1/step1_result1-ARTIFACT_INPUTS", Content: []uint8(`{"digest":"sha1:7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601","uri":"https://github.com/tektoncd/pipeline"}`), MediaType: JSONMediaType, }, { - Name: "stepResults/taskrun-build/step1_result1", + Name: "stepResults/build/step1/step1_result1", Content: []uint8(`"result-value"`), MediaType: JSONMediaType, }, diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go index 4bee349d95..6800bbb058 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go @@ -30,7 +30,7 @@ import ( const ( taskRunResults = "taskRunResults/%s/%s" - taskRunStepResults = "stepResults/%s/%s" + taskRunStepResults = "stepResults/%s/%s/%s" // 3 segments" stepResults/{taskIdentifier}/{stepName}/{resultName} ) // GenerateAttestation returns the provenance for the given taskrun in SALSA 1.0 format. @@ -51,17 +51,23 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsa return provenance.GetSLSA1Statement(tro, sub, &bd, bp, slsaConfig) } -// ByProducts returns the results categorized as byproduct from the given TaskRun. func ByProducts(tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) { + return ByProductsWithTaskName(tro, tro.TaskIdentifier()) +} + +// ByProductsWithTaskName is like ByProducts but uses the given taskName instead +// of TaskIdentifier(). This is needed for matrix fan-out where multiple TaskRuns +// share the same pipelineTask label and need a disambiguated name. +func ByProductsWithTaskName(tro *objects.TaskRunObjectV1, taskName string) ([]*intoto.ResourceDescriptor, error) { byProd := []*intoto.ResourceDescriptor{} - res, err := results.GetResultsWithoutBuildArtifacts(tro.GetName(), tro.GetResults(), taskRunResults) + res, err := results.GetResultsWithoutBuildArtifacts(taskName, tro.GetResults(), taskRunResults) if err != nil { return nil, err } byProd = append(byProd, res...) - res, err = results.GetResultsWithoutBuildArtifacts(tro.GetName(), tro.GetStepResults(), taskRunStepResults) + res, err = results.GetStepResultsWithoutBuildArtifacts(taskName, tro.GetStepResults(), taskRunStepResults) if err != nil { return nil, err } 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 f19432fdfa..ab45c65cee 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go @@ -138,12 +138,12 @@ func TestTaskRunGenerateAttestation(t *testing.T) { }, Byproducts: []*intoto.ResourceDescriptor{ { - Name: "stepResults/taskrun-build/step1_result1", + Name: "stepResults/build/step1/step1_result1", MediaType: "application/json", Content: []uint8(`"result-value"`), }, { - Name: "stepResults/taskrun-build/step1_result1-ARTIFACT_OUTPUTS", + Name: "stepResults/build/step2/step1_result1-ARTIFACT_OUTPUTS", MediaType: "application/json", Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"gcr.io/my/image/fromstep2"}`), }, diff --git a/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go b/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go index 7a57663ab6..e45fb7b991 100644 --- a/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/slsav2_test.go @@ -169,12 +169,12 @@ func TestTaskRunCreatePayload1(t *testing.T) { }, Byproducts: []*intoto.ResourceDescriptor{ { - Name: "stepResults/taskrun-build/step1_result1", + Name: "stepResults/build/step1/step1_result1", Content: resultBytesStepResult, MediaType: jsonMediaType, }, { - Name: "stepResults/taskrun-build/step1_result1-ARTIFACT_OUTPUTS", + Name: "stepResults/build/step2/step1_result1-ARTIFACT_OUTPUTS", Content: resultBytesStepResultObj, MediaType: jsonMediaType, }, @@ -292,7 +292,7 @@ func TestTaskRunCreatePayload2(t *testing.T) { MediaType: jsonMediaType, }, { - Name: "stepResults/git-clone/step1_result1-ARTIFACT_INPUTS", + Name: "stepResults/git-clone/step1/step1_result1-ARTIFACT_INPUTS", Content: resultBytesObj, MediaType: jsonMediaType, }, diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index d04f8ecd9a..9026b2b56c 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -52,9 +52,10 @@ type Object interface { // Result is a generic key value store containing the results // of Tekton operations. (eg. PipelineRun and TaskRun results) type Result struct { - Name string - Type v1.ResultsType - Value v1.ParamValue + Name string + Type v1.ResultsType + Value v1.ParamValue + StepName string } // StepProvenance associates a step name with its remote StepAction provenanance @@ -170,8 +171,9 @@ func (tro *TaskRunObjectV1) GetStepResults() []Result { for _, s := range tro.Status.Steps { for _, r := range s.Results { res = append(res, Result{ - Name: r.Name, - Value: r.Value, + Name: r.Name, + Value: r.Value, + StepName: s.Name, }) } } @@ -437,3 +439,11 @@ func (pro *PipelineRunObjectV1) GetExecutedTasks() (tro []*TaskRunObjectV1) { return } + +// TaskIdentifier returns the pipeline task name label if present, falling back to the TaskRun's Kubernetes name. +func (tro *TaskRunObjectV1) TaskIdentifier() string { + if label, ok := tro.Labels[PipelineTaskLabel]; ok { + return label + } + return tro.GetName() +} diff --git a/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json b/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json index cb0838a386..7a6928cf2c 100644 --- a/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json +++ b/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json @@ -189,7 +189,7 @@ { "content": "eyJkaWdlc3QiOiJzaGEyNTY6NTg2Nzg5YWEwMzFmYWZjN2Q3OGE1MzkzY2RjNzcyZTBiNTUxMDdlYTU0YmI4YmNmM2YyY2RhYzZjNmRhNTFlZSIsInVyaSI6Imdjci5pby9mb28vaW1nMiJ9", "mediaType": "application/json", - "name": "taskRunResults/{{index .ChildTaskRunNames 0}}/output2" + "name": "taskRunResults/t1/output2" } ], "metadata": {