diff --git a/examples/v2alpha4/pipeline-with-repeated-results.yaml b/examples/v2alpha4/pipeline-with-repeated-results.yaml new file mode 100644 index 0000000000..e259c1d9e7 --- /dev/null +++ b/examples/v2alpha4/pipeline-with-repeated-results.yaml @@ -0,0 +1,64 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + name: pipeline-test-run +spec: + pipelineSpec: + results: + - name: output1-ARTIFACT_OUTPUTS + value: $(tasks.t1.results.output1-ARTIFACT_OUTPUTS) + - name: output2-ARTIFACT_OUTPUTS + value: $(tasks.t1.results.output2) + - name: output3-ARTIFACT_OUTPUTS + value: $(tasks.t2.results.output3-ARTIFACT_OUTPUTS) + tasks: + - name: t1 + taskSpec: + results: + - name: output1-ARTIFACT_OUTPUTS + type: object + properties: + uri: {} + digest: {} + isBuildArtifact: {} + + - name: output2 + type: object + properties: + uri: {} + digest: {} + + steps: + - name: step1 + image: busybox:glibc + script: | + echo -n "Hello!" + echo -n "{\"uri\":\"gcr.io/foo/img1\", \"digest\":\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\", \"isBuildArtifact\": \"true\" }" > $(results.output1-ARTIFACT_OUTPUTS.path) + echo -n "{\"uri\":\"gcr.io/foo/img2\", \"digest\":\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\"}" > $(results.output2.path) + + - name: t2 + taskSpec: + results: + - name: output3-ARTIFACT_OUTPUTS + type: object + properties: + uri: {} + digest: {} + isBuildArtifact: {} + steps: + - name: step1 + image: busybox:glibc + script: | + echo -n "Hello!" + echo -n "{\"uri\":\"gcr.io/foo/img1\", \"digest\":\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\", \"isBuildArtifact\": \"true\" }" > $(results.output3-ARTIFACT_OUTPUTS.path) + + - name: t3 + taskSpec: + results: + - name: IMAGES + type: string + steps: + - name: step1 + image: busybox:glibc + script: | + echo -n "gcr.io/foo/img1@sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee" > $(results.IMAGES.path) \ No newline at end of file diff --git a/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun2.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun2.json new file mode 100644 index 0000000000..46af7d9952 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun2.json @@ -0,0 +1,328 @@ +{ + "metadata": { + "name": "pipelinerun-build", + "uid": "abhhf-12354-asjsdbjs23-3435353n" + }, + "spec": { + "params": [ + { + "name": "IMAGE", + "value": "test.io/test/image" + } + ], + "pipelineRef": { + "name": "test-pipeline" + }, + "taskRunTemplate": { + "serviceAccountName": "pipeline" + } + }, + "status": { + "startTime": "2021-03-29T09:50:00Z", + "completionTime": "2021-03-29T09:50:15Z", + "conditions": [ + { + "lastTransitionTime": "2021-03-29T09:50:15Z", + "message": "Tasks Completed: 2 (Failed: 0, Cancelled 0), Skipped: 0", + "reason": "Succeeded", + "status": "True", + "type": "Succeeded" + } + ], + "results": [ + { + "name": "CHAINS-GIT_COMMIT", + "value": "abcd" + }, + { + "name": "CHAINS-GIT_URL", + "value": "https://git.test.com" + }, + { + "name": "IMAGE_URL", + "value": "test.io/test/image" + }, + { + "name": "IMAGE_DIGEST", + "value": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + }, + { + "name": "build-artifact-ARTIFACT_OUTPUTS", + "value": { + "uri": "abc", + "digest": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + "isBuildArtifact": "true" + } + }, + { + "name": "img-ARTIFACT_INPUTS", + "value": { + "uri": "abc","digest": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + } + }, + { + "name": "img2-ARTIFACT_OUTPUTS", + "value": { + "uri": "def","digest": "sha256:","isBuildArtifact": "true" + } + }, + { + "name": "img_no_uri-ARTIFACT_OUTPUTS", + "value": { + "digest": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + } + }, + { + "name": "common-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/common/image", + "digest": "sha256:33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a", + "isBuildArtifact": "true" + } + } + ], + "pipelineSpec": { + "params": [ + { + "description": "Image path on registry", + "name": "IMAGE", + "type": "string" + } + ], + "results": [ + { + "description": "", + "name": "CHAINS-GIT_COMMIT", + "value": "$(tasks.git-clone.results.commit)" + }, + { + "description": "", + "name": "CHAINS-GIT_URL", + "value": "$(tasks.git-clone.results.url)" + }, + { + "description": "", + "name": "IMAGE_URL", + "value": "$(tasks.build.results.IMAGE_URL)" + }, + { + "description": "", + "name": "IMAGE_DIGEST", + "value": "$(tasks.build.results.IMAGE_DIGEST)" + } + ], + "tasks": [ + { + "name": "git-clone", + "params": [ + { + "name": "url", + "value": "https://git.test.com" + }, + { + "name": "revision", + "value": "" + } + ], + "taskRef": { + "kind": "ClusterTask", + "name": "git-clone" + } + }, + { + "name": "build", + "params": [ + { + "name": "CHAINS-GIT_COMMIT", + "value": "$(tasks.git-clone.results.commit)" + }, + { + "name": "CHAINS-GIT_URL", + "value": "$(tasks.git-clone.results.url)" + } + ], + "taskRef": { + "kind": "ClusterTask", + "name": "build" + } + } + ] + }, + "taskRuns": { + "git-clone": { + "pipelineTaskName": "git-clone", + "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": "git-clone-pod", + "startTime": "2021-03-29T09:50:00Z", + "steps": [ + { + "container": "step-clone", + "imageID": "test.io/test/clone-image", + "name": "clone", + "terminated": { + "exitCode": 0, + "finishedAt": "2021-03-29T09:50:15Z", + "reason": "Completed", + "startedAt": "2022-05-31T19:13:27Z" + } + } + ], + "results": [ + { + "name": "commit", + "value": "abcd" + }, + { + "name": "url", + "value": "https://git.test.com" + } + ], + "taskSpec": { + "params": [ + { + "description": "Repository URL to clone from.", + "name": "url", + "type": "string" + }, + { + "default": "", + "description": "Revision to checkout. (branch, tag, sha, ref, etc...)", + "name": "revision", + "type": "string" + } + ], + "results": [ + { + "description": "The precise commit SHA that was fetched by this Task.", + "name": "commit" + }, + { + "description": "The precise URL that was fetched by this Task.", + "name": "url" + } + ], + "steps": [ + { + "env": [ + { + "name": "HOME", + "value": "$(params.userHome)" + }, + { + "name": "PARAM_URL", + "value": "$(params.url)" + } + ], + "image": "$(params.gitInitImage)", + "name": "clone", + "resources": {}, + "script": "git clone" + } + ] + } + } + }, + "taskrun-build": { + "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" + } + } + ], + "results": [ + { + "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" + } + ] + } + } + } + }, + "provenance": { + "refSource": { + "uri": "git+https://github.com/test", + "digest": { + "sha1": "28b123" + }, + "entryPoint": "pipeline.yaml" + } + } + } +} diff --git a/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun3.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun3.json new file mode 100644 index 0000000000..0c2ea16856 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun3.json @@ -0,0 +1,175 @@ +{ + "metadata": { + "name": "taskrun-build", + "labels": { + "tekton.dev/pipelineTask": "build" + }, + "uid": "abhhf-12354-asjsdbjs23-3435353n" + }, + "spec": { + "params": [ + { + "name": "IMAGE", + "value": "test.io/test/image" + }, + { + "name": "CHAINS-GIT_COMMIT", + "value": "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/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "results": [ + { + "name": "step1_result1", + "value": "result-value" + } + ] + }, + { + "name": "step2", + "container": "step-step2", + "imageID": "docker-pullable://gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "results": [ + { + "name": "step1_result1-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/common/image", + "digest": "sha256:33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a", + "isBuildArtifact": "true" + } + } + ] + }, + { + "name": "step3", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", + "results": [ + { + "name": "step3_result1-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/my/image/fromstep3", + "digest": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + "isBuildArtifact": "true" + } + } + ] + } + ], + "results": [ + { + "name": "IMAGE_DIGEST", + "value": "sha256:d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466" + }, + { + "name": "IMAGE_URL", + "value": "gcr.io/my/image" + }, + { + "name": "IMAGES", + "value": "gcr.io/common/image@sha256:33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a, gcr.io/task1/result@sha256:c6262181543796435ae52eb233d7337ec570ff0448e333460122f4a65a59a96a" + } + ], + "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": "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": "IMAGE_URL", + "description": "URL of the file just built." + }, + { + "name": "IMAGE_URL", + "description": "Images built." + } + ] + }, + "provenance": { + "refSource": { + "uri": "git+https://github.com/test", + "digest": { + "sha1": "ab123" + }, + "entryPoint": "build.yaml" + }, + "featureFlags": { + "EnableAPIFields": "beta", + "ResultExtractionMethod": "termination-message" + } + } + } +} diff --git a/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun4.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun4.json new file mode 100644 index 0000000000..5694120e72 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun4.json @@ -0,0 +1,133 @@ +{ + "metadata": { + "name": "git-clone", + "labels": { + "tekton.dev/pipelineTask": "git-clone" + }, + "uid": "abhhf-12354-asjsdbjs23-3435353n" + }, + "spec": { + "params": [], + "taskRef": { + "name": "git-clone", + "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/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "results": [ + { + "name": "step1_result1-ARTIFACT_INPUTS", + "value": { + "uri": "https://github.com/tektoncd/pipeline", + "digest": "sha1:7f2f46e1b97df36b2b82d1b1d87c81b8b3d21601" + } + } + ] + }, + { + "name": "step3", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "results": [ + { + "name": "step3_result1-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/task2/step/artifact", + "digest": "sha256:cb06e289303c9529cd980657a5b1a2c8a146c1b13ca08a2bbedb72ec4b7573b9", + "isBuildArtifact": "true" + } + } + ] + }, + { + "name": "step4", + "container": "step-step4", + "imageID": "docker-pullable://gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "results": [ + { + "name": "step4_result1-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/common/image", + "digest": "sha256:33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a", + "isBuildArtifact": "true" + } + } + ] + } + ], + "results": [ + { + "name": "some-uri_DIGEST", + "value": "sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6" + }, + { + "name": "some-uri", + "value": "pkg:deb/debian/curl@7.50.3-1" + }, + { + "name": "task2-ARTIFACT_OUTPUTS", + "value": { + "uri": "gcr.io/common/image", + "digest": "sha256:33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a", + "isBuildArtifact": "true" + } + } + ], + "taskSpec": { + "steps": [ + { + "env": [ + { + "name": "HOME", + "value": "$(params.userHome)" + }, + { + "name": "PARAM_URL", + "value": "$(params.url)" + } + ], + "name": "step1", + "script": "git clone" + } + ], + "params": [], + "results": [ + { + "name": "some-uri_DIGEST", + "description": "Digest of a file to push." + }, + { + "name": "some-uri", + "description": "some calculated uri" + } + ] + }, + "provenance": { + "refSource": { + "uri": "git+https://github.com/catalog", + "digest": { + "sha1": "x123" + }, + "entryPoint": "git-clone.yaml" + } + } + } +} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go index ca504a4006..8864923a74 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go @@ -18,6 +18,7 @@ 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" @@ -81,7 +82,7 @@ func subjectDigests(ctx context.Context, pro *objects.PipelineRunObjectV1, slsac } for _, task := range pro.GetExecutedTasks() { - subjects = append(subjects, taskrun.SubjectDigests(ctx, task)...) + subjects = artifact.AppendSubjects(subjects, taskrun.SubjectDigests(ctx, task)...) } 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 e848d2c46a..deb2141a37 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go @@ -75,13 +75,13 @@ func TestByProducts(t *testing.T) { func TestGenerateAttestation(t *testing.T) { ctx := logtesting.TestContextWithLogger(t) - pr := createPro("../../../testdata/slsa-v2alpha4/pipelinerun1.json") e1BuildStart := time.Unix(1617011400, 0) e1BuildFinished := time.Unix(1617011415, 0) tests := []struct { name string + pr *objects.PipelineRunObjectV1 expectedStatement *intoto.Statement expectedPredicate *slsa.Provenance expectedSubjects []*intoto.ResourceDescriptor @@ -91,6 +91,7 @@ func TestGenerateAttestation(t *testing.T) { }{ { name: "attestation without deepinspection", + pr: createPro("../../../testdata/slsa-v2alpha4/pipelinerun1.json", "../../../testdata/slsa-v2alpha4/taskrun1.json", "../../../testdata/slsa-v2alpha4/taskrun2.json"), expectedSubjects: []*intoto.ResourceDescriptor{ { Name: "abc", @@ -166,6 +167,7 @@ func TestGenerateAttestation(t *testing.T) { }, { name: "attestation with deepinspection", + pr: createPro("../../../testdata/slsa-v2alpha4/pipelinerun1.json", "../../../testdata/slsa-v2alpha4/taskrun1.json", "../../../testdata/slsa-v2alpha4/taskrun2.json"), withDeepInspection: true, expectedSubjects: []*intoto.ResourceDescriptor{ { @@ -287,11 +289,144 @@ func TestGenerateAttestation(t *testing.T) { }, }, }, + { + name: "attestation with no repetead subjects", + pr: createPro("../../../testdata/slsa-v2alpha4/pipelinerun2.json", "../../../testdata/slsa-v2alpha4/taskrun3.json", "../../../testdata/slsa-v2alpha4/taskrun4.json"), + withDeepInspection: true, + expectedSubjects: []*intoto.ResourceDescriptor{ + { + Name: "abc", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/common/image", + Digest: common.DigestSet{ + "sha256": "33e7e52645f4859622e282167d9200da9861b3d0a6e9c93b85e9cae5526ffc0a", + }, + }, + { + Name: "test.io/test/image", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/task2/step/artifact", + Digest: common.DigestSet{ + "sha256": "cb06e289303c9529cd980657a5b1a2c8a146c1b13ca08a2bbedb72ec4b7573b9", + }, + }, + { + Name: "gcr.io/my/image/fromstep3", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "gcr.io/my/image", + Digest: common.DigestSet{ + "sha256": "d31cc8328054de2bd93735f9cbf0ccfb6e0ee8f4c4225da7d8f8cb3900eaf466", + }, + }, + { + Name: "gcr.io/task1/result", + Digest: common.DigestSet{ + "sha256": "c6262181543796435ae52eb233d7337ec570ff0448e333460122f4a65a59a96a", + }, + }, + }, + expectedResolvedDependencies: []*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": "taskrun"}, + }, + { + Name: "inputs/result", + Uri: "git+https://git.test.com.git", + Digest: common.DigestSet{"sha1": "abcd"}, + }, + }, + expectedByProducts: []*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, + }, + }, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := GenerateAttestation(ctx, pr, &slsaconfig.SlsaConfig{ + got, err := GenerateAttestation(ctx, test.pr, &slsaconfig.SlsaConfig{ BuilderID: "test_builder-1", DeepInspectionEnabled: test.withDeepInspection, BuildType: "https://tekton.dev/chains/v2/slsa", @@ -305,7 +440,7 @@ func TestGenerateAttestation(t *testing.T) { BuildDefinition: &slsa.BuildDefinition{ BuildType: "https://tekton.dev/chains/v2/slsa", ExternalParameters: getStruct(t, map[string]any{ - "runSpec": pr.Spec, + "runSpec": test.pr.Spec, }), InternalParameters: getStruct(t, map[string]any{}), ResolvedDependencies: test.expectedResolvedDependencies, @@ -339,22 +474,21 @@ func TestGenerateAttestation(t *testing.T) { } } -func createPro(path string) *objects.PipelineRunObjectV1 { - pr, err := objectloader.PipelineRunFromFile(path) - if err != nil { - panic(err) - } - tr1, err := objectloader.TaskRunFromFile("../../../testdata/slsa-v2alpha4/taskrun1.json") - if err != nil { - panic(err) - } - tr2, err := objectloader.TaskRunFromFile("../../../testdata/slsa-v2alpha4/taskrun2.json") +func createPro(prPath string, trPaths ...string) *objects.PipelineRunObjectV1 { + pr, err := objectloader.PipelineRunFromFile(prPath) if err != nil { panic(err) } p := objects.NewPipelineRunObjectV1(pr) - p.AppendTaskRun(tr1) - p.AppendTaskRun(tr2) + + for _, trPath := range trPaths { + tr, err := objectloader.TaskRunFromFile(trPath) + if err != nil { + panic(err) + } + p.AppendTaskRun(tr) + } + return p } diff --git a/test/examples_test.go b/test/examples_test.go index f317e9e740..e2efa2a766 100644 --- a/test/examples_test.go +++ b/test/examples_test.go @@ -177,6 +177,22 @@ func TestExamples(t *testing.T) { outputLocation: "slsa/v2alpha4", predicate: "slsav1.0", }, + { + name: "pipelinerun-no-repeated-subjects-v2alpha4", + cm: map[string]string{ + "artifacts.pipelinerun.format": "slsa/v2alpha4", + "artifacts.oci.storage": "tekton", + "artifacts.pipelinerun.enable-deep-inspection": "true", + }, + pipelinesCm: map[string]string{ + "enable-api-fields": "alpha", + }, + getExampleObjects: getPipelineRunWithRepeatedBuildArtifacts, + payloadKey: "chains.tekton.dev/payload-pipelinerun-%s", + signatureKey: "chains.tekton.dev/signature-pipelinerun-%s", + outputLocation: "slsa/v2alpha4", + predicate: "slsav1.0", + }, } for _, test := range tests { @@ -527,6 +543,14 @@ func getPipelineRunWithTypeHintedResultsExamples(t *testing.T, ns string) map[st return prs } +func getPipelineRunWithRepeatedBuildArtifacts(t *testing.T, ns string) map[string]objects.TektonObject { + t.Helper() + path := "../examples/v2alpha4/pipeline-with-repeated-results.yaml" + prs := make(map[string]objects.TektonObject) + prs[path] = pipelineRunFromExample(t, ns, path) + return prs +} + func getPipelineRunExamples(t *testing.T, ns string) map[string]objects.TektonObject { t.Helper() examples := make(map[string]objects.TektonObject) diff --git a/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json b/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json new file mode 100644 index 0000000000..3f7651843e --- /dev/null +++ b/test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json @@ -0,0 +1,181 @@ +{ + "_type": "https://in-toto.io/Statement/v1", + "subject": [ + { + "name": "gcr.io/foo/img1", + "digest": { + "sha256": "586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee" + } + } + ], + "predicateType": "https://slsa.dev/provenance/v1", + "predicate": { + "buildDefinition": { + "buildType": "https://tekton.dev/chains/v2/slsa", + "externalParameters": { + "runSpec": { + "pipelineSpec": { + "results": [ + { + "description": "", + "name": "output1-ARTIFACT_OUTPUTS", + "value": "$(tasks.t1.results.output1-ARTIFACT_OUTPUTS)" + }, + { + "description": "", + "name": "output2-ARTIFACT_OUTPUTS", + "value": "$(tasks.t1.results.output2)" + }, + { + "description": "", + "name": "output3-ARTIFACT_OUTPUTS", + "value": "$(tasks.t2.results.output3-ARTIFACT_OUTPUTS)" + } + ], + "tasks": [ + { + "name": "t1", + "taskSpec": { + "metadata": {}, + "results": [ + { + "name": "output1-ARTIFACT_OUTPUTS", + "properties": { + "digest": { + "type": "string" + }, + "isBuildArtifact": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "type": "object" + }, + { + "name": "output2", + "properties": { + "digest": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "type": "object" + } + ], + "spec": null, + "steps": [ + { + "computeResources": {}, + "image": "busybox:glibc", + "name": "step1", + "script": "echo -n \"Hello!\"\necho -n \"{\\\"uri\\\":\\\"gcr.io/foo/img1\\\", \\\"digest\\\":\\\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\\\", \\\"isBuildArtifact\\\": \\\"true\\\" }\" > $(results.output1-ARTIFACT_OUTPUTS.path)\necho -n \"{\\\"uri\\\":\\\"gcr.io/foo/img2\\\", \\\"digest\\\":\\\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\\\"}\" > $(results.output2.path)\n" + } + ] + } + }, + { + "name": "t2", + "taskSpec": { + "metadata": {}, + "results": [ + { + "name": "output3-ARTIFACT_OUTPUTS", + "properties": { + "digest": { + "type": "string" + }, + "isBuildArtifact": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "type": "object" + } + ], + "spec": null, + "steps": [ + { + "computeResources": {}, + "image": "busybox:glibc", + "name": "step1", + "script": "echo -n \"Hello!\"\necho -n \"{\\\"uri\\\":\\\"gcr.io/foo/img1\\\", \\\"digest\\\":\\\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\\\", \\\"isBuildArtifact\\\": \\\"true\\\" }\" > $(results.output3-ARTIFACT_OUTPUTS.path)\n" + } + ] + } + } + ] + }, + "taskRunTemplate": { + "serviceAccountName": "default" + }, + "timeouts": { + "pipeline": "1h0m0s" + } + } + }, + "internalParameters": { + "tekton-pipelines-feature-flags": { + "AwaitSidecarReadiness": true, + "Coschedule": "workspaces", + "DisableAffinityAssistant": false, + "DisableCredsInit": false, + "EnableAPIFields": "beta", + "EnableArtifacts": false, + "EnableCELInWhenExpression": false, + "EnableKeepPodOnCancel": false, + "EnableParamEnum": false, + "EnableProvenanceInStatus": true, + "EnableStepActions": true, + "EnableTektonOCIBundles": false, + "EnforceNonfalsifiability": "none", + "MaxResultSize": 4096, + "RequireGitSSHSecretKnownHosts": false, + "ResultExtractionMethod": "termination-message", + "RunningInEnvWithInjectedSidecars": true, + "ScopeWhenExpressionsToTask": false, + "SendCloudEventsForRuns": false, + "SetSecurityContext": false, + "VerificationNoMatchPolicy": "ignore" + } + }, + "resolvedDependencies": [ + {{range .URIDigest}} + { + "uri": "{{.URI}}", + "digest": { + "sha256": "{{.Digest}}" + } + } + {{end}} + ] + }, + "runDetails": { + "builder": { + "id": "https://tekton.dev/chains/v2" + }, + "byproducts": [ + { + "content": "eyJkaWdlc3QiOiJzaGEyNTY6NTg2Nzg5YWEwMzFmYWZjN2Q3OGE1MzkzY2RjNzcyZTBiNTUxMDdlYTU0YmI4YmNmM2YyY2RhYzZjNmRhNTFlZSIsInVyaSI6Imdjci5pby9mb28vaW1nMiJ9", + "mediaType": "application/json", + "name": "pipelineRunResults/output2-ARTIFACT_OUTPUTS" + }, + { + "content": "eyJkaWdlc3QiOiJzaGEyNTY6NTg2Nzg5YWEwMzFmYWZjN2Q3OGE1MzkzY2RjNzcyZTBiNTUxMDdlYTU0YmI4YmNmM2YyY2RhYzZjNmRhNTFlZSIsInVyaSI6Imdjci5pby9mb28vaW1nMiJ9", + "mediaType": "application/json", + "name": "taskRunResults/output2" + } + ], + "metadata": { + "invocationId": "{{.UID}}", + "startedOn": "{{.PipelineStartedOn}}", + "finishedOn": "{{.PipelineFinishedOn}}" + } + } + } +} \ No newline at end of file