diff --git a/pkg/chains/formats/slsa/extract/extract.go b/pkg/chains/formats/slsa/extract/extract.go index e01fefcb1e..4b167846ef 100644 --- a/pkg/chains/formats/slsa/extract/extract.go +++ b/pkg/chains/formats/slsa/extract/extract.go @@ -34,12 +34,67 @@ import ( // SubjectDigests returns software artifacts produced from the TaskRun/PipelineRun object // in the form of standard subject field of intoto statement. -// The type hinting fields expected in results help identify the generated software artifacts. +// The type hinting fields expected in TaskRun results help identify the generated software artifacts in a TaskRun/PipelineRun. // Valid type hinting fields must: // - have suffix `IMAGE_URL` & `IMAGE_DIGEST` or `ARTIFACT_URI` & `ARTIFACT_DIGEST` pair. // - the `*_DIGEST` field must be in the format of ":" where the algorithm must be "sha256" and actual sha must be valid per https://github.com/opencontainers/image-spec/blob/main/descriptor.md#sha-256. // - the `*_URL` or `*_URI` fields cannot be empty. func SubjectDigests(ctx context.Context, obj objects.TektonObject) []intoto.Subject { + var subjects []intoto.Subject + + switch obj.GetObject().(type) { + case *v1beta1.PipelineRun: + subjects = subjectsFromPipelineRun(ctx, obj) + case *v1beta1.TaskRun: + subjects = subjectsFromTaskrun(ctx, obj) + } + + sort.Slice(subjects, func(i, j int) bool { + return subjects[i].Name <= subjects[j].Name + }) + + return subjects +} + +func subjectsFromPipelineRun(ctx context.Context, obj objects.TektonObject) []intoto.Subject { + // create a map to represent the subjects. Key is the Subject.Name; Value is the Subject.DigestSet + subjects := make(map[string](map[string]string)) + + pr := obj.(*objects.PipelineRunObject) + + for _, tr := range pr.TaskRuns { + // collect subjects from individual TaskRuns + trSubjects := subjectsFromTaskrun(ctx, objects.NewTaskRunObject(tr)) + + for _, s := range trSubjects { + if v, ok := subjects[s.Name]; ok { + mergeMaps(subjects[s.Name], v) + } else { + subjects[s.Name] = make(map[string]string) + subjects[s.Name] = s.Digest + } + } + } + + // convert to subject slice + var results []intoto.Subject + for k, v := range subjects { + results = append(results, intoto.Subject{ + Name: k, + Digest: v, + }) + } + + return results +} + +func mergeMaps(m1 map[string]string, m2 map[string]string) { + for k, v := range m2 { + m1[k] = v + } +} + +func subjectsFromTaskrun(ctx context.Context, obj objects.TektonObject) []intoto.Subject { logger := logging.FromContext(ctx) var subjects []intoto.Subject @@ -121,9 +176,7 @@ func SubjectDigests(ctx context.Context, obj objects.TektonObject) []intoto.Subj }) } } - sort.Slice(subjects, func(i, j int) bool { - return subjects[i].Name <= subjects[j].Name - }) + return subjects } diff --git a/pkg/chains/formats/slsa/extract/extract_test.go b/pkg/chains/formats/slsa/extract/extract_test.go index 0b5a576a5e..9b69ae92ff 100644 --- a/pkg/chains/formats/slsa/extract/extract_test.go +++ b/pkg/chains/formats/slsa/extract/extract_test.go @@ -144,23 +144,22 @@ func createTaskRunObjectWithResults(results map[string]string) objects.TektonObj } func createPipelineRunObjectWithResults(results map[string]string) objects.TektonObject { - prResults := []v1beta1.PipelineRunResult{} + pro := objects.NewPipelineRunObject(nil) prefix := 0 for url, digest := range results { - prResults = append(prResults, - v1beta1.PipelineRunResult{Name: fmt.Sprintf("%v_IMAGE_DIGEST", prefix), Value: *v1beta1.NewStructuredValues(digest)}, - v1beta1.PipelineRunResult{Name: fmt.Sprintf("%v_IMAGE_URL", prefix), Value: *v1beta1.NewStructuredValues(url)}, - ) + tr := &v1beta1.TaskRun{ + Status: v1beta1.TaskRunStatus{ + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + TaskRunResults: []v1beta1.TaskRunResult{ + {Name: fmt.Sprintf("%v_IMAGE_DIGEST", prefix), Value: *v1beta1.NewStructuredValues(digest)}, + {Name: fmt.Sprintf("%v_IMAGE_URL", prefix), Value: *v1beta1.NewStructuredValues(url)}, + }, + }, + }, + } + pro.AppendTaskRun(tr) prefix++ } - return objects.NewPipelineRunObject( - &v1beta1.PipelineRun{ - Status: v1beta1.PipelineRunStatus{ - PipelineRunStatusFields: v1beta1.PipelineRunStatusFields{ - PipelineResults: prResults, - }, - }, - }, - ) + return pro } diff --git a/pkg/chains/formats/slsa/testdata/taskrun-multiple-subjects.json b/pkg/chains/formats/slsa/testdata/taskrun-multiple-subjects.json index d7716372c8..32ddbc30e5 100644 --- a/pkg/chains/formats/slsa/testdata/taskrun-multiple-subjects.json +++ b/pkg/chains/formats/slsa/testdata/taskrun-multiple-subjects.json @@ -28,7 +28,7 @@ "taskResults": [ { "name": "IMAGES", - "value": "gcr.io/myimage@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367" + "value": "gcr.io/myimage1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage2@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367" } ], "taskSpec": { diff --git a/pkg/chains/formats/slsa/testdata/v2alpha2/taskrun-multiple-subjects.json b/pkg/chains/formats/slsa/testdata/v2alpha2/taskrun-multiple-subjects.json index d7716372c8..32ddbc30e5 100644 --- a/pkg/chains/formats/slsa/testdata/v2alpha2/taskrun-multiple-subjects.json +++ b/pkg/chains/formats/slsa/testdata/v2alpha2/taskrun-multiple-subjects.json @@ -28,7 +28,7 @@ "taskResults": [ { "name": "IMAGES", - "value": "gcr.io/myimage@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367" + "value": "gcr.io/myimage1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage2@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367" } ], "taskSpec": { diff --git a/pkg/chains/formats/slsa/v1/intotoite6_test.go b/pkg/chains/formats/slsa/v1/intotoite6_test.go index 8ae993dcd5..eb6b03e287 100644 --- a/pkg/chains/formats/slsa/v1/intotoite6_test.go +++ b/pkg/chains/formats/slsa/v1/intotoite6_test.go @@ -163,7 +163,7 @@ func TestPipelineRunCreatePayload(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "test.io/test/image", + Name: "gcr.io/my/image", Digest: common.DigestSet{ "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, @@ -387,7 +387,7 @@ func TestPipelineRunCreatePayloadChildRefs(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "test.io/test/image", + Name: "gcr.io/my/image", Digest: common.DigestSet{ "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, @@ -681,12 +681,12 @@ func TestMultipleSubjects(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage1", Digest: common.DigestSet{ "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, }, { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage2", Digest: common.DigestSet{ "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go index 1f1ee34c6c..e5c8f87dc9 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go @@ -474,7 +474,7 @@ var ignore = []cmp.Option{cmpopts.IgnoreUnexported(name.Registry{}, name.Reposit func TestSubjectDigests(t *testing.T) { wantSubjects := []intoto.Subject{ { - Name: "test.io/test/image", + Name: "gcr.io/my/image", Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, }, } diff --git a/pkg/chains/formats/slsa/v2alpha1/slsav2_test.go b/pkg/chains/formats/slsa/v2alpha1/slsav2_test.go index ad8090f678..c06e126ca7 100644 --- a/pkg/chains/formats/slsa/v2alpha1/slsav2_test.go +++ b/pkg/chains/formats/slsa/v2alpha1/slsav2_test.go @@ -292,12 +292,12 @@ func TestMultipleSubjects(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage1", Digest: common.DigestSet{ "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, }, { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage2", Digest: common.DigestSet{ "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, @@ -352,7 +352,7 @@ func TestMultipleSubjects(t *testing.T) { Name: "IMAGES", Value: v1beta1.ParamValue{ Type: "string", - StringVal: "gcr.io/myimage@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", + StringVal: "gcr.io/myimage1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage2@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, }, }, diff --git a/pkg/chains/formats/slsa/v2alpha2/internal/pipelinerun/pipelinerun_test.go b/pkg/chains/formats/slsa/v2alpha2/internal/pipelinerun/pipelinerun_test.go index e68db3a49d..6bd1d29561 100644 --- a/pkg/chains/formats/slsa/v2alpha2/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha2/internal/pipelinerun/pipelinerun_test.go @@ -255,7 +255,7 @@ func TestGenerateAttestation(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "test.io/test/image", + Name: "gcr.io/my/image", Digest: common.DigestSet{ "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, diff --git a/pkg/chains/formats/slsa/v2alpha2/slsav2_test.go b/pkg/chains/formats/slsa/v2alpha2/slsav2_test.go index 97eef48e33..dad531e02e 100644 --- a/pkg/chains/formats/slsa/v2alpha2/slsav2_test.go +++ b/pkg/chains/formats/slsa/v2alpha2/slsav2_test.go @@ -291,7 +291,7 @@ func TestMultipleSubjects(t *testing.T) { resultValue := v1beta1.ParamValue{ Type: "string", - StringVal: "gcr.io/myimage@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", + StringVal: "gcr.io/myimage1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6,gcr.io/myimage2@sha256:daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", } resultBytes, err := json.Marshal(resultValue) if err != nil { @@ -308,12 +308,12 @@ func TestMultipleSubjects(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage1", Digest: common.DigestSet{ "sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", }, }, { - Name: "gcr.io/myimage", + Name: "gcr.io/myimage2", Digest: common.DigestSet{ "sha256": "daa1a56e13c85cf164e7d9e595006649e3a04c47fe4a8261320e18a0bf3b0367", }, @@ -395,7 +395,7 @@ func TestPipelineRunCreatePayload1(t *testing.T) { PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { - Name: "test.io/test/image", + Name: "gcr.io/my/image", Digest: common.DigestSet{ "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", }, diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index 46e5ecd69a..6197ce43a5 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -135,8 +135,8 @@ func (tro *TaskRunObject) GetPullSecrets() []string { type PipelineRunObject struct { // The base PipelineRun *v1beta1.PipelineRun - // taskRuns that were apart of this PipelineRun - taskRuns []*v1beta1.TaskRun + // TaskRuns that were apart of this PipelineRun + TaskRuns []*v1beta1.TaskRun } var _ TektonObject = &PipelineRunObject{} @@ -194,12 +194,12 @@ func (pro *PipelineRunObject) IsSuccessful() bool { // Append TaskRuns to this PipelineRun func (pro *PipelineRunObject) AppendTaskRun(tr *v1beta1.TaskRun) { - pro.taskRuns = append(pro.taskRuns, tr) + pro.TaskRuns = append(pro.TaskRuns, tr) } // Get the associated TaskRun via the Task name func (pro *PipelineRunObject) GetTaskRunFromTask(taskName string) *v1beta1.TaskRun { - for _, tr := range pro.taskRuns { + for _, tr := range pro.TaskRuns { val, ok := tr.Labels[PipelineTaskLabel] if ok && val == taskName { return tr diff --git a/pkg/chains/storage/grafeas/grafeas_test.go b/pkg/chains/storage/grafeas/grafeas_test.go index 0c4d28e5a1..1bb221bde8 100644 --- a/pkg/chains/storage/grafeas/grafeas_test.go +++ b/pkg/chains/storage/grafeas/grafeas_test.go @@ -145,20 +145,6 @@ var ( Name: "ci-pipeline", UID: types.UID("uid-pipeline"), }, - Status: v1beta1.PipelineRunStatus{ - PipelineRunStatusFields: v1beta1.PipelineRunStatusFields{ - PipelineResults: []v1beta1.PipelineRunResult{ - // the results from task 1 - clone - {Name: "CHAINS-GIT_COMMIT", Value: *v1beta1.NewStructuredValues(commitSHA)}, - {Name: "CHAINS-GIT_URL", Value: *v1beta1.NewStructuredValues(repoURL)}, - // the results from task 2 - build - {Name: "IMAGE_DIGEST", Value: *v1beta1.NewStructuredValues("sha256:" + artifactDigest1)}, - {Name: "IMAGE_URL", Value: *v1beta1.NewStructuredValues(artifactURL1)}, - {Name: "x_ARTIFACT_DIGEST", Value: *v1beta1.NewStructuredValues("sha256:" + artifactDigest2)}, - {Name: "x_ARTIFACT_URI", Value: *v1beta1.NewStructuredValues(artifactURL2)}, - }, - }, - }, } // ci pipelinerun provenance @@ -302,6 +288,7 @@ func TestGrafeasBackend_StoreAndRetrieve(t *testing.T) { args: args{ runObject: &objects.PipelineRunObject{ PipelineRun: ciPipeline, + TaskRuns: []*v1beta1.TaskRun{cloneTaskRun, buildTaskRun}, }, payload: getRawPayload(t, ciPipelineRunProvenance), signature: "ci pipelinerun signature",