Skip to content

Commit

Permalink
Add new v2alpha4 version for PipelineRuns
Browse files Browse the repository at this point in the history
This new version will now process the information from any associated StepAction from the executed PipelineRun when `artifacts.pipelinerun.enable-deep-inspection` is set to `true`.

Also, the way chains read results from PipelineRuns to populate the `subjects` field is changing: now the user has to explicitly mark a result as a subject using an object type-hinted tag (*ARTIFACT_OUTPUTS) + the new `isBuildArtifact` property in the value.

Refactors to share logic between v2alph3 and v2alpha4.
  • Loading branch information
renzodavid9 committed May 21, 2024
1 parent 8e9373e commit 2586135
Show file tree
Hide file tree
Showing 22 changed files with 1,750 additions and 333 deletions.
3 changes: 2 additions & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Supported keys include:

| Key | Description | Supported Values | Default |
| :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------- | :-------- |
| `artifacts.pipelinerun.format` | The format to store `PipelineRun` payloads in. | `in-toto`, `slsa/v1`, `slsa/v2alpha3` | `in-toto` |
| `artifacts.pipelinerun.format` | The format to store `PipelineRun` payloads in. | `in-toto`, `slsa/v1`, `slsa/v2alpha3`, `slsa/v2alpha4` | `in-toto` |
| `artifacts.pipelinerun.storage` | The storage backend to store `PipelineRun` signatures in. Multiple backends can be specified with comma-separated list ("tekton,oci"). To disable the `PipelineRun` artifact input an empty string (""). | `tekton`, `oci`, `gcs`, `docdb`, `grafeas` | `tekton` |
| `artifacts.pipelinerun.signer` | The signature backend to sign `PipelineRun` payloads with. | `x509`, `kms` | `x509` |
| `artifacts.pipelinerun.enable-deep-inspection` | This boolean option will configure whether Chains should inspect child taskruns in order to capture inputs/outputs within a pipelinerun. `"false"` means that Chains only checks pipeline level results, whereas `"true"` means Chains inspects both pipeline level and task level results. | `"true"`, `"false"` | `"false"` |
Expand All @@ -45,6 +45,7 @@ Supported keys include:
> - For grafeas storage backend, currently we only support Container Analysis. We will make grafeas server address configurabe within a short time.
> - `slsa/v1` is an alias of `in-toto` for backwards compatibility.
> - `slsa/v2alpha3` corresponds to the slsav1.0 spec. and uses latest [`v1` Tekton Objects](https://tekton.dev/docs/pipelines/pipeline-api/#tekton.dev/v1). Recommended format for new chains users who want the slsav1.0 spec.
> - `slsa/v2alpha4` corresponds to the slsav1.0 spec. and uses latest [`v1` Tekton Objects](https://tekton.dev/docs/pipelines/pipeline-api/#tekton.dev/v1). It reads type-hinted results from [StepActions](https://tekton.dev/docs/pipelines/pipeline-api/#tekton.dev/v1alpha1.StepAction) when `artifacts.pipelinerun.enable-deep-inspection` is set to `true`. Recommended format for new chains users who want the slsav1.0 spec.

### OCI Configuration
Expand Down
35 changes: 35 additions & 0 deletions examples/v2alpha4/pipeline-with-object-type-hinting.yaml
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
Expand Down Expand Up @@ -62,6 +63,46 @@ func GetTaskRunBuildDefinition(ctx context.Context, tro *objects.TaskRunObjectV1
}, nil
}

// GetPipelineRunBuildDefinition 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.BuildDefinition, error) {
buildDefinitionType := slsaconfig.BuildType
if slsaconfig.BuildType == "" {
buildDefinitionType = buildtypes.SlsaBuildType
}

td, err := resolveddependencies.GetTaskDescriptor(buildDefinitionType)
if err != nil {
return slsa.BuildDefinition{}, err
}

rd, err := resolveddependencies.PipelineRun(ctx, pro, slsaconfig, resolveOpts, td)
if err != nil {
return slsa.BuildDefinition{}, err
}

externalParams := externalparameters.PipelineRun(pro)
structExternalParams, err := getStruct(externalParams)
if err != nil {
return slsa.BuildDefinition{}, err
}

internalParams, err := internalparameters.GetInternalParamters(pro, buildDefinitionType)
if err != nil {
return slsa.BuildDefinition{}, err
}
structInternalParams, err := getStruct(internalParams)
if err != nil {
return slsa.BuildDefinition{}, err
}

return slsa.BuildDefinition{
BuildType: buildDefinitionType,
ExternalParameters: structExternalParams,
InternalParameters: structInternalParams,
ResolvedDependencies: rd,
}, nil
}

func getStruct(data map[string]any) (*structpb.Struct, error) {
bytes, err := json.Marshal(data)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ import (

"github.com/google/go-cmp/cmp"
slsa "github.com/in-toto/attestation/go/predicates/provenance/v1"
intoto "github.com/in-toto/attestation/go/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"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
)

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)
Expand Down Expand Up @@ -103,7 +105,7 @@ func TestGetBuildDefinition(t *testing.T) {
}
}

func TestUnsupportedBuildType(t *testing.T) {
func TestTaskRunUnsupportedBuildType(t *testing.T) {
tr, err := objectloader.TaskRunFromFile("../../testdata/slsa-v2alpha4/taskrun1.json")
if err != nil {
t.Fatal(err)
Expand All @@ -127,3 +129,102 @@ func getProtoStruct(t *testing.T, data map[string]any) *structpb.Struct {

return protoStruct
}

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.BuildDefinition
}{
{
name: "test slsa build type",
config: &slsaconfig.SlsaConfig{BuildType: "https://tekton.dev/chains/v2/slsa"},
want: slsa.BuildDefinition{
BuildType: "https://tekton.dev/chains/v2/slsa",
ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)),
InternalParameters: getProtoStruct(t, 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.BuildDefinition{
BuildType: "https://tekton.dev/chains/v2/slsa-tekton",
ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)),
InternalParameters: getProtoStruct(t, 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.BuildDefinition{
BuildType: "https://tekton.dev/chains/v2/slsa",
ExternalParameters: getProtoStruct(t, externalparameters.PipelineRun(pr)),
InternalParameters: getProtoStruct(t, internalparameters.SLSAInternalParameters(pr)),
ResolvedDependencies: getResolvedDependencies(pr, resolveddependencies.AddSLSATaskDescriptor),
},
},
}

for i := range tests {
tc := &tests[i]
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, protocmp.Transform()); 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) (*intoto.ResourceDescriptor, error)) []*intoto.ResourceDescriptor {
rd, err := resolveddependencies.PipelineRun(context.Background(), pr, &slsaconfig.SlsaConfig{}, resolveddependencies.ResolveOptions{}, addTasks)
if err != nil {
return []*intoto.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.BuildDefinition{}, &got, protocmp.Transform()); diff != "" {
t.Errorf("getBuildDefinition(): -want +got: %s", diff)
}
}
Loading

0 comments on commit 2586135

Please sign in to comment.