diff --git a/examples/v2alpha4/pipeline-with-object-type-hinting.yaml b/examples/v2alpha4/pipeline-with-object-type-hinting.yaml new file mode 100644 index 0000000000..1f925bd132 --- /dev/null +++ b/examples/v2alpha4/pipeline-with-object-type-hinting.yaml @@ -0,0 +1,35 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + name: pipeline-test-run +spec: + pipelineSpec: + results: + - name: output1-ARTIFACT_OUTPUTS + value: $(tasks.t1.results.output1) + - name: output2-ARTIFACT_OUTPUTS + value: $(tasks.t1.results.output2) + tasks: + - name: t1 + taskSpec: + results: + - name: output1 + 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.path) + echo -n "{\"uri\":\"gcr.io/foo/img2\", \"digest\":\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\"}" > $(results.output2.path) \ No newline at end of file diff --git a/pkg/chains/formats/slsa/internal/build_definition/build_definition.go b/pkg/chains/formats/slsa/internal/build_definition/build_definition.go index 7e9307c26c..2cd2abb2b9 100644 --- a/pkg/chains/formats/slsa/internal/build_definition/build_definition.go +++ b/pkg/chains/formats/slsa/internal/build_definition/build_definition.go @@ -21,6 +21,7 @@ import ( externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" ) @@ -55,3 +56,30 @@ func GetTaskRunBuildDefinition(ctx context.Context, tro *objects.TaskRunObjectV1 return getBuildDefinition(buildDefinitionType, rd, externalParams, internalParams), nil } + +// GetBuildDefinition returns the buildDefinition for the given PipelineRun based on the configured buildType. This will default to the slsa buildType +func GetPipelineRunBuildDefinition(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig, resolveOpts resolveddependencies.ResolveOptions) (slsa.ProvenanceBuildDefinition, error) { + buildDefinitionType := slsaconfig.BuildType + if slsaconfig.BuildType == "" { + buildDefinitionType = buildtypes.SlsaBuildType + } + + td, err := resolveddependencies.GetTaskDescriptor(buildDefinitionType) + if err != nil { + return slsa.ProvenanceBuildDefinition{}, err + } + + rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveOpts, td) + if err != nil { + return slsa.ProvenanceBuildDefinition{}, err + } + + externalParams := externalparameters.PipelineRun(pro) + + internalParams, err := internalparameters.GetInternalParamters(pro, buildDefinitionType) + if err != nil { + return slsa.ProvenanceBuildDefinition{}, err + } + + return getBuildDefinition(buildDefinitionType, rd, externalParams, internalParams), nil +} diff --git a/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go b/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go index 68326622f1..932aaff61b 100644 --- a/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go +++ b/pkg/chains/formats/slsa/internal/build_definition/build_definition_test.go @@ -19,14 +19,16 @@ import ( "github.com/google/go-cmp/cmp" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + v1resourcedescriptor "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" ) -func TestGetBuildDefinition(t *testing.T) { +func TestGetTaskRunBuildDefinition(t *testing.T) { tr, err := objectloader.TaskRunFromFile("../../testdata/slsa-v2alpha4/taskrun1.json") if err != nil { t.Fatal(err) @@ -100,7 +102,7 @@ func TestGetBuildDefinition(t *testing.T) { } } -func TestUnsupportedBuildType(t *testing.T) { +func TestTaskRunnUnsupportedBuildType(t *testing.T) { tr, err := objectloader.TaskRunFromFile("../../testdata/slsa-v2alpha4/taskrun1.json") if err != nil { t.Fatal(err) @@ -114,3 +116,101 @@ func TestUnsupportedBuildType(t *testing.T) { t.Errorf("getBuildDefinition(): -want +got: %s", diff) } } + +func TestGetPipelineRunBuildDefinition(t *testing.T) { + pr := createPro("../../testdata/slsa-v2alpha3/pipelinerun1.json") + pr.Annotations = map[string]string{ + "annotation1": "annotation1", + } + pr.Labels = map[string]string{ + "label1": "label1", + } + tests := []struct { + name string + config *slsaconfig.SlsaConfig + want slsa.ProvenanceBuildDefinition + }{ + { + name: "test slsa build type", + config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, + want: slsa.ProvenanceBuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: externalparameters.PipelineRun(pr), + InternalParameters: internalparameters.SLSAInternalParameters(pr), + ResolvedDependencies: getResolvedDependencies(pr, resolveddependencies.AddSLSATaskDescriptor), + }, + }, + { + name: "test tekton build type", + config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa-tekton"}, + want: slsa.ProvenanceBuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa-tekton", + ExternalParameters: externalparameters.PipelineRun(pr), + InternalParameters: internalparameters.TektonInternalParameters(pr), + ResolvedDependencies: getResolvedDependencies(pr, resolveddependencies.AddTektonTaskDescriptor), + }, + }, + { + name: "test default build type", + config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, + want: slsa.ProvenanceBuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: externalparameters.PipelineRun(pr), + InternalParameters: internalparameters.SLSAInternalParameters(pr), + ResolvedDependencies: getResolvedDependencies(pr, resolveddependencies.AddSLSATaskDescriptor), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + bd, err := GetPipelineRunBuildDefinition(context.TODO(), pr, tc.config, resolveddependencies.ResolveOptions{}) + if err != nil { + t.Fatalf("Did not expect an error but got %v", err) + } + + if diff := cmp.Diff(tc.want, bd); diff != "" { + t.Errorf("getBuildDefinition(): -want +got: %v", diff) + } + }) + } +} + +func createPro(path string) *objects.PipelineRunObjectV1 { + pr, err := objectloader.PipelineRunFromFile(path) + if err != nil { + panic(err) + } + tr1, err := objectloader.TaskRunFromFile("../../testdata/slsa-v2alpha3/taskrun1.json") + if err != nil { + panic(err) + } + tr2, err := objectloader.TaskRunFromFile("../../testdata/slsa-v2alpha3/taskrun2.json") + if err != nil { + panic(err) + } + p := objects.NewPipelineRunObjectV1(pr) + p.AppendTaskRun(tr1) + p.AppendTaskRun(tr2) + return p +} + +func getResolvedDependencies(pr *objects.PipelineRunObjectV1, addTasks func(*objects.TaskRunObjectV1) (*v1resourcedescriptor.ResourceDescriptor, error)) []v1resourcedescriptor.ResourceDescriptor { //nolint:staticcheck + rd, err := resolveddependencies.PipelineRun(context.Background(), pr, &slsaconfig.SlsaConfig{}, resolveddependencies.ResolveOptions{}, addTasks) + if err != nil { + return []v1resourcedescriptor.ResourceDescriptor{} + } + return rd +} + +func TestPipelineRunUnsupportedBuildType(t *testing.T) { + pr := createPro("../../testdata/slsa-v2alpha3/pipelinerun1.json") + + got, err := GetPipelineRunBuildDefinition(context.Background(), pr, &slsaconfig.SlsaConfig{BuildType: "bad-buildtype"}, resolveddependencies.ResolveOptions{}) + if err == nil { + t.Error("getBuildDefinition(): expected error got nil") + } + if diff := cmp.Diff(slsa.ProvenanceBuildDefinition{}, got); diff != "" { + t.Errorf("getBuildDefinition(): -want +got: %s", diff) + } +} diff --git a/pkg/chains/formats/slsa/internal/metadata/metadata_test.go b/pkg/chains/formats/slsa/internal/metadata/metadata_test.go index 2f0059f249..1125b1e1d1 100644 --- a/pkg/chains/formats/slsa/internal/metadata/metadata_test.go +++ b/pkg/chains/formats/slsa/internal/metadata/metadata_test.go @@ -25,62 +25,139 @@ import ( ) func TestMetadata(t *testing.T) { - tr := &v1.TaskRun{ //nolint:staticcheck - ObjectMeta: metav1.ObjectMeta{ - Name: "my-taskrun", - Namespace: "my-namespace", - Annotations: map[string]string{ - "chains.tekton.dev/reproducible": "true", + tests := []struct { + name string + obj objects.TektonObject + expected slsa.BuildMetadata + }{ + { + name: "taskrun metadata", + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + ObjectMeta: metav1.ObjectMeta{ + Name: "my-taskrun", + Namespace: "my-namespace", + Annotations: map[string]string{ + "chains.tekton.dev/reproducible": "true", + }, + UID: "abhhf-12354-asjsdbjs23-3435353n", + }, + Status: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)}, + CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)}, + }, + }, + }), + expected: slsa.BuildMetadata{ + InvocationID: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)), + FinishedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)), }, - UID: "abhhf-12354-asjsdbjs23-3435353n", }, - Status: v1.TaskRunStatus{ - TaskRunStatusFields: v1.TaskRunStatusFields{ - StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)}, - CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)}, + { + name: "pipelinerun metadata", + obj: objects.NewPipelineRunObjectV1(&v1.PipelineRun{ //nolint:staticcheck + ObjectMeta: metav1.ObjectMeta{ + Name: "my-taskrun", + Namespace: "my-namespace", + Annotations: map[string]string{ + "chains.tekton.dev/reproducible": "true", + }, + UID: "abhhf-12354-asjsdbjs23-3435353n", + }, + Status: v1.PipelineRunStatus{ + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)}, + CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)}, + }, + }, + }), + expected: slsa.BuildMetadata{ + InvocationID: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)), + FinishedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)), }, }, } - start := time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC) - end := time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC) - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, - } - got := GetBuildMetadata(objects.NewTaskRunObjectV1(tr)) - if d := cmp.Diff(want, got); d != "" { - t.Fatalf("metadata (-want, +got):\n%s", d) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := GetBuildMetadata(test.obj) + if d := cmp.Diff(test.expected, got); d != "" { + t.Fatalf("metadata (-want, +got):\n%s", d) + } + }) } } func TestMetadataInTimeZone(t *testing.T) { tz := time.FixedZone("Test Time", int((12 * time.Hour).Seconds())) - tr := &v1.TaskRun{ //nolint:staticcheck - ObjectMeta: metav1.ObjectMeta{ - Name: "my-taskrun", - Namespace: "my-namespace", - Annotations: map[string]string{ - "chains.tekton.dev/reproducible": "true", + + tests := []struct { + name string + obj objects.TektonObject + expected slsa.BuildMetadata + }{ + { + name: "taskrun metadata", + obj: objects.NewTaskRunObjectV1(&v1.TaskRun{ //nolint:staticcheck + ObjectMeta: metav1.ObjectMeta{ + Name: "my-taskrun", + Namespace: "my-namespace", + Annotations: map[string]string{ + "chains.tekton.dev/reproducible": "true", + }, + UID: "abhhf-12354-asjsdbjs23-3435353n", + }, + Status: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, tz)}, + CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, tz)}, + }, + }, + }), + expected: slsa.BuildMetadata{ + InvocationID: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC()), + FinishedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC()), }, - UID: "abhhf-12354-asjsdbjs23-3435353n", }, - Status: v1.TaskRunStatus{ - TaskRunStatusFields: v1.TaskRunStatusFields{ - StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, tz)}, - CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, tz)}, + { + name: "pipelinerun metadata", + obj: objects.NewPipelineRunObjectV1(&v1.PipelineRun{ //nolint:staticcheck + ObjectMeta: metav1.ObjectMeta{ + Name: "my-taskrun", + Namespace: "my-namespace", + Annotations: map[string]string{ + "chains.tekton.dev/reproducible": "true", + }, + UID: "abhhf-12354-asjsdbjs23-3435353n", + }, + Status: v1.PipelineRunStatus{ + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, tz)}, + CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, tz)}, + }, + }, + }), + expected: slsa.BuildMetadata{ + InvocationID: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC()), + FinishedOn: getPtr(time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC()), }, }, } - start := time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC() - end := time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC() - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, - } - got := GetBuildMetadata(objects.NewTaskRunObjectV1(tr)) - if d := cmp.Diff(want, got); d != "" { - t.Fatalf("metadata (-want, +got):\n%s", d) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := GetBuildMetadata(test.obj) + if d := cmp.Diff(test.expected, got); d != "" { + t.Fatalf("metadata (-want, +got):\n%s", d) + } + }) } } + +func getPtr(t time.Time) *time.Time { + return &t +} diff --git a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go index a619d9d299..2c7ae4a840 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies.go @@ -23,6 +23,7 @@ import ( "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + buildtypes "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_types" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/material" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" @@ -275,7 +276,7 @@ func TaskRun(ctx context.Context, opts ResolveOptions, tro *objects.TaskRunObjec } // PipelineRun constructs `predicate.resolvedDependencies` section by collecting all the artifacts that influence a pipeline run such as source code repo and step&sidecar base images. -func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig, addTasks addTaskDescriptorContent) ([]slsa.ResourceDescriptor, error) { +func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig, opts ResolveOptions, addTasks addTaskDescriptorContent) ([]slsa.ResourceDescriptor, error) { var err error var resolvedDependencies []slsa.ResourceDescriptor logger := logging.FromContext(ctx) @@ -297,6 +298,14 @@ func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconf } resolvedDependencies = append(resolvedDependencies, rds...) + if slsaconfig.DeepInspectionEnabled && opts.WithStepActionsResults { + execTasks := pro.GetExecutedTasks() + for _, task := range execTasks { + stepActionMat := material.FromStepActionsResults(ctx, task) + resolvedDependencies = append(resolvedDependencies, ConvertMaterialsToResolvedDependencies(stepActionMat, InputResultName)...) + } + } + // add resolved dependencies from pipeline results mats := material.FromPipelineParamsAndResults(ctx, pro, slsaconfig) // convert materials to resolved dependencies @@ -309,3 +318,15 @@ func PipelineRun(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconf } return resolvedDependencies, nil } + +// GetTaskDescriptor returns the conrresponding addTaskDescriptor function according to the given build type. +func GetTaskDescriptor(buildDefinition string) (addTaskDescriptorContent, error) { + switch buildDefinition { + case buildtypes.SlsaBuildType: + return AddSLSATaskDescriptor, nil + case buildtypes.TektonBuildType: + return AddTektonTaskDescriptor, nil + default: + return nil, fmt.Errorf("unsupported buildType %v", buildDefinition) + } +} diff --git a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go index a32ab0db9a..808a76c80c 100644 --- a/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go +++ b/pkg/chains/formats/slsa/internal/resolved_dependencies/resolved_dependencies_test.go @@ -18,6 +18,8 @@ package resolveddependencies import ( "encoding/json" + "reflect" + "runtime" "strings" "testing" @@ -651,12 +653,55 @@ func TestPipelineRun(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { pro := createPro("../../testdata/slsa-v2alpha3/pipelinerun1.json") - got, err := PipelineRun(ctx, pro, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}, tc.taskDescriptor) + got, err := PipelineRun(ctx, pro, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}, ResolveOptions{}, tc.taskDescriptor) if err != nil { t.Error(err) } if d := cmp.Diff(tc.want, got); d != "" { - t.Errorf("PipelineRunResolvedDependencies(): -want +got: %s", got) + t.Errorf("PipelineRunResolvedDependencies(): -want +got: %s", d) + } + }) + } +} + +func TestGetTaskDescriptor(t *testing.T) { + tests := []struct { + name string + buildDefinitionType string + expected addTaskDescriptorContent + shouldErr bool + }{ + { + name: "slsa task descriptor", + buildDefinitionType: "https://tekton.dev/chains/v2/slsa", + expected: AddSLSATaskDescriptor, + }, + { + name: "tekton task descriptor", + buildDefinitionType: "https://tekton.dev/chains/v2/slsa-tekton", + expected: AddTektonTaskDescriptor, + }, + { + name: "bad descriptor", + buildDefinitionType: "https://foo.io/fake", + shouldErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + td, err := GetTaskDescriptor(test.buildDefinitionType) + didErr := err != nil + + if test.shouldErr != didErr { + t.Fatalf("Unexpected behavior in error, shouldErr: %v, didErr: %v, err: %v", test.shouldErr, didErr, err) + } + + got := runtime.FuncForPC(reflect.ValueOf(td).Pointer()).Name() + expected := runtime.FuncForPC(reflect.ValueOf(test.expected).Pointer()).Name() + + if d := cmp.Diff(expected, got); d != "" { + t.Errorf("GetTaskDescriptor(): -want +got: %v", d) } }) } diff --git a/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun-remote-resolver.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun-remote-resolver.json new file mode 100644 index 0000000000..e4fe52f6f9 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun-remote-resolver.json @@ -0,0 +1,113 @@ +{ + "metadata": { + "name": "build-push-gke-deploy-run-2", + "uid": "5e28bf07-ed8f-4fe2-bfa0-d1f2cb90e5de" + }, + "spec": { + "params": [ + { + "name": "pathToContext", + "value": "gke-deploy/example/app" + }, + { + "name": "pathToKubernetesConfigs", + "value": "gke-deploy/example/app/config" + } + ], + "pipelineRef": { + "params": [ + { + "name": "url", + "value": "https://github.com/tektoncd/catalog" + }, + { + "name": "revision", + "value": "main" + }, + { + "name": "pathInRepo", + "value": "pipeline/build-push-gke-deploy/0.1/build-push-gke-deploy.yaml" + } + ], + "resolver": "git" + }, + "taskRunTemplate": { + "serviceAccountName": "default" + } + }, + "status": { + "childReferences": [ + { + "apiVersion": "tekton.dev/v1", + "kind": "TaskRun", + "name": "build-push-gke-deploy-run-2-kaniko", + "pipelineTaskName": "kaniko" + } + ], + "conditions": [ + { + "lastTransitionTime": "2024-04-29T20:45:15Z", + "message": "Tasks Completed: 0 (Failed: 0, Cancelled 0), Incomplete: 2, Skipped: 0", + "reason": "Running", + "status": "Unknown", + "type": "Succeeded" + } + ], + "pipelineSpec": { + "description": "This Pipeline builds, pushes, and deploys your application to a Google Kubernetes Engine cluster using gke-deploy.", + "params": [ + { + "default": ".", + "description": "The path to the build context relative to your source repo's root. This is used by Kaniko.", + "name": "pathToContext", + "type": "string" + }, + { + "description": "The path to the Kubernetes configs to deploy, relative to your source repo's root.", + "name": "pathToKubernetesConfigs", + "type": "string" + } + ], + "tasks": [ + { + "name": "gke-deploy", + "params": [ + { + "name": "ARGS", + "value": [ + "run", + "--image=gcr.io/foo/gke-deploy-tekton-demo:1.0.0", + "--filename=$(workspaces.source.path)/gke-deploy/example/app/config" + ] + } + ], + "taskRef": { + "kind": "Task", + "name": "gke-deploy" + }, + "workspaces": [ + { + "name": "source", + "workspace": "source" + } + ] + } + ], + "workspaces": [ + { + "description": "The workspace containing the source code which is to be build, pushed and deployed", + "name": "source" + } + ] + }, + "provenance": { + "refSource": { + "digest": { + "sha1": "4df486f198c3c2616ab129186fb30a74f580b5a1" + }, + "entryPoint": "pipeline/build-push-gke-deploy/0.1/build-push-gke-deploy.yaml", + "uri": "git+https://github.com/tektoncd/catalog" + } + } + } +} diff --git a/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun1.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun1.json new file mode 100644 index 0000000000..9148fd450a --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun1.json @@ -0,0 +1,320 @@ +{ + "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" + } + } + ], + "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/pipelinerun_structured_results.json b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun_structured_results.json new file mode 100644 index 0000000000..dbaac82e33 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/slsa-v2alpha4/pipelinerun_structured_results.json @@ -0,0 +1,270 @@ +{ + "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": "image-ARTIFACT_INPUTS", + "value": { + "uri": "abcd", + "digest": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + } + }, + { + "name": "image-ARTIFACT_OUTPUTS", + "value": { + "uri": "hello_world", + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + } + } + ], + "pipelineSpec": { + "params": [ + { + "description": "Image path on registry", + "name": "IMAGE", + "type": "string" + } + ], + "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/v2alpha3/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go index 3c24410991..d453f0daed 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun.go @@ -18,12 +18,10 @@ import ( "encoding/json" "fmt" - intoto "github.com/in-toto/in-toto-golang/in_toto" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" - buildtypes "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/build_types" - externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" - internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" + 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" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" @@ -42,44 +40,14 @@ func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, return nil, err } - bd, err := getBuildDefinition(ctx, slsaconfig, pro) + bd, err := builddefinition.GetPipelineRunBuildDefinition(ctx, pro, slsaconfig, resolveddependencies.ResolveOptions{}) if err != nil { return nil, err } - att := intoto.ProvenanceStatementSLSA1{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: extract.SubjectDigests(ctx, pro, slsaconfig), - }, - Predicate: slsa.ProvenancePredicate{ - BuildDefinition: bd, - RunDetails: slsa.ProvenanceRunDetails{ - Builder: slsa.Builder{ - ID: slsaconfig.BuilderID, - }, - BuildMetadata: metadata(pro), - Byproducts: bp, - }, - }, - } - return att, nil -} + sub := extract.SubjectDigests(ctx, pro, slsaconfig) -func metadata(pro *objects.PipelineRunObjectV1) slsa.BuildMetadata { - m := slsa.BuildMetadata{ - InvocationID: string(pro.ObjectMeta.UID), - } - if pro.Status.StartTime != nil { - utc := pro.Status.StartTime.Time.UTC() - m.StartedOn = &utc - } - if pro.Status.CompletionTime != nil { - utc := pro.Status.CompletionTime.Time.UTC() - m.FinishedOn = &utc - } - return m + return provenance.GetSLSA1Statement(pro, sub, bd, bp, slsaconfig), nil } // byproducts contains the pipelineRunResults @@ -99,39 +67,3 @@ func byproducts(pro *objects.PipelineRunObjectV1) ([]slsa.ResourceDescriptor, er } return byProd, nil } - -// getBuildDefinition get the buildDefinition based on the configured buildType. This will default to the slsa buildType -func getBuildDefinition(ctx context.Context, slsaconfig *slsaconfig.SlsaConfig, pro *objects.PipelineRunObjectV1) (slsa.ProvenanceBuildDefinition, error) { - // if buildType is not set in the chains-config, default to slsa build type - buildDefinitionType := slsaconfig.BuildType - if slsaconfig.BuildType == "" { - buildDefinitionType = buildtypes.SlsaBuildType - } - - switch buildDefinitionType { - case buildtypes.SlsaBuildType: - rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveddependencies.AddSLSATaskDescriptor) - if err != nil { - return slsa.ProvenanceBuildDefinition{}, err - } - return slsa.ProvenanceBuildDefinition{ - BuildType: buildDefinitionType, - ExternalParameters: externalparameters.PipelineRun(pro), - InternalParameters: internalparameters.SLSAInternalParameters(pro), - ResolvedDependencies: rd, - }, nil - case buildtypes.TektonBuildType: - rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveddependencies.AddTektonTaskDescriptor) - if err != nil { - return slsa.ProvenanceBuildDefinition{}, err - } - return slsa.ProvenanceBuildDefinition{ - BuildType: buildDefinitionType, - ExternalParameters: externalparameters.PipelineRun(pro), - InternalParameters: internalparameters.TektonInternalParameters(pro), - ResolvedDependencies: rd, - }, nil - default: - return slsa.ProvenanceBuildDefinition{}, fmt.Errorf("unsupported buildType %v", buildDefinitionType) - } -} diff --git a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go index 229caf69de..5b3a617f6b 100644 --- a/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go +++ b/pkg/chains/formats/slsa/v2alpha3/internal/pipelinerun/pipelinerun_test.go @@ -17,7 +17,6 @@ limitations under the License. package pipelinerun import ( - "context" "encoding/json" "testing" "time" @@ -26,80 +25,14 @@ import ( "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" - v1resourcedescriptor "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" - externalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/external_parameters" - internalparameters "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/internal_parameters" - resolveddependencies "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/resolved_dependencies" "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/internal/objectloader" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logtesting "knative.dev/pkg/logging/testing" ) -func TestMetadata(t *testing.T) { - pr := &v1.PipelineRun{ //nolint:staticcheck - ObjectMeta: metav1.ObjectMeta{ - Name: "my-taskrun", - Namespace: "my-namespace", - Annotations: map[string]string{ - "chains.tekton.dev/reproducible": "true", - }, - UID: "abhhf-12354-asjsdbjs23-3435353n", - }, - Status: v1.PipelineRunStatus{ - PipelineRunStatusFields: v1.PipelineRunStatusFields{ - StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC)}, - CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC)}, - }, - }, - } - start := time.Date(1995, time.December, 24, 6, 12, 12, 12, time.UTC) - end := time.Date(1995, time.December, 24, 6, 12, 12, 24, time.UTC) - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, - } - got := metadata(objects.NewPipelineRunObjectV1(pr)) - if d := cmp.Diff(want, got); d != "" { - t.Fatalf("metadata (-want, +got):\n%s", d) - } -} - -func TestMetadataInTimeZone(t *testing.T) { - tz := time.FixedZone("Test Time", int((12 * time.Hour).Seconds())) - pr := &v1.PipelineRun{ //nolint:staticcheck - ObjectMeta: metav1.ObjectMeta{ - Name: "my-taskrun", - Namespace: "my-namespace", - Annotations: map[string]string{ - "chains.tekton.dev/reproducible": "true", - }, - UID: "abhhf-12354-asjsdbjs23-3435353n", - }, - Status: v1.PipelineRunStatus{ - PipelineRunStatusFields: v1.PipelineRunStatusFields{ - StartTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 12, tz)}, - CompletionTime: &metav1.Time{Time: time.Date(1995, time.December, 24, 6, 12, 12, 24, tz)}, - }, - }, - } - start := time.Date(1995, time.December, 24, 6, 12, 12, 12, tz).UTC() - end := time.Date(1995, time.December, 24, 6, 12, 12, 24, tz).UTC() - want := slsa.BuildMetadata{ - InvocationID: "abhhf-12354-asjsdbjs23-3435353n", - StartedOn: &start, - FinishedOn: &end, - } - got := metadata(objects.NewPipelineRunObjectV1(pr)) - if d := cmp.Diff(want, got); d != "" { - t.Fatalf("metadata (-want, +got):\n%s", d) - } -} - func TestByProducts(t *testing.T) { resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} pr := &v1.PipelineRun{ //nolint:staticcheck @@ -274,87 +207,3 @@ func TestGenerateAttestation(t *testing.T) { t.Errorf("GenerateAttestation(): -want +got: %s", diff) } } - -func getResolvedDependencies(addTasks func(*objects.TaskRunObjectV1) (*v1resourcedescriptor.ResourceDescriptor, error)) []v1resourcedescriptor.ResourceDescriptor { //nolint:staticcheck - pr := createPro("../../../testdata/slsa-v2alpha3/pipelinerun1.json") - rd, err := resolveddependencies.PipelineRun(context.Background(), pr, &slsaconfig.SlsaConfig{DeepInspectionEnabled: false}, addTasks) - if err != nil { - return []v1resourcedescriptor.ResourceDescriptor{} - } - return rd -} - -func TestGetBuildDefinition(t *testing.T) { - pr := createPro("../../../testdata/slsa-v2alpha3/pipelinerun1.json") - pr.Annotations = map[string]string{ - "annotation1": "annotation1", - } - pr.Labels = map[string]string{ - "label1": "label1", - } - tests := []struct { - name string - taskContent func(*objects.TaskRunObjectV1) (*v1resourcedescriptor.ResourceDescriptor, error) //nolint:staticcheck - config *slsaconfig.SlsaConfig - want slsa.ProvenanceBuildDefinition - }{ - { - name: "test slsa build type", - taskContent: resolveddependencies.AddSLSATaskDescriptor, - config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, - want: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.SLSAInternalParameters(pr), - ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddSLSATaskDescriptor), - }, - }, - { - name: "test tekton build type", - config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa-tekton"}, - taskContent: resolveddependencies.AddSLSATaskDescriptor, - want: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa-tekton", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.TektonInternalParameters(pr), - ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddTektonTaskDescriptor), - }, - }, - { - name: "test default build type", - config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"}, - taskContent: resolveddependencies.AddSLSATaskDescriptor, - want: slsa.ProvenanceBuildDefinition{ - BuildType: "https://tekton.dev/chains/v2/slsa", - ExternalParameters: externalparameters.PipelineRun(pr), - InternalParameters: internalparameters.SLSAInternalParameters(pr), - ResolvedDependencies: getResolvedDependencies(resolveddependencies.AddSLSATaskDescriptor), - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - bd, err := getBuildDefinition(context.Background(), tc.config, pr) - if err != nil { - t.Fatalf("Did not expect an error but got %v", err) - } - - if diff := cmp.Diff(tc.want, bd); diff != "" { - t.Errorf("getBuildDefinition(): -want +got: %v", diff) - } - }) - } -} - -func TestUnsupportedBuildType(t *testing.T) { - pr := createPro("../../../testdata/slsa-v2alpha3/pipelinerun1.json") - - got, err := getBuildDefinition(context.Background(), &slsaconfig.SlsaConfig{BuildType: "bad-buildtype"}, pr) - if err == nil { - t.Error("getBuildDefinition(): expected error got nil") - } - if diff := cmp.Diff(slsa.ProvenanceBuildDefinition{}, got); diff != "" { - t.Errorf("getBuildDefinition(): -want +got: %s", diff) - } -} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go new file mode 100644 index 0000000000..51cddbfcb4 --- /dev/null +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The Tekton Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pipelinerun + +import ( + "context" + + intoto "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/extract" + 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" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/results" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha4/internal/taskrun" + "github.com/tektoncd/chains/pkg/chains/objects" +) + +const ( + pipelineRunResults = "pipelineRunResults/%s" + // JsonMediaType is the media type of json encoded content used in resource descriptors + JsonMediaType = "application/json" +) + +// GenerateAttestation generates a provenance statement with SLSA v1.0 predicate for a pipeline run. +func GenerateAttestation(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) (interface{}, error) { + bp, err := byproducts(pro, slsaconfig) + if err != nil { + return nil, err + } + + opts := resolveddependencies.ResolveOptions{WithStepActionsResults: true} + bd, err := builddefinition.GetPipelineRunBuildDefinition(ctx, pro, slsaconfig, opts) + if err != nil { + return nil, err + } + + sub := subjectDigests(ctx, pro, slsaconfig) + + return provenance.GetSLSA1Statement(pro, sub, bd, bp, slsaconfig), nil +} + +// byproducts contains the pipelineRunResults +func byproducts(pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) ([]slsa.ResourceDescriptor, error) { + byProd := []slsa.ResourceDescriptor{} + + res, err := results.GetResultsWithoutBuildArtifacts(pro.GetResults(), pipelineRunResults) + if err != nil { + return nil, err + } + byProd = append(byProd, res...) + + if !slsaconfig.DeepInspectionEnabled { + return byProd, nil + } + + tros := pro.GetExecutedTasks() + + for _, tro := range tros { + taskProds, err := taskrun.ByProducts(tro) + if err != nil { + return nil, err + } + byProd = append(byProd, taskProds...) + } + + return byProd, nil +} + +func subjectDigests(ctx context.Context, pro *objects.PipelineRunObjectV1, slsaconfig *slsaconfig.SlsaConfig) []intoto.Subject { + results := pro.GetResults() + + if slsaconfig.DeepInspectionEnabled { + results = append(results, pro.GetNestedResults()...) + } + + return extract.SubjectsFromBuildArtifact(ctx, results) +} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go new file mode 100644 index 0000000000..34002296d0 --- /dev/null +++ b/pkg/chains/formats/slsa/v2alpha4/internal/pipelinerun/pipelinerun_test.go @@ -0,0 +1,209 @@ +/* +Copyright 2021 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pipelinerun + +import ( + "encoding/json" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/in-toto/in-toto-golang/in_toto" + "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" + slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/compare" + "github.com/tektoncd/chains/pkg/chains/formats/slsa/internal/slsaconfig" + "github.com/tektoncd/chains/pkg/chains/objects" + "github.com/tektoncd/chains/pkg/internal/objectloader" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + logtesting "knative.dev/pkg/logging/testing" +) + +func TestByProducts(t *testing.T) { + resultValue := v1.ResultValue{Type: "string", StringVal: "result-value"} + pr := &v1.PipelineRun{ //nolint:staticcheck + Status: v1.PipelineRunStatus{ + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + Results: []v1.PipelineRunResult{ + { + Name: "result-name", + Value: resultValue, + }, + }, + }, + }, + } + + resultBytes, err := json.Marshal(resultValue) + if err != nil { + t.Fatalf("Could not marshal results: %s", err) + } + want := []slsa.ResourceDescriptor{ + { + Name: "pipelineRunResults/result-name", + Content: resultBytes, + MediaType: JsonMediaType, + }, + } + got, err := byproducts(objects.NewPipelineRunObjectV1(pr), &slsaconfig.SlsaConfig{}) + if err != nil { + t.Fatalf("Could not extract byproducts: %s", err) + } + if d := cmp.Diff(want, got); d != "" { + t.Fatalf("byproducts (-want, +got):\n%s", d) + } +} + +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) + + want := in_toto.ProvenanceStatementSLSA1{ + StatementHeader: in_toto.StatementHeader{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []in_toto.Subject{ + { + Name: "abc", + Digest: common.DigestSet{ + "sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + }, + }, + Predicate: slsa.ProvenancePredicate{ + BuildDefinition: slsa.ProvenanceBuildDefinition{ + BuildType: "https://tekton.dev/chains/v2/slsa", + ExternalParameters: map[string]any{ + "runSpec": pr.Spec, + }, + InternalParameters: map[string]any{}, + ResolvedDependencies: []slsa.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"}, + }, + { + URI: "abc", + Digest: common.DigestSet{"sha256": "827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}, + Name: "inputs/result", + }, + {Name: "inputs/result", URI: "git+https://git.test.com.git", Digest: common.DigestSet{"sha1": "abcd"}}, + }, + }, + RunDetails: slsa.ProvenanceRunDetails{ + Builder: slsa.Builder{ + ID: "test_builder-1", + }, + BuildMetadata: slsa.BuildMetadata{ + InvocationID: "abhhf-12354-asjsdbjs23-3435353n", + StartedOn: &e1BuildStart, + FinishedOn: &e1BuildFinished, + }, + Byproducts: []slsa.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/IMAGE_URL", + Content: []uint8(`"test.io/test/image"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/IMAGE_DIGEST", + Content: []uint8(`"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img-ARTIFACT_INPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7","uri":"abc"}`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img2-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:","isBuildArtifact":"true","uri":"def"}`), + MediaType: JsonMediaType, + }, { + Name: "pipelineRunResults/img_no_uri-ARTIFACT_OUTPUTS", + Content: []uint8(`{"digest":"sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7"}`), + MediaType: JsonMediaType, + }, + }, + }, + }, + } + + got, err := GenerateAttestation(ctx, pr, &slsaconfig.SlsaConfig{ + BuilderID: "test_builder-1", + DeepInspectionEnabled: false, + BuildType: "https://tekton.dev/chains/v2/slsa", + }) + + if err != nil { + t.Errorf("unwant error: %s", err.Error()) + } + if diff := cmp.Diff(want, got, compare.SLSAV1CompareOptions()...); diff != "" { + t.Errorf("GenerateAttestation(): -want +got: %s", diff) + } +} + +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") + if err != nil { + panic(err) + } + p := objects.NewPipelineRunObjectV1(pr) + p.AppendTaskRun(tr1) + p.AppendTaskRun(tr2) + return p +} diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go index d702a52c95..298dbb1372 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun.go @@ -33,7 +33,7 @@ const ( // GenerateAttestation returns the provenance for the given taskrun in SALSA 1.0 format. func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsaConfig *slsaconfig.SlsaConfig) (interface{}, error) { - bp, err := byproducts(tro) + bp, err := ByProducts(tro) if err != nil { return nil, err } @@ -50,7 +50,7 @@ func GenerateAttestation(ctx context.Context, tro *objects.TaskRunObjectV1, slsa return provenance.GetSLSA1Statement(tro, sub, bd, bp, slsaConfig), nil } -func byproducts(tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { +func ByProducts(tro *objects.TaskRunObjectV1) ([]slsa.ResourceDescriptor, error) { byProd := []slsa.ResourceDescriptor{} res, err := results.GetResultsWithoutBuildArtifacts(tro.GetResults(), taskRunResults) diff --git a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go index 134dfad5eb..94285e19db 100644 --- a/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go +++ b/pkg/chains/formats/slsa/v2alpha4/internal/taskrun/taskrun_test.go @@ -66,7 +66,7 @@ func TestByProducts(t *testing.T) { MediaType: jsonMediaType, }, } - got, err := byproducts(objects.NewTaskRunObjectV1(tr)) + got, err := ByProducts(objects.NewTaskRunObjectV1(tr)) if err != nil { t.Fatalf("Could not extract byproducts: %s", err) } diff --git a/pkg/chains/formats/slsa/v2alpha4/slsav2.go b/pkg/chains/formats/slsa/v2alpha4/slsav2.go index 59312139d7..f0c0a8f9ce 100644 --- a/pkg/chains/formats/slsa/v2alpha4/slsav2.go +++ b/pkg/chains/formats/slsa/v2alpha4/slsav2.go @@ -22,6 +22,7 @@ import ( "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" "github.com/tektoncd/chains/pkg/chains/formats/slsa/v2alpha4/internal/taskrun" "github.com/tektoncd/chains/pkg/chains/objects" @@ -58,6 +59,8 @@ func (s *Slsa) CreatePayload(ctx context.Context, obj interface{}) (interface{}, switch v := obj.(type) { case *objects.TaskRunObjectV1: return taskrun.GenerateAttestation(ctx, v, s.slsaConfig) + case *objects.PipelineRunObjectV1: + return pipelinerun.GenerateAttestation(ctx, v, s.slsaConfig) default: return nil, fmt.Errorf("intoto does not support type: %s", v) } diff --git a/pkg/chains/objects/objects.go b/pkg/chains/objects/objects.go index 4ad2244ed9..6a52e41e3b 100644 --- a/pkg/chains/objects/objects.go +++ b/pkg/chains/objects/objects.go @@ -296,8 +296,13 @@ func (pro *PipelineRunObjectV1) GetResults() []Result { } // GetNestedResults returns the results of the associated TaskRuns of the PipelineRun. -func (pro *PipelineRunObjectV1) GetNestedResults() []Result { - return []Result{} +func (pro *PipelineRunObjectV1) GetNestedResults() (results []Result) { + execTasks := pro.GetExecutedTasks() + for _, task := range execTasks { + results = append(results, task.GetResults()...) + results = append(results, task.GetNestedResults()...) + } + return } // Get the ServiceAccount declared in the PipelineRun @@ -385,6 +390,26 @@ func (pro *PipelineRunObjectV1) GetCompletitionTime() *time.Time { return utc } +func (pro *PipelineRunObjectV1) GetExecutedTasks() (tro []*TaskRunObjectV1) { + pSpec := pro.Status.PipelineSpec + if pSpec == nil { + return + } + + tasks := append(pSpec.Tasks, pSpec.Finally...) + for _, task := range tasks { + tr := pro.GetTaskRunFromTask(task.Name) + + if tr == nil || tr.Status.CompletionTime == nil { + continue + } + + tro = append(tro, tr) + } + + return +} + // Get the imgPullSecrets from a pod template, if they exist func getPodPullSecrets(podTemplate *pod.Template) []string { imgPullSecrets := []string{} diff --git a/pkg/config/config.go b/pkg/config/config.go index c18cc9af2c..859d57c211 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -271,7 +271,7 @@ func NewConfigFromMap(data map[string]string) (*Config, error) { asString(taskrunSignerKey, &cfg.Artifacts.TaskRuns.Signer, "x509", "kms"), // PipelineRuns - asString(pipelinerunFormatKey, &cfg.Artifacts.PipelineRuns.Format, "in-toto", "slsa/v1", "slsa/v2alpha2", "slsa/v2alpha3"), + asString(pipelinerunFormatKey, &cfg.Artifacts.PipelineRuns.Format, "in-toto", "slsa/v1", "slsa/v2alpha2", "slsa/v2alpha3", "slsa/v2alpha4"), asStringSet(pipelinerunStorageKey, &cfg.Artifacts.PipelineRuns.StorageBackend, sets.New[string]("tekton", "oci", "gcs", "docdb", "grafeas")), asString(pipelinerunSignerKey, &cfg.Artifacts.PipelineRuns.Signer, "x509", "kms"), asBool(pipelinerunEnableDeepInspectionKey, &cfg.Artifacts.PipelineRuns.DeepInspectionEnabled), diff --git a/test/examples_test.go b/test/examples_test.go index f413370323..c2b9a30a27 100644 --- a/test/examples_test.go +++ b/test/examples_test.go @@ -183,6 +183,33 @@ func TestExamples(t *testing.T) { outputLocation: "slsa/v2alpha4", predicate: "slsav1.0", }, + { + name: "pipelinerun-examples-slsa-v2alpha4", + cm: map[string]string{ + "artifacts.pipelinerun.format": "slsa/v2alpha4", + "artifacts.oci.storage": "tekton", + }, + getExampleObjects: getPipelineRunExamples, + payloadKey: "chains.tekton.dev/payload-pipelinerun-%s", + signatureKey: "chains.tekton.dev/signature-pipelinerun-%s", + outputLocation: "slsa/v2alpha4", + predicate: "slsav1.0", + }, + { + name: "pipelinerun-type-hinted-results-v2alpha4", + cm: map[string]string{ + "artifacts.pipelinerun.format": "slsa/v2alpha4", + "artifacts.oci.storage": "tekton", + }, + pipelinesCm: map[string]string{ + "enable-api-fields": "alpha", + }, + getExampleObjects: getPipelineRunWithTypeHintedResultsExamples, + payloadKey: "chains.tekton.dev/payload-pipelinerun-%s", + signatureKey: "chains.tekton.dev/signature-pipelinerun-%s", + outputLocation: "slsa/v2alpha4", + predicate: "slsav1.0", + }, } for _, test := range tests { @@ -496,6 +523,13 @@ func getTaskRunWithTypeHintedResultsExamples(t *testing.T, ns string) map[string return trs } +func getPipelineRunWithTypeHintedResultsExamples(t *testing.T, ns string) map[string]objects.TektonObject { + path := "../examples/v2alpha4/pipeline-with-object-type-hinting.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 { examples := make(map[string]objects.TektonObject) for _, example := range getExamplePaths(t, pipelineRunExamplesPath) { diff --git a/test/testdata/slsa/v2alpha4/pipeline-output-image.json b/test/testdata/slsa/v2alpha4/pipeline-output-image.json new file mode 100644 index 0000000000..d4f02032d7 --- /dev/null +++ b/test/testdata/slsa/v2alpha4/pipeline-output-image.json @@ -0,0 +1,119 @@ +{ + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v1", + "subject": null, + "predicate": { + "buildDefinition": { + "buildType": "https://tekton.dev/chains/v2/slsa", + "externalParameters": { + "runSpec": { + "taskRunTemplate": { + "serviceAccountName": "default" + }, + "params": [ + { + "name": "CHAINS-GIT_COMMIT", + "value": "my-git-commit" + },{ + "name": "CHAINS-GIT_URL", + "value": "https://my-git-url" + } + ], + "pipelineSpec": { + "results": [ + { + "name": "IMAGE_URL", + "description": "", + "value": "$(tasks.buildimage.results.IMAGE_URL)" + }, + { + "name": "IMAGE_DIGEST", + "description": "", + "value": "$(tasks.buildimage.results.IMAGE_DIGEST)" + } + ], + "tasks": [ + { + "name": "buildimage", + "taskSpec": { + "metadata": {}, + "steps": [ + { + "name": "create-dockerfile", + "image": "distroless.dev/busybox@sha256:186312fcf3f381b5fc1dd80b1afc0d316f3ed39fb4add8ff900d1f0c7c49a92c", + "computeResources": {}, + "script": "#!/usr/bin/env sh\necho 'gcr.io/foo/bar' | tee $(results.IMAGE_URL.path)\necho 'sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5' | tee $(results.IMAGE_DIGEST.path)", + "volumeMounts": [ + { + "mountPath": "/dockerfile", + "name": "dockerfile" + } + ] + } + ], + "spec": null, + "results": [ + { + "name": "IMAGE_URL", + "type": "string" + },{ + "name": "IMAGE_DIGEST", + "type": "string" + } + ], + "volumes": [ + { + "emptyDir": {}, + "name": "dockerfile" + } + ] + + } + } + ] + }, + "timeouts": { + "pipeline": "1h0m0s" + } + } + }, + "resolvedDependencies": [ + {{range .URIDigest}} + { + "uri": "{{.URI}}", + "digest": { + "sha256": "{{.Digest}}" + } + }, + {{end}} + { + "uri": "git+https://my-git-url.git", + "digest": {"sha1": "my-git-commit"}, + "name": "inputs/result" + } + ] + }, + "runDetails": { + "builder": { + "id": "https://tekton.dev/chains/v2" + }, + "metadata": { + "invocationID": "{{.UID}}", + "startedOn": "{{.PipelineStartedOn}}", + "finishedOn": "{{.PipelineFinishedOn}}" + }, + "byproducts": [ + { + "name": "pipelineRunResults/IMAGE_URL", + "mediaType": "application/json", + "content": "Imdjci5pby9mb28vYmFyXG4i" + }, + { + "name": "pipelineRunResults/IMAGE_DIGEST", + "mediaType": "application/json", + "content": "InNoYTI1NjowNWY5NWIyNmVkMTA2NjhiNzE4M2MxZTJkYTk4NjEwZTkxMzcyZmE5ZjUxMDA0NmQ0Y2U1ODEyYWRkYWQ4NmI1XG4i" + } + ] + } + } +} diff --git a/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json new file mode 100644 index 0000000000..d99cc28e9a --- /dev/null +++ b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json @@ -0,0 +1,139 @@ +{ + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v1", + "subject": [ + { + "name": "gcr.io/foo/img1", + "digest": { + "sha256": "586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee" + } + } + ], + "predicate": { + "buildDefinition": { + "buildType": "https://tekton.dev/chains/v2/slsa", + "externalParameters": { + "runSpec": { + "pipelineSpec": { + "tasks": [ + { + "name": "t1", + "taskSpec": { + "spec": null, + "metadata": {}, + "steps": [ + { + "name": "step1", + "image": "busybox:glibc", + "computeResources": {}, + "script": "echo -n \"Hello!\"\necho -n \"{\\\"uri\\\":\\\"gcr.io/foo/img1\\\", \\\"digest\\\":\\\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\\\", \\\"isBuildArtifact\\\": \\\"true\\\" }\" \u003e $(results.output1.path)\necho -n \"{\\\"uri\\\":\\\"gcr.io/foo/img2\\\", \\\"digest\\\":\\\"sha256:586789aa031fafc7d78a5393cdc772e0b55107ea54bb8bcf3f2cdac6c6da51ee\\\"}\" \u003e $(results.output2.path)" + } + ], + "results": [ + { + "name": "output1", + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "isBuildArtifact": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + { + "name": "output2", + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + } + ] + } + } + ], + "results": [ + { + "name": "output1-ARTIFACT_OUTPUTS", + "description": "", + "value": "$(tasks.t1.results.output1)" + }, + { + "name": "output2-ARTIFACT_OUTPUTS", + "description": "", + "value": "$(tasks.t1.results.output2)" + } + ] + }, + "timeouts": { + "pipeline": "1h0m0s" + }, + "taskRunTemplate": { + "serviceAccountName": "default" + } + } + }, + "internalParameters": { + "tekton-pipelines-feature-flags": { + "DisableAffinityAssistant": false, + "DisableCredsInit": false, + "RunningInEnvWithInjectedSidecars": true, + "RequireGitSSHSecretKnownHosts": false, + "EnableTektonOCIBundles": false, + "ScopeWhenExpressionsToTask": false, + "EnableAPIFields": "beta", + "SendCloudEventsForRuns": false, + "AwaitSidecarReadiness": true, + "EnforceNonfalsifiability": "none", + "EnableKeepPodOnCancel": false, + "VerificationNoMatchPolicy": "ignore", + "EnableProvenanceInStatus": true, + "ResultExtractionMethod": "termination-message", + "MaxResultSize": 4096, + "SetSecurityContext": false, + "Coschedule": "workspaces", + "EnableCELInWhenExpression": false, + "EnableStepActions": true, + "EnableParamEnum": false, + "EnableArtifacts": false + } + }, + "resolvedDependencies": [ + {{range .URIDigest}} + { + "uri": "{{.URI}}", + "digest": { + "sha256": "{{.Digest}}" + } + } + {{end}} + ] + }, + "runDetails": { + "builder": { + "id": "https://tekton.dev/chains/v2" + }, + "metadata": { + "invocationID": "{{.UID}}", + "startedOn": "{{.PipelineStartedOn}}", + "finishedOn": "{{.PipelineFinishedOn}}" + }, + "byproducts": [ + { + "name": "pipelineRunResults/output2-ARTIFACT_OUTPUTS", + "mediaType": "application/json", + "content": "eyJkaWdlc3QiOiJzaGEyNTY6NTg2Nzg5YWEwMzFmYWZjN2Q3OGE1MzkzY2RjNzcyZTBiNTUxMDdlYTU0YmI4YmNmM2YyY2RhYzZjNmRhNTFlZSIsInVyaSI6Imdjci5pby9mb28vaW1nMiJ9" + } + ] + } + } +} \ No newline at end of file