From 0023878364ce6d069e2fe26e33ccd124c77bfc8d Mon Sep 17 00:00:00 2001 From: Renzo Rojas Date: Wed, 5 Jun 2024 10:15:13 -0400 Subject: [PATCH 1/3] Fix field names in generated json statement (#1129) To keep backwards compatibility with previous struct versions (e.g, github.com/in-toto/in-toto-golang/in_toto) we have to use protojson.Marshal function to transform the struct to its json representation so the tags defined in the intoto.Statement are follow and applied to the resulting string. (cherry picked from commit 3bf8cac208463f2a0fd33b76301d2e56fd530330) --- pkg/chains/signing.go | 20 ++++- pkg/chains/signing_test.go | 82 +++++++++++++++++++ .../slsa/v1/pipeline-output-image.json | 4 +- test/testdata/slsa/v1/task-output-image.json | 4 +- test/testdata/slsa/v2/task-output-image.json | 4 +- .../slsa/v2alpha3/pipeline-output-image.json | 4 +- .../slsa/v2alpha3/task-output-image.json | 4 +- .../slsa/v2alpha4/pipeline-output-image.json | 4 +- .../pipeline-with-object-type-hinting.json | 4 +- .../slsa/v2alpha4/task-output-image.json | 4 +- .../task-with-object-type-hinting.json | 4 +- 11 files changed, 119 insertions(+), 19 deletions(-) diff --git a/pkg/chains/signing.go b/pkg/chains/signing.go index 453bad9820..ce0bb380af 100644 --- a/pkg/chains/signing.go +++ b/pkg/chains/signing.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/hashicorp/go-multierror" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/formats" "github.com/tektoncd/chains/pkg/chains/objects" @@ -29,6 +30,7 @@ import ( "github.com/tektoncd/chains/pkg/chains/storage" "github.com/tektoncd/chains/pkg/config" versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + "google.golang.org/protobuf/encoding/protojson" "k8s.io/apimachinery/pkg/util/sets" "knative.dev/pkg/logging" ) @@ -169,7 +171,7 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject) } logger.Infof("Signing object with %s", signerType) - rawPayload, err := json.Marshal(payload) + rawPayload, err := getRawPayload(payload) if err != nil { logger.Warnf("Unable to marshal payload: %v", signerType, obj) continue @@ -248,3 +250,19 @@ func HandleRetry(ctx context.Context, obj objects.TektonObject, ps versioned.Int } return MarkFailed(ctx, obj, ps, annotations) } + +// getRawPayload returns the payload as a json string. If the given payload is a intoto.Statement type, protojson.Marshal +// is used to get the proper labels/field names in the resulting json. +func getRawPayload(payload interface{}) ([]byte, error) { + switch payloadObj := payload.(type) { + case intoto.Statement: + return protojson.Marshal(&payloadObj) + case *intoto.Statement: + if payloadObj == nil { + return json.Marshal(payload) + } + return protojson.Marshal(payloadObj) + default: + return json.Marshal(payload) + } +} diff --git a/pkg/chains/signing_test.go b/pkg/chains/signing_test.go index 0a8cba2f09..687953afe2 100644 --- a/pkg/chains/signing_test.go +++ b/pkg/chains/signing_test.go @@ -14,12 +14,16 @@ limitations under the License. package chains import ( + "bytes" "context" + "encoding/json" "errors" "fmt" "reflect" "testing" + "github.com/google/go-cmp/cmp" + intoto "github.com/in-toto/attestation/go/v1" "github.com/sigstore/rekor/pkg/generated/models" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/chains/signing" @@ -405,6 +409,84 @@ func TestSigningObjects(t *testing.T) { } } +func TestGetRawPayload(t *testing.T) { + tests := []struct { + name string + payload interface{} + expected string + }{ + { + name: "intoto.Statement object", + payload: intoto.Statement{ + Type: "type1", + PredicateType: "predicate-type1", + }, + expected: compactJSON(t, []byte(`{"_type":"type1","predicateType":"predicate-type1"}`)), + }, + { + name: "*intoto.Statement object", + payload: &intoto.Statement{ + Type: "type1", + PredicateType: "predicate-type1", + }, + expected: compactJSON(t, []byte(`{"_type":"type1","predicateType":"predicate-type1"}`)), + }, + { + name: "*intoto.Statement object - nil", + payload: (func() *intoto.Statement { return nil })(), + expected: "null", + }, + { + name: "other object - nil", + payload: nil, + expected: "null", + }, + { + name: "other object with value", + payload: struct { + Name string + ID int + Inner any + }{ + Name: "wrapper", + ID: 1, + Inner: struct { + InnerID int + Description string + IsArtifact bool + }{ + InnerID: 2, + Description: "some description", + IsArtifact: true, + }, + }, + expected: compactJSON(t, []byte(`{"Name":"wrapper","ID":1,"Inner": {"InnerID":2,"Description":"some description","IsArtifact":true}}`)), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := getRawPayload(test.payload) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + compactExpected := compactJSON(t, got) + if diff := cmp.Diff(test.expected, compactExpected); diff != "" { + t.Errorf("getRawPayload(), -want +got, diff = %s", diff) + } + }) + } +} + +func compactJSON(t *testing.T, jsonString []byte) string { + t.Helper() + dst := &bytes.Buffer{} + if err := json.Compact(dst, jsonString); err != nil { + t.Fatalf("error getting compact JSON: %v", err) + } + return dst.String() +} + func fakeAllBackends(backends []*mockBackend) map[string]storage.Backend { newBackends := map[string]storage.Backend{} for _, m := range backends { diff --git a/test/testdata/slsa/v1/pipeline-output-image.json b/test/testdata/slsa/v1/pipeline-output-image.json index 5a303c06c5..2c57b0090b 100644 --- a/test/testdata/slsa/v1/pipeline-output-image.json +++ b/test/testdata/slsa/v1/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v1/task-output-image.json b/test/testdata/slsa/v1/task-output-image.json index c816782574..6f50533549 100644 --- a/test/testdata/slsa/v1/task-output-image.json +++ b/test/testdata/slsa/v1/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2/task-output-image.json b/test/testdata/slsa/v2/task-output-image.json index aa4ebf9575..46bcf76a57 100644 --- a/test/testdata/slsa/v2/task-output-image.json +++ b/test/testdata/slsa/v2/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha3/pipeline-output-image.json b/test/testdata/slsa/v2alpha3/pipeline-output-image.json index 74ffc938d4..569c44a509 100644 --- a/test/testdata/slsa/v2alpha3/pipeline-output-image.json +++ b/test/testdata/slsa/v2alpha3/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha3/task-output-image.json b/test/testdata/slsa/v2alpha3/task-output-image.json index c8e8956401..bb092330d4 100644 --- a/test/testdata/slsa/v2alpha3/task-output-image.json +++ b/test/testdata/slsa/v2alpha3/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/pipeline-output-image.json b/test/testdata/slsa/v2alpha4/pipeline-output-image.json index 03a2cd2639..d7ae8848de 100644 --- a/test/testdata/slsa/v2alpha4/pipeline-output-image.json +++ b/test/testdata/slsa/v2alpha4/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json index 3dbc87d1cf..704f896f34 100644 --- a/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json +++ b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/img1", diff --git a/test/testdata/slsa/v2alpha4/task-output-image.json b/test/testdata/slsa/v2alpha4/task-output-image.json index 440ea595f3..d16164077c 100644 --- a/test/testdata/slsa/v2alpha4/task-output-image.json +++ b/test/testdata/slsa/v2alpha4/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json index 9f835644a3..a8c156330d 100644 --- a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json +++ b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/img2", From 069573e82ce10cdcd2bbf5e3a5269cceb6637a0e Mon Sep 17 00:00:00 2001 From: Renzo Rojas Date: Wed, 5 Jun 2024 11:29:13 -0400 Subject: [PATCH 2/3] Fix no repeated subjects for v2alpha4 pipelineruns (#1130) Change the pipelinerun v2alpha4 method that calculates the subjects to use `artifact.AppendSubjects` instead of `append` to avoid duplicated subjects. (cherry picked from commit badc7340a7c749979af6e02e545d870b554830af) --- .../pipeline-with-repeated-results.yaml | 64 ++++ .../testdata/slsa-v2alpha4/pipelinerun2.json | 328 ++++++++++++++++++ .../slsa/testdata/slsa-v2alpha4/taskrun3.json | 175 ++++++++++ .../slsa/testdata/slsa-v2alpha4/taskrun4.json | 133 +++++++ .../internal/pipelinerun/pipelinerun.go | 3 +- .../internal/pipelinerun/pipelinerun_test.go | 164 ++++++++- test/examples_test.go | 24 ++ .../pipeline-with-repeated-results.json | 181 ++++++++++ 8 files changed, 1056 insertions(+), 16 deletions(-) create mode 100644 examples/v2alpha4/pipeline-with-repeated-results.yaml create mode 100644 pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun2.json create mode 100644 pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun3.json create mode 100644 pkg/chains/formats/slsa/testdata/slsa-v2alpha4/taskrun4.json create mode 100644 test/testdata/slsa/v2alpha4/pipeline-with-repeated-results.json 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 From d12cd2101c626ac858c86a8b56131a41e19ccb63 Mon Sep 17 00:00:00 2001 From: Renzo Rojas Date: Sat, 8 Jun 2024 15:03:14 -0400 Subject: [PATCH 3/3] Calculate subjects per formatter (#1132) * Calculate subjects per formatter * Tests for new retrieve full uris in grafeas (cherry picked from commit 750a98e0e0cd20b1c9499bc4bf67e2b1d5f696ac) --- pkg/chains/formats/format.go | 1 + pkg/chains/formats/simple/simple.go | 5 + pkg/chains/formats/slsa/v1/intotoite6.go | 10 + pkg/chains/formats/slsa/v2alpha3/slsav2.go | 10 + .../internal/pipelinerun/pipelinerun.go | 5 +- pkg/chains/formats/slsa/v2alpha4/slsav2.go | 23 ++ pkg/chains/storage/grafeas/grafeas.go | 20 +- pkg/chains/storage/grafeas/grafeas_test.go | 291 +++++++++++++++++- 8 files changed, 353 insertions(+), 12 deletions(-) diff --git a/pkg/chains/formats/format.go b/pkg/chains/formats/format.go index 797240f999..4caabbc88b 100644 --- a/pkg/chains/formats/format.go +++ b/pkg/chains/formats/format.go @@ -25,6 +25,7 @@ type Payloader interface { CreatePayload(ctx context.Context, obj interface{}) (interface{}, error) Type() config.PayloadType Wrap() bool + RetrieveAllArtifactURIs(ctx context.Context, obj interface{}) ([]string, error) } const ( diff --git a/pkg/chains/formats/simple/simple.go b/pkg/chains/formats/simple/simple.go index 10c464f96a..1089574774 100644 --- a/pkg/chains/formats/simple/simple.go +++ b/pkg/chains/formats/simple/simple.go @@ -69,3 +69,8 @@ func (i SimpleContainerImage) ImageName() string { func (i *SimpleSigning) Type() config.PayloadType { return formats.PayloadTypeSimpleSigning } + +// RetrieveAllArtifactURIs returns always an error, feature not available for simplesigning formatter. +func (i *SimpleSigning) RetrieveAllArtifactURIs(_ context.Context, _ interface{}) ([]string, error) { + return nil, fmt.Errorf("RetrieveAllArtifactURIs not supported for simeplesining formatter") +} diff --git a/pkg/chains/formats/slsa/v1/intotoite6.go b/pkg/chains/formats/slsa/v1/intotoite6.go index f4c3bc8d8f..d6a9c8abf9 100644 --- a/pkg/chains/formats/slsa/v1/intotoite6.go +++ b/pkg/chains/formats/slsa/v1/intotoite6.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/tektoncd/chains/pkg/chains/formats" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/pipelinerun" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v1/taskrun" @@ -94,3 +95,12 @@ func (i *InTotoIte6) CreatePayload(ctx context.Context, obj interface{}) (interf func (i *InTotoIte6) Type() config.PayloadType { return formats.PayloadTypeSlsav1 } + +// RetrieveAllArtifactURIs returns the full URI of all artifacts detected as subjects. +func (i *InTotoIte6) RetrieveAllArtifactURIs(ctx context.Context, obj interface{}) ([]string, error) { + tkObj, ok := obj.(objects.TektonObject) + if !ok { + return nil, fmt.Errorf("intoto does not support type") + } + return extract.RetrieveAllArtifactURIs(ctx, tkObj, i.slsaConfig.DeepInspectionEnabled), nil +} diff --git a/pkg/chains/formats/slsa/v2alpha3/slsav2.go b/pkg/chains/formats/slsa/v2alpha3/slsav2.go index d86dd01802..3b8c241462 100644 --- a/pkg/chains/formats/slsa/v2alpha3/slsav2.go +++ b/pkg/chains/formats/slsa/v2alpha3/slsav2.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/tektoncd/chains/pkg/chains/formats" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha3/internal/taskrun" @@ -68,3 +69,12 @@ func (s *Slsa) CreatePayload(ctx context.Context, obj interface{}) (interface{}, func (s *Slsa) Type() config.PayloadType { return formats.PayloadTypeSlsav2alpha3 } + +// RetrieveAllArtifactURIs returns the full URI of all artifacts detected as subjects. +func (s *Slsa) RetrieveAllArtifactURIs(ctx context.Context, obj interface{}) ([]string, error) { + tkObj, ok := obj.(objects.TektonObject) + if !ok { + return nil, fmt.Errorf("intoto does not support type") + } + return extract.RetrieveAllArtifactURIs(ctx, tkObj, s.slsaConfig.DeepInspectionEnabled), nil +} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go index 8864923a74..4ccf941a89 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go @@ -47,7 +47,7 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, return nil, err } - sub := subjectDigests(ctx, pro, slsaconfig) + sub := SubjectDigests(ctx, pro, slsaconfig) return provenance.GetSLSA1Statement(pro, sub, &bd, bp, slsaconfig) } @@ -74,7 +74,8 @@ func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaCon return byProd, nil } -func subjectDigests(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) []*intoto.ResourceDescriptor { +// 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()) if !slsaconfig.DeepInspectionEnabled { diff --git a/pkg/chains/formats/slsa/v2alpha4/slsav2.go b/pkg/chains/formats/slsa/v2alpha4/slsav2.go index d756cac477..c5e6cb5273 100644 --- a/pkg/chains/formats/slsa/v2alpha4/slsav2.go +++ b/pkg/chains/formats/slsa/v2alpha4/slsav2.go @@ -20,6 +20,7 @@ import ( "context" "fmt" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/chains/formats" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun" @@ -74,3 +75,25 @@ func (s *Slsa) CreatePayload(ctx context.Context, obj interface{}) (interface{}, func (s *Slsa) Type() config.PayloadType { return payloadTypeSlsav2alpha4 } + +// RetrieveAllArtifactURIs returns the full URI of all artifacts detected as subjects. +func (s *Slsa) RetrieveAllArtifactURIs(ctx context.Context, obj interface{}) ([]string, error) { + var subjects []*intoto.ResourceDescriptor + var fullURIs []string + + switch v := obj.(type) { + case *objects.TaskRunObjectV1: + subjects = taskrun.SubjectDigests(ctx, v) + case *objects.PipelineRunObjectV1: + subjects = pipelinerun.SubjectDigests(ctx, v, s.slsaConfig) + default: + return nil, fmt.Errorf("intoto does not support type: %s", v) + } + + for _, s := range subjects { + for algo, digest := range s.Digest { + fullURIs = append(fullURIs, fmt.Sprintf("%s@%s:%s", s.Name, algo, digest)) + } + } + return fullURIs, nil +} diff --git a/pkg/chains/storage/grafeas/grafeas.go b/pkg/chains/storage/grafeas/grafeas.go index 7f04f3e86e..ef86b87acc 100644 --- a/pkg/chains/storage/grafeas/grafeas.go +++ b/pkg/chains/storage/grafeas/grafeas.go @@ -253,7 +253,7 @@ func (b *Backend) createOccurrence(ctx context.Context, obj objects.TektonObject } // create Occurrence_Build for TaskRun - allURIs := extract.RetrieveAllArtifactURIs(ctx, obj, b.cfg.Artifacts.PipelineRuns.DeepInspectionEnabled) + allURIs := b.getAllArtifactURIs(ctx, opts.PayloadFormat, obj) for _, uri := range allURIs { occ, err := b.createBuildOccurrence(ctx, obj, payload, signature, uri) if err != nil { @@ -264,6 +264,22 @@ func (b *Backend) createOccurrence(ctx context.Context, obj objects.TektonObject return occs, nil } +func (b *Backend) getAllArtifactURIs(ctx context.Context, payloadFormat config.PayloadType, obj objects.TektonObject) []string { + logger := logging.FromContext(ctx) + payloader, err := formats.GetPayloader(payloadFormat, b.cfg) + if err != nil { + logger.Infof("couldn't get payloader for %v format, will use extract.RetrieveAllArtifactURIs method instead", payloadFormat) + return extract.RetrieveAllArtifactURIs(ctx, obj, b.cfg.Artifacts.PipelineRuns.DeepInspectionEnabled) + } + + if uris, err := payloader.RetrieveAllArtifactURIs(ctx, obj); err == nil { + return uris + } + + logger.Infof("couldn't get URIs from payloader %v, will use extract.RetrieveAllArtifactURIs method instead", payloadFormat) + return extract.RetrieveAllArtifactURIs(ctx, obj, b.cfg.Artifacts.PipelineRuns.DeepInspectionEnabled) +} + func (b *Backend) createAttestationOccurrence(ctx context.Context, payload []byte, signature string, uri string) (*pb.Occurrence, error) { occurrenceDetails := &pb.Occurrence_Attestation{ Attestation: &pb.AttestationOccurrence{ @@ -364,7 +380,7 @@ func (b *Backend) getBuildNotePath(obj objects.TektonObject) string { func (b *Backend) getAllOccurrences(ctx context.Context, obj objects.TektonObject, opts config.StorageOpts) ([]*pb.Occurrence, error) { result := []*pb.Occurrence{} // step 1: get all resource URIs created under the taskrun - uriFilters := extract.RetrieveAllArtifactURIs(ctx, obj, b.cfg.Artifacts.PipelineRuns.DeepInspectionEnabled) + uriFilters := b.getAllArtifactURIs(ctx, opts.PayloadFormat, obj) // step 2: find all build occurrences if _, ok := formats.IntotoAttestationSet[opts.PayloadFormat]; ok { diff --git a/pkg/chains/storage/grafeas/grafeas_test.go b/pkg/chains/storage/grafeas/grafeas_test.go index 57809aee31..398c7a8eee 100644 --- a/pkg/chains/storage/grafeas/grafeas_test.go +++ b/pkg/chains/storage/grafeas/grafeas_test.go @@ -21,14 +21,15 @@ import ( "sort" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" intoto "github.com/in-toto/attestation/go/v1" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/tektoncd/chains/pkg/chains/formats" - "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" "github.com/tektoncd/chains/pkg/chains/objects" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -46,6 +47,8 @@ import ( "knative.dev/pkg/logging" logtesting "knative.dev/pkg/logging/testing" rtesting "knative.dev/pkg/reconciler/testing" + + _ "github.com/tektoncd/chains/pkg/chains/formats/all" ) const ( @@ -159,6 +162,125 @@ var ( }, } + // TaskRun with step results. + taskRunWithStepResults = &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "taskrun-with-steps", + UID: types.UID("uid-pipeline"), + Labels: map[string]string{ + "tekton.dev/pipelineTask": "taskrun-with-steps", + }, + }, + Status: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)}, + Results: []v1.TaskRunResult{ + { + Name: "art1-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img1", + "digest": "sha256:52e18b100a8da6e191a1955913ba127b75a8b38146cd9b0f573ec1d8e8ecd135", + "isBuildArtifact": "true", + }), + }, + { + Name: "art2-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img2", + "digest": "sha256:2996854378975c2f8011ddf0526975d1aaf1790b404da7aad4bf25293055bc8b", + "isBuildArtifact": "false", + }), + }, + {Name: "IMAGE_URL", Value: *v1.NewStructuredValues("gcr.io/img3")}, + {Name: "IMAGE_DIGEST", Value: *v1.NewStructuredValues("sha256:ef334b5d9704da9b325ed6d4e3e5327863847e2da6d43f81831fd1decbdb2213")}, + }, + Steps: []v1.StepState{ + { + Name: "step1", + Results: []v1.TaskRunStepResult{ + { + Name: "art3-repeated-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img1", + "digest": "sha256:52e18b100a8da6e191a1955913ba127b75a8b38146cd9b0f573ec1d8e8ecd135", + "isBuildArtifact": "true", + }), + }, + { + Name: "art4-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img4", + "digest": "sha256:910700c5ace59f70588c4e2a38ed131146c9f65c94379dfe12376075fc2f338f", + "isBuildArtifact": "true", + }), + }, + { + Name: "art5-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img5", + "digest": "sha256:7492314e32aa75ff1f2cfea35b7dda85d8831929d076aab52420c3400c8c65d8", + }), + }, + }, + }, + }, + }, + }, + } + + taskRunWithStepResultsProvenancev2alpha4 = intoto.Statement{ + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/img1", + Digest: common.DigestSet{ + "sha256": "52e18b100a8da6e191a1955913ba127b75a8b38146cd9b0f573ec1d8e8ecd135", + }, + }, + { + Name: "gcr.io/img4", + Digest: common.DigestSet{ + "sha256": "910700c5ace59f70588c4e2a38ed131146c9f65c94379dfe12376075fc2f338f", + }, + }, + { + Name: "gcr.io/img3", + Digest: common.DigestSet{ + "sha256": "ef334b5d9704da9b325ed6d4e3e5327863847e2da6d43f81831fd1decbdb2213", + }, + }, + }, + } + + pipelineRunWithStepResultsProvenancev2alpha4 = intoto.Statement{ + Subject: []*intoto.ResourceDescriptor{ + { + Name: "gcr.io/img1", + Digest: common.DigestSet{ + "sha256": "52e18b100a8da6e191a1955913ba127b75a8b38146cd9b0f573ec1d8e8ecd135", + }, + }, + { + Name: "gcr.io/img0", + Digest: common.DigestSet{ + "sha256": "e82e58757ff417de62d35689a6ff58f4a3975c331595ceda979d900d4f5de465", + }, + }, + { + Name: "gcr.io/img4", + Digest: common.DigestSet{ + "sha256": "910700c5ace59f70588c4e2a38ed131146c9f65c94379dfe12376075fc2f338f", + }, + }, + { + Name: "gcr.io/img3", + Digest: common.DigestSet{ + "sha256": "ef334b5d9704da9b325ed6d4e3e5327863847e2da6d43f81831fd1decbdb2213", + }, + }, + }, + } + // ci pipelinerun provenance ciPipelineRunPredicate = slsa.ProvenancePredicate{ Materials: cloneTaskRunPredicate.Materials, @@ -173,10 +295,11 @@ type args struct { } type testConfig struct { - name string - args args - wantOccurrences []*pb.Occurrence - wantErr bool + name string + args args + withDeepInspection bool + wantOccurrences []*pb.Occurrence + wantErr bool } // This function is to test the implementation of the fake server's ListOccurrences function. @@ -327,6 +450,29 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { wantOccurrences: nil, wantErr: true, }, + { + name: "slsav1/v2alpha4 taskrun format, no error", + args: args{ + runObject: &objects.TaskRunObjectV1{ + TaskRun: taskRunWithStepResults, + }, + payload: getRawFromProto(t, &taskRunWithStepResultsProvenancev2alpha4), + signature: "bar", + opts: config.StorageOpts{PayloadFormat: formats.PayloadTypeSlsav2alpha4}, + }, + wantOccurrences: getOccFromProvenance(t, &taskRunWithStepResultsProvenancev2alpha4, "taskrun"), + }, + { + name: "slsav1/v2alpha4 pipelinerun format, no error", + withDeepInspection: true, + args: args{ + runObject: getPipelineRunWithSteps(t), + payload: getRawFromProto(t, &pipelineRunWithStepResultsProvenancev2alpha4), + signature: "bar", + opts: config.StorageOpts{PayloadFormat: formats.PayloadTypeSlsav2alpha4}, + }, + wantOccurrences: getOccFromProvenance(t, &pipelineRunWithStepResultsProvenancev2alpha4, "pipelinerun"), + }, } // setup connection @@ -354,7 +500,7 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { }, Artifacts: config.ArtifactConfigs{ PipelineRuns: config.Artifact{ - DeepInspectionEnabled: false, + DeepInspectionEnabled: test.withDeepInspection, }, }, }, @@ -388,6 +534,112 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { } } +func getPipelineRunWithSteps(t *testing.T) *objects.PipelineRunObjectV1 { + t.Helper() + pr := &v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "pipeline-with-steps", + UID: types.UID("uid-pipeline"), + }, + Status: v1.PipelineRunStatus{ + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + Results: []v1.PipelineRunResult{ + { + Name: "art1-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img1", + "digest": "sha256:52e18b100a8da6e191a1955913ba127b75a8b38146cd9b0f573ec1d8e8ecd135", + "isBuildArtifact": "true", + }), + }, + { + Name: "art0-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img0", + "digest": "sha256:e82e58757ff417de62d35689a6ff58f4a3975c331595ceda979d900d4f5de465", + "isBuildArtifact": "true", + }), + }, + { + Name: "art0-ARTIFACT_OUTPUTS", + Value: *v1.NewObject(map[string]string{ + "uri": "gcr.io/img7", + "digest": "sha256:dc9c5a6b73b21dbc5c49254907fafead52ce59604e073c200c2c38002a9102e7", + "isBuildArtifact": "false", + }), + }, + }, + PipelineSpec: &v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: "taskrun-with-steps", + }, + }, + }, + }, + }, + } + + prObj := &objects.PipelineRunObjectV1{ + PipelineRun: pr, + } + + prObj.AppendTaskRun(taskRunWithStepResults) + + return prObj +} + +func getOccFromProvenance(t *testing.T, provenance *intoto.Statement, noteType string) []*pb.Occurrence { + t.Helper() + var intotoSubjects []*pb.Subject + var occurs []*pb.Occurrence + subjects := provenance.Subject + noteName := fmt.Sprintf("projects/%s/notes/%s-%s-intoto", ProjectID, NoteID, noteType) + + for _, subject := range subjects { + intotoSubjects = append(intotoSubjects, &pb.Subject{ + Name: subject.Name, + Digest: subject.Digest, + }) + } + + for _, subject := range subjects { + identifier := fmt.Sprintf("%v@sha256:%v", subject.Name, subject.Digest["sha256"]) + occ := &pb.Occurrence{ + Name: identifier, + ResourceUri: identifier, + NoteName: noteName, + Details: &pb.Occurrence_Build{ + Build: &pb.BuildOccurrence{ + IntotoStatement: &pb.InTotoStatement{ + Subject: intotoSubjects, + Predicate: &pb.InTotoStatement_SlsaProvenanceZeroTwo{ + SlsaProvenanceZeroTwo: &pb.SlsaProvenanceZeroTwo{ + Builder: &pb.SlsaProvenanceZeroTwo_SlsaBuilder{}, + Invocation: &pb.SlsaProvenanceZeroTwo_SlsaInvocation{ + ConfigSource: &pb.SlsaProvenanceZeroTwo_SlsaConfigSource{}, + }, + }, + }, + }, + }, + }, + Envelope: &pb.Envelope{ + Payload: getRawFromProto(t, provenance), + PayloadType: "application/vnd.in-toto+json", + Signatures: []*pb.EnvelopeSignature{ + {Sig: []byte("bar")}, + }, + }, + } + + occurs = append(occurs, occ) + } + + return occurs +} + // test attestation storage and retrieval func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConfig, backend Backend) { t.Helper() @@ -407,7 +659,7 @@ func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConf expectSignature[test.args.opts.FullKey] = []string{test.args.signature} } if _, ok := formats.IntotoAttestationSet[test.args.opts.PayloadFormat]; ok { - allURIs := extract.RetrieveAllArtifactURIs(ctx, test.args.runObject, false) + allURIs := retrieveAllArtifactURIs(ctx, t, test, backend) for _, u := range allURIs { expectSignature[u] = []string{test.args.signature} } @@ -429,7 +681,7 @@ func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConf expectPayload[test.args.opts.FullKey] = string(test.args.payload) } if _, ok := formats.IntotoAttestationSet[test.args.opts.PayloadFormat]; ok { - allURIs := extract.RetrieveAllArtifactURIs(ctx, test.args.runObject, false) + allURIs := retrieveAllArtifactURIs(ctx, t, test, backend) for _, u := range allURIs { expectPayload[u] = string(test.args.payload) } @@ -445,6 +697,20 @@ func testStoreAndRetrieveHelper(ctx context.Context, t *testing.T, test testConf } } +func retrieveAllArtifactURIs(ctx context.Context, t *testing.T, test testConfig, backend Backend) []string { + t.Helper() + payloader, err := formats.GetPayloader(test.args.opts.PayloadFormat, backend.cfg) + if err != nil { + t.Fatalf("error getting payloader: %v", err) + } + allURIs, err := payloader.RetrieveAllArtifactURIs(ctx, test.args.runObject) + if err != nil { + t.Fatalf("error getting artifacts URIs: %v", err) + } + + return allURIs +} + // ------------------ occurrences for taskruns and pipelineruns -------------- // BUILD Occurrence for the build taskrun that stores the slsa provenance func getTaskRunBuildOcc(t *testing.T, identifier string) *pb.Occurrence { @@ -570,6 +836,15 @@ func getRawPayload(t *testing.T, in interface{}) []byte { return rawPayload } +func getRawFromProto(t *testing.T, in *intoto.Statement) []byte { + t.Helper() + raw, err := protojson.Marshal(in) + if err != nil { + t.Errorf("Unable to marshal the proto provenance: %v", in) + } + return raw +} + // set up the connection between grafeas server and client // and return the client object to the caller func setupConnection() (*grpc.ClientConn, pb.GrafeasClient, error) { //nolint:ireturn