diff --git a/pkg/chains/formats/slsa/extract/extract.go b/pkg/chains/formats/slsa/extract/extract.go index cc107f0e76..967514432a 100644 --- a/pkg/chains/formats/slsa/extract/extract.go +++ b/pkg/chains/formats/slsa/extract/extract.go @@ -26,6 +26,7 @@ import ( "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/artifact" "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" @@ -80,56 +81,16 @@ func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject, slsa } trSubjects := subjectsFromTektonObject(ctx, tr) - for _, s := range trSubjects { - result = addSubject(result, s) - } + result = artifact.AppendSubject(result, trSubjects...) } } // also add subjects observed from pipelinerun level with duplication removed - for _, s := range prSubjects { - result = addSubject(result, s) - } + result = artifact.AppendSubject(result, prSubjects...) return result } -// addSubject adds a new subject item to the original slice. -func addSubject(original []intoto.Subject, item intoto.Subject) []intoto.Subject { - - for i, s := range original { - // if there is an equivalent entry in the original slice, merge item's DigestSet - // into the existing entry's DigestSet. - if subjectEqual(s, item) { - mergeMaps(original[i].Digest, item.Digest) - return original - } - } - - original = append(original, item) - return original -} - -// two subjects are equal if and only if they have same name and have at least -// one common algorithm and hex value. -func subjectEqual(x, y intoto.Subject) bool { - if x.Name != y.Name { - return false - } - for algo, hex := range x.Digest { - if y.Digest[algo] == hex { - return true - } - } - return false -} - -func mergeMaps(m1 map[string]string, m2 map[string]string) { - for k, v := range m2 { - m1[k] = v - } -} - func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []intoto.Subject { logger := logging.FromContext(ctx) var subjects []intoto.Subject @@ -137,7 +98,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i imgs := artifacts.ExtractOCIImagesFromResults(ctx, obj) for _, i := range imgs { if d, ok := i.(name.Digest); ok { - subjects = append(subjects, intoto.Subject{ + subjects = artifact.AppendSubject(subjects, intoto.Subject{ Name: d.Repository.Name(), Digest: common.DigestSet{ "sha256": strings.TrimPrefix(d.DigestStr(), "sha256:"), @@ -153,7 +114,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 = append(subjects, intoto.Subject{ + subjects = artifact.AppendSubject(subjects, intoto.Subject{ Name: obj.URI, Digest: common.DigestSet{ splits[0]: splits[1], @@ -166,7 +127,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i splits := strings.Split(s.Digest, ":") alg := splits[0] digest := splits[1] - subjects = append(subjects, intoto.Subject{ + subjects = artifact.AppendSubject(subjects, intoto.Subject{ Name: s.URI, Digest: common.DigestSet{ alg: digest, @@ -204,7 +165,7 @@ func subjectsFromTektonObject(ctx context.Context, obj objects.TektonObject) []i } } } - subjects = append(subjects, intoto.Subject{ + subjects = artifact.AppendSubject(subjects, intoto.Subject{ Name: url, Digest: common.DigestSet{ "sha256": strings.TrimPrefix(digest, "sha256:"), diff --git a/pkg/chains/formats/slsa/internal/artifact/append.go b/pkg/chains/formats/slsa/internal/artifact/append.go new file mode 100644 index 0000000000..67808ea422 --- /dev/null +++ b/pkg/chains/formats/slsa/internal/artifact/append.go @@ -0,0 +1,126 @@ +/* +Copyright 2023 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package artifact + +import ( + intoto "github.com/in-toto/in-toto-golang/in_toto" + "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" +) + +// AppendSubject adds a new subject entry to the original slice with duplicates removed/merged +func AppendSubject(original []intoto.Subject, items ...intoto.Subject) []intoto.Subject { + var artifacts []artifact + for _, s := range original { + artifacts = append(artifacts, subjectToArtifact(s)) + } + + for _, s := range items { + artifacts = addArtifact(artifacts, subjectToArtifact(s)) + } + + var result []intoto.Subject + for _, a := range artifacts { + result = append(result, artifactToSubject(a)) + } + return result +} + +// AppendMaterials adds new material(s) to the original slice with duplicates removed/merged +func AppendMaterials(original []common.ProvenanceMaterial, items ...common.ProvenanceMaterial) []common.ProvenanceMaterial { + var artifacts []artifact + for _, m := range original { + artifacts = append(artifacts, materialToArtifact(m)) + } + + for _, m := range items { + artifacts = addArtifact(artifacts, materialToArtifact(m)) + } + + var result []common.ProvenanceMaterial + for _, a := range artifacts { + result = append(result, artifactToMaterial(a)) + } + return result +} + +type artifact struct { + name string + digestSet map[string]string +} + +// AddArtifact adds a new artifact item to the original slice. +func addArtifact(original []artifact, item artifact) []artifact { + + for i, a := range original { + // if there is an equivalent entry in the original slice, merge the + // artifact's DigestSet into the existing entry's DigestSet. + if artifactEqual(a, item) { + mergeMaps(original[i].digestSet, item.digestSet) + return original + } + } + + original = append(original, item) + return original +} + +// two artifacts are equal if and only if they have same name and have at least +// one common algorithm and hex value. +func artifactEqual(x, y artifact) bool { + if x.name != y.name { + return false + } + for algo, hex := range x.digestSet { + if y.digestSet[algo] == hex { + return true + } + } + return false +} + +func mergeMaps(m1 map[string]string, m2 map[string]string) { + for k, v := range m2 { + m1[k] = v + } +} + +func subjectToArtifact(s intoto.Subject) artifact { + return artifact{ + name: s.Name, + digestSet: s.Digest, + } +} + +func artifactToSubject(a artifact) intoto.Subject { + return intoto.Subject{ + Name: a.name, + Digest: a.digestSet, + } +} + +func materialToArtifact(m common.ProvenanceMaterial) artifact { + return artifact{ + name: m.URI, + digestSet: m.Digest, + } +} + +func artifactToMaterial(a artifact) common.ProvenanceMaterial { + return common.ProvenanceMaterial{ + URI: 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 new file mode 100644 index 0000000000..828aa5810c --- /dev/null +++ b/pkg/chains/formats/slsa/internal/artifact/append_test.go @@ -0,0 +1,320 @@ +/* +Copyright 2023 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package artifact + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + intoto "github.com/in-toto/in-toto-golang/in_toto" + "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" +) + +func TestAppendSubject(t *testing.T) { + tests := []struct { + name string + original []intoto.Subject + toAdd []intoto.Subject + want []intoto.Subject + }{{ + name: "add a completely new subject", + original: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + Name: "gcr.io/cloud-marketplace-containers/google/bazel", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + toAdd: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Digest: common.DigestSet{ + "sha256": "c", + }, + }, + }, + want: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + Name: "gcr.io/cloud-marketplace-containers/google/bazel", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Digest: common.DigestSet{ + "sha256": "c", + }, + }, + }, + }, { + name: "add a subject with same uri and digest", + original: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + toAdd: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + want: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + }, { + name: "add a subject with same uri but different digest", + original: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + toAdd: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + want: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + }, + { + name: "add a subject with same uri, one common digest and one different digest", + original: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha224": "b", + }, + }, + }, + toAdd: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha512": "c", + }, + }, + }, + want: []intoto.Subject{ + { + Name: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha224": "b", + "sha512": "c", + }, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := AppendSubject(tc.original, tc.toAdd...) + + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("materials(): -want +got: %s", diff) + } + }) + } +} + +func TestAppendMaterials(t *testing.T) { + tests := []struct { + name string + original []common.ProvenanceMaterial + toAdd []common.ProvenanceMaterial + want []common.ProvenanceMaterial + }{{ + name: "add a completely new material", + original: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + URI: "gcr.io/cloud-marketplace-containers/google/bazel", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + toAdd: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Digest: common.DigestSet{ + "sha256": "c", + }, + }, + }, + want: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + URI: "gcr.io/cloud-marketplace-containers/google/bazel", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", + Digest: common.DigestSet{ + "sha256": "c", + }, + }, + }, + }, { + name: "add a material with same uri and digest", + original: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + toAdd: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + want: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + }, { + name: "add a material with same uri but different digest", + original: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, + }, + toAdd: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + want: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + }, + }, { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "b", + }, + }, + }, + }, + { + name: "add a material with same uri, one common digest and one different digest", + original: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha224": "b", + }, + }, + }, + toAdd: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha512": "c", + }, + }, + }, + want: []common.ProvenanceMaterial{ + { + URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", + Digest: common.DigestSet{ + "sha256": "a", + "sha224": "b", + "sha512": "c", + }, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := AppendMaterials(tc.original, tc.toAdd...) + + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("materials(): -want +got: %s", diff) + } + }) + } +} diff --git a/pkg/chains/formats/slsa/internal/material/material.go b/pkg/chains/formats/slsa/internal/material/material.go index eb31fb2a48..864dc14ab4 100644 --- a/pkg/chains/formats/slsa/internal/material/material.go +++ b/pkg/chains/formats/slsa/internal/material/material.go @@ -18,7 +18,6 @@ package material import ( "context" - "encoding/json" "fmt" "strings" @@ -26,6 +25,7 @@ import ( "github.com/tektoncd/chains/internal/backport" "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/formats/slsa/attest" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/artifact" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" "knative.dev/pkg/logging" @@ -45,25 +45,20 @@ func TaskMaterials(ctx context.Context, tro *objects.TaskRunObject) ([]common.Pr if err != nil { return nil, err } - mats = append(mats, stepMaterials...) + mats = artifact.AppendMaterials(mats, stepMaterials...) // add sidecar images sidecarMaterials, err := FromSidecarImages(tro) if err != nil { return nil, err } - mats = append(mats, sidecarMaterials...) + mats = artifact.AppendMaterials(mats, sidecarMaterials...) - mats = append(mats, FromTaskParamsAndResults(ctx, tro)...) + mats = artifact.AppendMaterials(mats, FromTaskParamsAndResults(ctx, tro)...) // add task resources - mats = append(mats, FromTaskResources(ctx, tro)...) + mats = artifact.AppendMaterials(mats, FromTaskResources(ctx, tro)...) - // remove duplicate materials - mats, err = removeDuplicateMaterials(mats) - if err != nil { - return mats, err - } return mats, nil } @@ -75,7 +70,7 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObject, slsa URI: p.RefSource.URI, Digest: p.RefSource.Digest, } - mats = append(mats, m) + mats = artifact.AppendMaterials(mats, m) } pSpec := pro.Status.PipelineSpec if pSpec != nil { @@ -92,14 +87,14 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObject, slsa if err != nil { return mats, err } - mats = append(mats, stepMaterials...) + mats = artifact.AppendMaterials(mats, stepMaterials...) // add sidecar images sidecarMaterials, err := FromSidecarImages(tr) if err != nil { return nil, err } - mats = append(mats, sidecarMaterials...) + mats = artifact.AppendMaterials(mats, sidecarMaterials...) // add remote task configsource information in materials if tr.Status.Provenance != nil && tr.Status.Provenance.RefSource != nil { @@ -107,18 +102,13 @@ func PipelineMaterials(ctx context.Context, pro *objects.PipelineRunObject, slsa URI: tr.Status.Provenance.RefSource.URI, Digest: tr.Status.Provenance.RefSource.Digest, } - mats = append(mats, m) + mats = artifact.AppendMaterials(mats, m) } } } - mats = append(mats, FromPipelineParamsAndResults(ctx, pro, slsaconfig)...) + mats = artifact.AppendMaterials(mats, FromPipelineParamsAndResults(ctx, pro, slsaconfig)...) - // remove duplicate materials - mats, err := removeDuplicateMaterials(mats) - if err != nil { - return mats, err - } return mats, nil } @@ -130,7 +120,7 @@ func FromStepImages(tro *objects.TaskRunObject) ([]common.ProvenanceMaterial, er if err != nil { return nil, err } - mats = append(mats, m) + mats = artifact.AppendMaterials(mats, m) } return mats, nil } @@ -143,7 +133,7 @@ func FromSidecarImages(tro *objects.TaskRunObject) ([]common.ProvenanceMaterial, if err != nil { return nil, err } - mats = append(mats, m) + mats = artifact.AppendMaterials(mats, m) } return mats, nil } @@ -203,7 +193,7 @@ func FromTaskResources(ctx context.Context, tro *objects.TaskRunObject) []common } } m.URI = attest.SPDXGit(url, revision) - mats = append(mats, m) + mats = artifact.AppendMaterials(mats, m) } } return mats @@ -253,7 +243,7 @@ func FromTaskParamsAndResults(ctx context.Context, tro *objects.TaskRunObject) [ var mats []common.ProvenanceMaterial if commit != "" && url != "" { - mats = append(mats, common.ProvenanceMaterial{ + mats = artifact.AppendMaterials(mats, common.ProvenanceMaterial{ URI: url, // TODO. this could be sha256 as well. Fix in another PR. Digest: map[string]string{"sha1": commit}, @@ -261,38 +251,16 @@ func FromTaskParamsAndResults(ctx context.Context, tro *objects.TaskRunObject) [ } sms := artifacts.RetrieveMaterialsFromStructuredResults(ctx, tro, artifacts.ArtifactsInputsResultName) - mats = append(mats, sms...) + mats = artifact.AppendMaterials(mats, sms...) return mats } -// removeDuplicateMaterials removes duplicate materials from the slice of materials. -// Original order of materials is retained. -func removeDuplicateMaterials(mats []common.ProvenanceMaterial) ([]common.ProvenanceMaterial, error) { - out := make([]common.ProvenanceMaterial, 0, len(mats)) - - // make map to store seen materials - seen := map[string]bool{} - for _, mat := range mats { - m, err := json.Marshal(mat) - if err != nil { - return nil, err - } - if seen[string(m)] { - continue - } - - seen[string(m)] = true - out = append(out, mat) - } - return out, nil -} - // FromPipelineParamsAndResults extracts type hinted params and results and adds the url and digest to materials. func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunObject, slsaconfig *slsaconfig.SlsaConfig) []common.ProvenanceMaterial { mats := []common.ProvenanceMaterial{} sms := artifacts.RetrieveMaterialsFromStructuredResults(ctx, pro, artifacts.ArtifactsInputsResultName) - mats = append(mats, sms...) + mats = artifact.AppendMaterials(mats, sms...) var commit, url string @@ -310,7 +278,7 @@ func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunO continue } materialsFromTasks := FromTaskParamsAndResults(ctx, tr) - mats = append(mats, materialsFromTasks...) + mats = artifact.AppendMaterials(mats, materialsFromTasks...) } } @@ -351,7 +319,7 @@ func FromPipelineParamsAndResults(ctx context.Context, pro *objects.PipelineRunO } if len(commit) > 0 && len(url) > 0 { url = attest.SPDXGit(url, "") - mats = append(mats, common.ProvenanceMaterial{ + mats = artifact.AppendMaterials(mats, common.ProvenanceMaterial{ URI: url, Digest: map[string]string{"sha1": commit}, }) diff --git a/pkg/chains/formats/slsa/internal/material/material_test.go b/pkg/chains/formats/slsa/internal/material/material_test.go index 47ac15030b..2f86b45f37 100644 --- a/pkg/chains/formats/slsa/internal/material/material_test.go +++ b/pkg/chains/formats/slsa/internal/material/material_test.go @@ -439,142 +439,6 @@ func TestFromImageID(t *testing.T) { } } -func TestRemoveDuplicates(t *testing.T) { - tests := []struct { - name string - mats []common.ProvenanceMaterial - want []common.ProvenanceMaterial - }{{ - name: "no duplicate materials", - mats: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, { - URI: "gcr.io/cloud-marketplace-containers/google/bazel", - Digest: common.DigestSet{ - "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", - Digest: common.DigestSet{ - "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", - }, - }, - }, - want: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, { - URI: "gcr.io/cloud-marketplace-containers/google/bazel", - Digest: common.DigestSet{ - "sha256": "010a1ecd1a8c3610f12039a25b823e3a17bd3e8ae455a53e340dcfdd37a49964", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/sidecar-git-init", - Digest: common.DigestSet{ - "sha256": "a1234f6e7a69617db57b685893256f978436277094c21d43b153994acd8a09567", - }, - }, - }, - }, { - name: "same uri and digest", - mats: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, - }, - want: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, - }, - }, { - name: "same uri but different digest", - mats: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", - }, - }, - }, - want: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", - }, - }, - }, - }, { - name: "same uri but different digest, swap order", - mats: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, - }, - want: []common.ProvenanceMaterial{ - { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01248", - }, - }, { - URI: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init", - Digest: common.DigestSet{ - "sha256": "b963f6e7a69617db57b685893256f978436277094c21d43b153994acd8a01247", - }, - }, - }, - }} - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - mat, err := removeDuplicateMaterials(tc.mats) - if err != nil { - t.Fatalf("Did not expect an error but got %v", err) - } - if diff := cmp.Diff(tc.want, mat); diff != "" { - t.Errorf("materials(): -want +got: %s", diff) - } - }) - } -} - //nolint:all func TestFromPipelineParamsAndResults(t *testing.T) { tests := []struct {