Skip to content

Commit d4a946e

Browse files
committed
add wiring to capture the source of a remote stepActions used in a task. saved in SLSA provenance resolvedDependency
1 parent 4bb6198 commit d4a946e

4 files changed

Lines changed: 201 additions & 0 deletions

File tree

pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const (
4343
InputResultName = "inputs/result"
4444
// PipelineResourceName is the name of the resolved dependency of pipeline resource.
4545
PipelineResourceName = "pipelineResource"
46+
// StepActionConfigName is the name of the resolved dependencies of remote StepAction
47+
StepActionConfigName = "stepAction"
4648
)
4749

4850
// AddTaskDescriptorContent is used to toggle the fields in see AddTektonTaskDescriptor and AddSLSATaskDescriptor
@@ -183,6 +185,7 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV
183185
mats = append(mats, sidecarMaterials...)
184186

185187
// convert materials to resolved dependencies
188+
resolvedDependencies = append(resolvedDependencies, fromRemoteStepActions(tr)...)
186189
resolvedDependencies = append(resolvedDependencies, ConvertMaterialsToResolvedDependencies(mats, "")...)
187190

188191
}
@@ -191,6 +194,20 @@ func fromPipelineTask(logger *zap.SugaredLogger, pro *objects.PipelineRunObjectV
191194
return resolvedDependencies, nil
192195
}
193196

197+
// fromRemoteStepActions converts remote StepAction provenance into resolved dependencies
198+
func fromRemoteStepActions(tro *objects.TaskRunObjectV1) []*intoto.ResourceDescriptor {
199+
var rds []*intoto.ResourceDescriptor
200+
for _, stepProv := range tro.GetRemoteStepActions() {
201+
rd := &intoto.ResourceDescriptor{
202+
Name: fmt.Sprintf("%s/%s", StepActionConfigName, stepProv.StepName),
203+
Uri: stepProv.Provenance.RefSource.URI,
204+
Digest: stepProv.Provenance.RefSource.Digest,
205+
}
206+
rds = append(rds, rd)
207+
}
208+
return rds
209+
}
210+
194211
// taskDependencies gather all dependencies in a task and adds them to resolvedDependencies
195212
func taskDependencies(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjectV1) ([]*intoto.ResourceDescriptor, error) {
196213
var resolvedDependencies []*intoto.ResourceDescriptor
@@ -224,6 +241,8 @@ func taskDependencies(ctx context.Context, opts ResolveOptions, tro *objects.Tas
224241
resolvedDependencies = append(resolvedDependencies,
225242
ConvertMaterialsToResolvedDependencies(mats, PipelineResourceName)...)
226243

244+
resolvedDependencies = append(resolvedDependencies, fromRemoteStepActions(tro)...)
245+
227246
// remove duplicate resolved dependencies
228247
resolvedDependencies, err = RemoveDuplicateResolvedDependencies(resolvedDependencies)
229248
if err != nil {

pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,118 @@ func TestTaskRun(t *testing.T) {
489489
},
490490
},
491491
},
492+
{
493+
name: "resolvedDependencies from remote StepAction",
494+
obj: objects.NewTaskRunObjectV1(&v1.TaskRun{
495+
Status: v1.TaskRunStatus{
496+
TaskRunStatusFields: v1.TaskRunStatusFields{
497+
Steps: []v1.StepState{
498+
{
499+
Name: "remote-build",
500+
ImageID: "gcr.io/test/build@sha256:aaa111",
501+
Provenance: &v1.Provenance{
502+
RefSource: &v1.RefSource{
503+
URI: "git+https://github.com/tektoncd/stepactions",
504+
Digest: map[string]string{"sha256": "bbb222"},
505+
},
506+
},
507+
},
508+
},
509+
},
510+
},
511+
}),
512+
want: []*intoto.ResourceDescriptor{
513+
{
514+
Uri: "oci://gcr.io/test/build",
515+
Digest: common.DigestSet{"sha256": "aaa111"},
516+
},
517+
{
518+
Name: "stepAction/remote-build",
519+
Uri: "git+https://github.com/tektoncd/stepactions",
520+
Digest: common.DigestSet{"sha256": "bbb222"},
521+
},
522+
},
523+
},
524+
{
525+
name: "local StepAction excluded from resolvedDependencies",
526+
obj: objects.NewTaskRunObjectV1(&v1.TaskRun{
527+
Status: v1.TaskRunStatus{
528+
TaskRunStatusFields: v1.TaskRunStatusFields{
529+
Steps: []v1.StepState{
530+
{
531+
Name: "local-step",
532+
ImageID: "gcr.io/test/local@sha256:ccc333",
533+
},
534+
},
535+
},
536+
},
537+
}),
538+
want: []*intoto.ResourceDescriptor{
539+
{
540+
Uri: "oci://gcr.io/test/local",
541+
Digest: common.DigestSet{"sha256": "ccc333"},
542+
},
543+
},
544+
},
545+
{
546+
name: "multiple remote StepActions produce distinct resolvedDependencies",
547+
obj: objects.NewTaskRunObjectV1(&v1.TaskRun{
548+
Status: v1.TaskRunStatus{
549+
TaskRunStatusFields: v1.TaskRunStatusFields{
550+
Steps: []v1.StepState{
551+
{
552+
Name: "git-clone",
553+
ImageID: "gcr.io/test/git@sha256:aaa111",
554+
Provenance: &v1.Provenance{
555+
RefSource: &v1.RefSource{
556+
URI: "git+https://github.com/tektoncd/stepactions",
557+
Digest: map[string]string{"sha256": "step1digest"},
558+
},
559+
},
560+
},
561+
{
562+
Name: "build",
563+
ImageID: "gcr.io/test/build@sha256:bbb222",
564+
Provenance: &v1.Provenance{
565+
RefSource: &v1.RefSource{
566+
URI: "git+https://github.com/tektoncd/stepactions",
567+
Digest: map[string]string{"sha256": "step2digest"},
568+
},
569+
},
570+
},
571+
{
572+
Name: "local-lint",
573+
ImageID: "gcr.io/test/lint@sha256:ccc333",
574+
},
575+
},
576+
},
577+
},
578+
}),
579+
want: []*intoto.ResourceDescriptor{
580+
{
581+
Uri: "oci://gcr.io/test/git",
582+
Digest: common.DigestSet{"sha256": "aaa111"},
583+
},
584+
{
585+
Uri: "oci://gcr.io/test/build",
586+
Digest: common.DigestSet{"sha256": "bbb222"},
587+
},
588+
{
589+
Uri: "oci://gcr.io/test/lint",
590+
Digest: common.DigestSet{"sha256": "ccc333"},
591+
},
592+
{
593+
Name: "stepAction/git-clone",
594+
Uri: "git+https://github.com/tektoncd/stepactions",
595+
Digest: common.DigestSet{"sha256": "step1digest"},
596+
},
597+
{
598+
Name: "stepAction/build",
599+
Uri: "git+https://github.com/tektoncd/stepactions",
600+
Digest: common.DigestSet{"sha256": "step2digest"},
601+
},
602+
},
603+
},
492604
}
493605
for _, tc := range tests {
494606
t.Run(tc.name, func(t *testing.T) {

pkg/chains/objects/objects.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ type Result struct {
5757
Value v1.ParamValue
5858
}
5959

60+
// StepProvenance associates a step name with its remote StepAction provenanance
61+
type StepProvenance struct {
62+
StepName string
63+
Provenance *v1.Provenance
64+
}
65+
6066
// Tekton object is an extended Kubernetes object with operations specific
6167
// to Tekton objects.
6268
type TektonObject interface {
@@ -172,6 +178,20 @@ func (tro *TaskRunObjectV1) GetStepResults() []Result {
172178
return res
173179
}
174180

181+
// GetRemoteStepActions returns the provenance of each step that used a remotely resolved StepAction.
182+
func (tro *TaskRunObjectV1) GetRemoteStepActions() []StepProvenance {
183+
var remoteSteps []StepProvenance
184+
for _, step := range tro.Status.Steps {
185+
if step.Provenance != nil && step.Provenance.RefSource != nil {
186+
remoteSteps = append(remoteSteps, StepProvenance{
187+
StepName: step.Name,
188+
Provenance: step.Provenance,
189+
})
190+
}
191+
}
192+
return remoteSteps
193+
}
194+
175195
func (tro *TaskRunObjectV1) GetStepImages() []string {
176196
images := []string{}
177197
for _, stepState := range tro.Status.Steps {

pkg/chains/objects/objects_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,56 @@ func TestTaskRun_GetResults(t *testing.T) {
238238

239239
}
240240

241+
func TestTaskRun_GetRemoteStepActions(t *testing.T) {
242+
t.Run("returns remote StepActions only", func(t *testing.T) {
243+
tr := NewTaskRunObjectV1(&v1.TaskRun{
244+
Status: v1.TaskRunStatus{
245+
TaskRunStatusFields: v1.TaskRunStatusFields{
246+
Steps: []v1.StepState{
247+
{
248+
Name: "remote-step",
249+
ImageID: "gcr.io/test/image@sha256:abc",
250+
Provenance: &v1.Provenance{
251+
RefSource: &v1.RefSource{
252+
URI: "git+https://github.com/tektoncd/stepactions",
253+
Digest: map[string]string{"sha256": "def456"},
254+
},
255+
},
256+
},
257+
{
258+
Name: "local-step",
259+
ImageID: "gcr.io/test/local@sha256:789",
260+
},
261+
},
262+
},
263+
},
264+
})
265+
got := tr.GetRemoteStepActions()
266+
want := []StepProvenance{
267+
{
268+
StepName: "remote-step",
269+
Provenance: &v1.Provenance{
270+
RefSource: &v1.RefSource{
271+
URI: "git+https://github.com/tektoncd/stepactions",
272+
Digest: map[string]string{"sha256": "def456"},
273+
},
274+
},
275+
},
276+
}
277+
if d := cmp.Diff(want, got); d != "" {
278+
t.Fatalf("GetRemoteStepActions (-want, +got):\n%s", d)
279+
}
280+
})
281+
282+
t.Run("returns empty when no remote StepActions", func(t *testing.T) {
283+
tr := NewTaskRunObjectV1(getTaskRun())
284+
got := tr.GetRemoteStepActions()
285+
if len(got) != 0 {
286+
t.Fatalf("expected empty slice, got %v", got)
287+
}
288+
})
289+
}
290+
241291
func TestPipelineRun_GetGVK(t *testing.T) {
242292
assert.Equal(t, "tekton.dev/v1/PipelineRun", NewPipelineRunObjectV1(getPipelineRun()).GetGVK())
243293
}

0 commit comments

Comments
 (0)