Skip to content

Commit

Permalink
Issue 642: predicate.materials needs to record more information in Pi…
Browse files Browse the repository at this point in the history
…pelineRun

This PR addresses issue tektoncd#642 by adding steps and sidecars image uri and digest information to predicate.materials for a PipelineRun. In addition, the configSource information for the remote tasks and the pipeline are also added.
  • Loading branch information
chitrangpatel committed Jan 17, 2023
1 parent 1b142b5 commit 96c74ec
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 24 deletions.
35 changes: 33 additions & 2 deletions pkg/chains/formats/intotoite6/intotoite6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ func TestPipelineRunCreatePayload(t *testing.T) {
Reproducible: false,
},
Materials: []slsa.ProvenanceMaterial{
{
URI: "docker-pullable://gcr.io/test1/test1",
Digest: slsa.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"},
},
{URI: "github.com/catalog", Digest: slsa.DigestSet{"sha1": "x123"}},
{
URI: "docker-pullable://gcr.io/test2/test2",
Digest: slsa.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"},
},
{
URI: "docker-pullable://gcr.io/test3/test3",
Digest: slsa.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"},
},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "ab123"}},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "28b123"}},
{URI: "abc", Digest: slsa.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}},
{URI: "git+https://git.test.com.git", Digest: slsa.DigestSet{"sha1": "abcd"}},
},
Expand Down Expand Up @@ -339,7 +354,8 @@ func TestPipelineRunCreatePayload(t *testing.T) {
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if diff := cmp.Diff(expected, got); diff != "" {
// Sort Materials since their order can vary and result in flakes
if diff := cmp.Diff(expected, got, test.OptSortMaterial); diff != "" {
t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff)
}
}
Expand Down Expand Up @@ -381,6 +397,20 @@ func TestPipelineRunCreatePayloadChildRefs(t *testing.T) {
},
Materials: []slsa.ProvenanceMaterial{
{URI: "git+https://git.test.com.git", Digest: slsa.DigestSet{"sha1": "abcd"}},
{
URI: "docker-pullable://gcr.io/test3/test3",
Digest: slsa.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"},
},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "ab123"}},
{
URI: "docker-pullable://gcr.io/test1/test1",
Digest: slsa.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"},
},
{URI: "github.com/catalog", Digest: slsa.DigestSet{"sha1": "x123"}},
{
URI: "docker-pullable://gcr.io/test2/test2",
Digest: slsa.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"},
},
},
Invocation: slsa.ProvenanceInvocation{
ConfigSource: slsa.ConfigSource{},
Expand Down Expand Up @@ -535,7 +565,8 @@ func TestPipelineRunCreatePayloadChildRefs(t *testing.T) {
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if diff := cmp.Diff(expected, got); diff != "" {
// Sort Materials since their order can vary and result in flakes
if diff := cmp.Diff(expected, got, test.OptSortMaterial); diff != "" {
t.Errorf("InTotoIte6.CreatePayload(): -want +got: %s", diff)
}
}
Expand Down
56 changes: 53 additions & 3 deletions pkg/chains/formats/intotoite6/pipelinerun/pipelinerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/tektoncd/chains/pkg/artifacts"
"github.com/tektoncd/chains/pkg/chains/formats/intotoite6/attest"
"github.com/tektoncd/chains/pkg/chains/formats/intotoite6/extract"
"github.com/tektoncd/chains/pkg/chains/formats/intotoite6/internal/material"
"github.com/tektoncd/chains/pkg/chains/formats/intotoite6/taskrun"
"github.com/tektoncd/chains/pkg/chains/objects"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"go.uber.org/zap"
Expand All @@ -47,6 +49,10 @@ type TaskAttestation struct {
func GenerateAttestation(builderID string, pro *objects.PipelineRunObject, logger *zap.SugaredLogger) (interface{}, error) {
subjects := extract.SubjectDigests(pro, logger)

mat, err := materials(pro, logger)
if err != nil {
return nil, err
}
att := intoto.ProvenanceStatement{
StatementHeader: intoto.StatementHeader{
Type: intoto.StatementInTotoV01,
Expand All @@ -61,7 +67,7 @@ func GenerateAttestation(builderID string, pro *objects.PipelineRunObject, logge
Invocation: invocation(pro),
BuildConfig: buildConfig(pro, logger),
Metadata: metadata(pro),
Materials: materials(pro, logger),
Materials: mat,
},
}
return att, nil
Expand Down Expand Up @@ -182,8 +188,46 @@ func metadata(pro *objects.PipelineRunObject) *slsa.ProvenanceMetadata {
}

// add any Git specification to materials
func materials(pro *objects.PipelineRunObject, logger *zap.SugaredLogger) []slsa.ProvenanceMaterial {
func materials(pro *objects.PipelineRunObject, logger *zap.SugaredLogger) ([]slsa.ProvenanceMaterial, error) {
var mats []slsa.ProvenanceMaterial
if p := pro.Status.Provenance; p != nil {
m := slsa.ProvenanceMaterial{
URI: p.ConfigSource.URI,
Digest: p.ConfigSource.Digest,
}
mats = append(mats, m)
}
pSpec := pro.Status.PipelineSpec
if pSpec != nil {
pipelineTasks := append(pSpec.Tasks, pSpec.Finally...)
for _, t := range pipelineTasks {
tr := pro.GetTaskRunFromTask(t.Name)
// Ignore Tasks that did not execute during the PipelineRun.
if tr == nil || tr.Status.CompletionTime == nil {
logger.Infof("taskrun status not found for task %s", t.Name)
continue
}

// add step images
if err := taskrun.AddStepImagesToMaterials(tr.Status.Steps, &mats); err != nil {
return mats, nil
}

// add sidecar images
if err := taskrun.AddSidecarImagesToMaterials(tr.Status.Sidecars, &mats); err != nil {
return mats, nil
}

// add remote task configsource information in materials
if tr.Status.Provenance != nil && tr.Status.Provenance.ConfigSource != nil {
m := slsa.ProvenanceMaterial{
URI: tr.Status.Provenance.ConfigSource.URI,
Digest: tr.Status.Provenance.ConfigSource.Digest,
}
mats = append(mats, m)
}
}
}
var commit, url string
// search spec.params
for _, p := range pro.Spec.Params {
Expand Down Expand Up @@ -231,7 +275,13 @@ func materials(pro *objects.PipelineRunObject, logger *zap.SugaredLogger) []slsa
Digest: map[string]string{"sha1": commit},
})
}
return mats

// remove duplicate materials
mats, err := material.RemoveDuplicateMaterials(mats)
if err != nil {
return mats, err
}
return mats, nil
}

// Following tkn cli's behavior
Expand Down
47 changes: 42 additions & 5 deletions pkg/chains/formats/intotoite6/pipelinerun/provenance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/tektoncd/chains/pkg/chains/formats/intotoite6/extract"
"github.com/tektoncd/chains/pkg/chains/objects"
"github.com/tektoncd/chains/pkg/internal/objectloader"
"github.com/tektoncd/chains/test"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"k8s.io/apimachinery/pkg/selection"
logtesting "knative.dev/pkg/logging/testing"
Expand Down Expand Up @@ -451,26 +452,62 @@ func TestMetadataInTimeZone(t *testing.T) {

func TestMaterials(t *testing.T) {
expected := []slsa.ProvenanceMaterial{
{
URI: "docker-pullable://gcr.io/test1/test1",
Digest: slsa.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"},
},
{URI: "github.com/catalog", Digest: slsa.DigestSet{"sha1": "x123"}},
{
URI: "docker-pullable://gcr.io/test2/test2",
Digest: slsa.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"},
},
{
URI: "docker-pullable://gcr.io/test3/test3",
Digest: slsa.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"},
},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "28b123"}},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "ab123"}},
{URI: "abc", Digest: slsa.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}},
{URI: "git+https://git.test.com.git", Digest: slsa.DigestSet{"sha1": "abcd"}},
}
got := materials(pro, logtesting.TestLogger(t))
if diff := cmp.Diff(expected, got); diff != "" {
t.Errorf("materials(): -want +got: %s", diff)
got, err := materials(pro, logtesting.TestLogger(t))
if err != nil {
t.Error(err)
}
if diff := cmp.Diff(expected, got, test.OptSortMaterial); diff != "" {
t.Errorf("Materials(): -want +got: %s", diff)
}
}

func TestStructuredResultMaterials(t *testing.T) {
want := []slsa.ProvenanceMaterial{
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "28b123"}},
{
URI: "docker-pullable://gcr.io/test1/test1",
Digest: slsa.DigestSet{"sha256": "d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6"},
},
{URI: "github.com/catalog", Digest: slsa.DigestSet{"sha1": "x123"}},
{
URI: "docker-pullable://gcr.io/test2/test2",
Digest: slsa.DigestSet{"sha256": "4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac"},
},
{
URI: "docker-pullable://gcr.io/test3/test3",
Digest: slsa.DigestSet{"sha256": "f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478"},
},
{URI: "github.com/test", Digest: slsa.DigestSet{"sha1": "ab123"}},
{
URI: "abcd",
Digest: slsa.DigestSet{
"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7",
},
},
}
got := materials(proStructuredResults, logtesting.TestLogger(t))
if diff := cmp.Diff(want, got); diff != "" {
got, err := materials(proStructuredResults, logtesting.TestLogger(t))
if err != nil {
t.Errorf("error while extracting materials: %v", err)
}
if diff := cmp.Diff(want, got, test.OptSortMaterial); diff != "" {
t.Errorf("materials(): -want +got: %s", diff)
}
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/chains/formats/intotoite6/taskrun/material.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,28 @@ const (
digestSeparator = ":"
)

// addStepImagesToMaterials adds step images to predicate.materials
func addStepImagesToMaterials(steps []v1beta1.StepState, mats *[]slsa.ProvenanceMaterial) error {
// AddStepImagesToMaterials adds step images to predicate.materials
func AddStepImagesToMaterials(steps []v1beta1.StepState, mats *[]slsa.ProvenanceMaterial) error {
for _, stepState := range steps {
if err := addImageIDToMaterials(stepState.ImageID, mats); err != nil {
if err := AddImageIDToMaterials(stepState.ImageID, mats); err != nil {
return err
}
}
return nil
}

// addSidecarImagesToMaterials adds sidecar images to predicate.materials
func addSidecarImagesToMaterials(sidecars []v1beta1.SidecarState, mats *[]slsa.ProvenanceMaterial) error {
// AddSidecarImagesToMaterials adds sidecar images to predicate.materials
func AddSidecarImagesToMaterials(sidecars []v1beta1.SidecarState, mats *[]slsa.ProvenanceMaterial) error {
for _, sidecarState := range sidecars {
if err := addImageIDToMaterials(sidecarState.ImageID, mats); err != nil {
if err := AddImageIDToMaterials(sidecarState.ImageID, mats); err != nil {
return err
}
}
return nil
}

// addImageIDToMaterials converts an imageId with format <uri>@sha256:<digest> and then adds it to a provenance materials.
func addImageIDToMaterials(imageID string, mats *[]slsa.ProvenanceMaterial) error {
// AddImageIDToMaterials converts an imageId with format <uri>@sha256:<digest> and then adds it to a provenance materials.
func AddImageIDToMaterials(imageID string, mats *[]slsa.ProvenanceMaterial) error {
m := slsa.ProvenanceMaterial{
Digest: slsa.DigestSet{},
}
Expand All @@ -83,12 +83,12 @@ func materials(tro *objects.TaskRunObject, logger *zap.SugaredLogger) ([]slsa.Pr
var mats []slsa.ProvenanceMaterial

// add step images
if err := addStepImagesToMaterials(tro.Status.Steps, &mats); err != nil {
if err := AddStepImagesToMaterials(tro.Status.Steps, &mats); err != nil {
return mats, nil
}

// add sidecar images
if err := addSidecarImagesToMaterials(tro.Status.Sidecars, &mats); err != nil {
if err := AddSidecarImagesToMaterials(tro.Status.Sidecars, &mats); err != nil {
return mats, nil
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/chains/formats/intotoite6/taskrun/material_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func TestAddStepImagesToMaterials(t *testing.T) {
}}
for _, tc := range tests {
var mat []slsa.ProvenanceMaterial
if err := addStepImagesToMaterials(tc.steps, &mat); err != nil {
if err := AddStepImagesToMaterials(tc.steps, &mat); err != nil {
if err.Error() != tc.wantError.Error() {
t.Fatalf("Expected error %v but got %v", tc.wantError, err)
}
Expand Down Expand Up @@ -373,7 +373,7 @@ func TestAddSidecarImagesToMaterials(t *testing.T) {
}}
for _, tc := range tests {
var mat []slsa.ProvenanceMaterial
if err := addSidecarImagesToMaterials(tc.sidecars, &mat); err != nil {
if err := AddSidecarImagesToMaterials(tc.sidecars, &mat); err != nil {
if err.Error() != tc.wantError.Error() {
t.Fatalf("Expected error %v but got %v", tc.wantError, err)
}
Expand Down Expand Up @@ -411,7 +411,7 @@ func TestAddImageIDToMaterials(t *testing.T) {
}}
for _, tc := range tests {
mat := []slsa.ProvenanceMaterial{}
if err := addImageIDToMaterials(tc.imageID, &mat); err != nil {
if err := AddImageIDToMaterials(tc.imageID, &mat); err != nil {
if err.Error() != tc.wantError.Error() {
t.Fatalf("Expected error %v but got %v", tc.wantError, err)
}
Expand Down
21 changes: 20 additions & 1 deletion test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ import (
"fmt"
"io"
"os"
"strings"
"testing"
"time"

"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/storage"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/sigstore/sigstore/pkg/cryptoutils"
Expand Down Expand Up @@ -769,8 +771,25 @@ func TestProvenanceMaterials(t *testing.T) {
},
},
}
if test.name == "pipelinerun" {
pr := signedObj.GetObject().(*v1beta1.PipelineRun)
for _, trStatus := range pr.Status.TaskRuns {
for _, step := range trStatus.Status.Steps {
want = append(want, provenance.ProvenanceMaterial{
URI: strings.Split(step.ImageID, "@")[0],
Digest: provenance.DigestSet{
"sha256": strings.Split(step.ImageID, ":")[1],
},
})
}
}
}
got := predicate.Materials
if d := cmp.Diff(want, got); d != "" {

sortMaterials := cmpopts.SortSlices(func(i, j provenance.ProvenanceMaterial) bool {
return i.URI < j.URI
})
if d := cmp.Diff(want, got, sortMaterials); d != "" {
t.Fatal(string(d))
}
})
Expand Down
Loading

0 comments on commit 96c74ec

Please sign in to comment.