diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6366d88723..152cc073d4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,3 +10,17 @@ updates: all: patterns: - "*" + labels: + - "ok-to-test" + - "dependencies" + - "release-note-none" + - "kind/misc" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "ok-to-test" + - "dependencies" + - "release-note-none" + - "kind/misc" diff --git a/.github/workflows/goclean.yml b/.github/workflows/goclean.yml index 89af75b5b3..23feae022a 100644 --- a/.github/workflows/goclean.yml +++ b/.github/workflows/goclean.yml @@ -24,10 +24,10 @@ jobs: goclean: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 # check-out repository + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: '1.22' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fd7a604c45..a449f23464 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -17,14 +17,14 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v4 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: "1.22" - - uses: actions/checkout@v3 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v6.1.0 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest diff --git a/.github/workflows/reusable-e2e.yaml b/.github/workflows/reusable-e2e.yaml index bf0da78ceb..7a18e297eb 100644 --- a/.github/workflows/reusable-e2e.yaml +++ b/.github/workflows/reusable-e2e.yaml @@ -34,26 +34,12 @@ jobs: TEKTON_CLI_RELEASE: "0.30.0" steps: - # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds - - uses: actions/cache@v2 - with: - # In order: - # * Module download cache - # * Build cache (Linux) - path: | - ~/go/pkg/mod - ~/.cache/go-build - ${{ env.KOCACHE }} - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: 1.22.x - - uses: imjasonh/setup-ko@v0.6 + - uses: ko-build/setup-ko@v0.7 with: version: tip @@ -64,7 +50,7 @@ jobs: chmod u+x ./tkn - name: Check out our repo - uses: actions/checkout@v2 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: path: ./src/github.com/tektoncd/chains @@ -140,7 +126,7 @@ jobs: - name: Collect diagnostics if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main + uses: chainguard-dev/actions/kind-diag@9ba949ac63357c725a9438f3e05a1e33d313498e # main with: cluster-resources: nodes namespace-resources: pods,taskruns,jobs diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun4.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun4.json new file mode 100644 index 0000000000..7b7a4b97d3 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun4.json @@ -0,0 +1,136 @@ +{ + "metadata": { + "name": "mismatch Status.Step.Name and Status.TaskSpec.Step.Name", + "labels": { + "tekton.dev/pipelineTask": "build" + } + }, + "spec": { + "params": [ + { + "name": "IMAGE", + "value": "test.io/test/image" + }, + { + "name": "CHAINS-GIT_COMMIT", + "value": "sha:taskrun" + }, + { + "name": "CHAINS-GIT_URL", + "value": "https://git.test.com" + } + ], + "taskRef": { + "name": "build", + "kind": "Task" + }, + "serviceAccountName": "default" + }, + "status": { + "startTime": "2021-03-29T09:50:00Z", + "completionTime": "2021-03-29T09:50:15Z", + "conditions": [ + { + "type": "Succeeded", + "status": "True", + "lastTransitionTime": "2021-03-29T09:50:15Z", + "reason": "Succeeded", + "message": "All Steps have completed executing" + } + ], + "podName": "test-pod-name", + "steps": [ + { + "name": "unnamed-", + "container": "step-step1", + "imageID": "docker-pullable://gcr.io/test7/test7@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6" + }, + { + "name": "step2", + "container": "step-step2", + "imageID": "docker-pullable://gcr.io/test8/test8@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac" + }, + { + "name": "step3", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test9/test9@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478" + } + ], + "taskResults": [ + { + "name": "IMAGE_DIGEST", + "value": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + }, + { + "name": "IMAGE_URL", + "value": "gcr.io/my/image" + } + ], + "taskSpec": { + "params": [ + { + "name": "IMAGE", + "type": "string" + }, + { + "name": "filename", + "type": "string" + }, + { + "name": "DOCKERFILE", + "type": "string" + }, + { + "name": "CONTEXT", + "type": "string" + }, + { + "name": "EXTRA_ARGS", + "type": "string" + }, + { + "name": "BUILDER_IMAGE", + "type": "string" + }, { + "name": "CHAINS-GIT_COMMIT", + "type": "string", + "default": "sha:task" + }, { + "name": "CHAINS-GIT_URL", + "type": "string", + "default": "https://defaultgit.test.com" + } + ], + "steps": [ + { + "name": "step1" + }, + { + "name": "step2" + }, + { + "name": "step3" + } + ], + "results": [ + { + "name": "IMAGE_DIGEST", + "description": "Digest of the image just built." + }, + { + "name": "filename_DIGEST", + "description": "Digest of the file just built." + } + ] + }, + "provenance": { + "refSource": { + "uri": "github.com/test", + "digest": { + "sha1": "ab123" + }, + "entryPoint": "build.yaml" + } + } + } +} diff --git a/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun5.json b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun5.json new file mode 100644 index 0000000000..86c4702c29 --- /dev/null +++ b/pkg/chains/formats/slsa/testdata/pipeline-v1beta1/taskrun5.json @@ -0,0 +1,141 @@ +{ + "metadata": { + "name": "mismatch size between Status.Steps and Status.TaskSpec.Steps", + "labels": { + "tekton.dev/pipelineTask": "build" + } + }, + "spec": { + "params": [ + { + "name": "IMAGE", + "value": "test.io/test/image" + }, + { + "name": "CHAINS-GIT_COMMIT", + "value": "sha:taskrun" + }, + { + "name": "CHAINS-GIT_URL", + "value": "https://git.test.com" + } + ], + "taskRef": { + "name": "build", + "kind": "Task" + }, + "serviceAccountName": "default" + }, + "status": { + "startTime": "2021-03-29T09:50:00Z", + "completionTime": "2021-03-29T09:50:15Z", + "conditions": [ + { + "type": "Succeeded", + "status": "True", + "lastTransitionTime": "2021-03-29T09:50:15Z", + "reason": "Succeeded", + "message": "All Steps have completed executing" + } + ], + "podName": "test-pod-name", + "steps": [ + { + "name": "step1", + "container": "step-step1", + "imageID": "docker-pullable://gcr.io/test10/test10@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6" + }, + { + "name": "step2", + "container": "step-step2", + "imageID": "docker-pullable://gcr.io/test11/test11@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac" + }, + { + "name": "step3", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test12/test12@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478" + }, + { + "name": "step4", + "container": "step-step3", + "imageID": "docker-pullable://gcr.io/test13/test13@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478" + } + ], + "taskResults": [ + { + "name": "IMAGE_DIGEST", + "value": "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7" + }, + { + "name": "IMAGE_URL", + "value": "gcr.io/my/image" + } + ], + "taskSpec": { + "params": [ + { + "name": "IMAGE", + "type": "string" + }, + { + "name": "filename", + "type": "string" + }, + { + "name": "DOCKERFILE", + "type": "string" + }, + { + "name": "CONTEXT", + "type": "string" + }, + { + "name": "EXTRA_ARGS", + "type": "string" + }, + { + "name": "BUILDER_IMAGE", + "type": "string" + }, { + "name": "CHAINS-GIT_COMMIT", + "type": "string", + "default": "sha:task" + }, { + "name": "CHAINS-GIT_URL", + "type": "string", + "default": "https://defaultgit.test.com" + } + ], + "steps": [ + { + "name": "step1" + }, + { + "name": "step2" + }, + { + "name": "step3" + } + ], + "results": [ + { + "name": "IMAGE_DIGEST", + "description": "Digest of the image just built." + }, + { + "name": "filename_DIGEST", + "description": "Digest of the file just built." + } + ] + }, + "provenance": { + "refSource": { + "uri": "github.com/test", + "digest": { + "sha1": "ab123" + }, + "entryPoint": "build.yaml" + } + } + } +} diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go index 363658277f..7329a615d4 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/pipelinerun.go @@ -15,6 +15,7 @@ package pipelinerun import ( "context" + "strings" "time" intoto "github.com/in-toto/attestation/go/v1" @@ -112,14 +113,39 @@ func buildConfig(ctx context.Context, pro *objects.PipelineRunObjectV1Beta1) Bui for _, tr := range taskRuns { // Ignore Tasks that did not execute during the PipelineRun. if tr.Status.CompletionTime == nil { - logger.Infof("taskrun status not complete for task %s", tr.Name) + logger.Warnf("taskrun status not complete for task %s", tr.Name) continue } steps := []attest.StepAttestation{} - for i, stepState := range tr.Status.Steps { - step := tr.Status.TaskSpec.Steps[i] - steps = append(steps, attest.Step(&step, &stepState)) + // tr.Status.TaskSpec.Steps and tr.Status.Steps should be sime size + if len(tr.Status.TaskSpec.Steps) != len(tr.Status.Steps) { + logger.Errorf("Mismatch in number of steps for task run %s. TaskSpec steps: %d, Status steps: %d", + tr.Name, len(tr.Status.TaskSpec.Steps), len(tr.Status.Steps)) + continue // Skip this task run entirely + } + + // Validate and process steps + valid := true + for i, step := range tr.Status.TaskSpec.Steps { + stepState := tr.Status.Steps[i] + + // Check if unnamed step matches empty name in the other list + if strings.HasPrefix(stepState.Name, "unnamed-") && step.Name != "" { + logger.Errorf("Mismatch in step names for task run %s. Step %d: %s, StepState %d: %s", + tr.Name, i, step.Name, i, stepState.Name) + valid = false + break + } + + if valid { + steps = append(steps, attest.Step(&step, &stepState)) + } + } + + if !valid { + logger.Errorf("Skipping task run %s due to step name mismatch", tr.Name) + continue } after := t.RunAfter diff --git a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go index 3c08ef6f0f..c35a300f55 100644 --- a/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go +++ b/pkg/chains/formats/slsa/v1/pipelinerun/provenance_test.go @@ -90,6 +90,7 @@ func TestInvocation(t *testing.T) { } func TestBuildConfig(t *testing.T) { + //nolint:dupl expected := BuildConfig{ Tasks: []TaskAttestation{ { @@ -302,6 +303,230 @@ func TestBuildConfig(t *testing.T) { } } +func TestBuildConfigMismatchStatusStepsAndTaskSpec(t *testing.T) { + //nolint:dupl + expected := BuildConfig{ + Tasks: []TaskAttestation{ + { + Name: "git-clone", + After: nil, + Ref: v1beta1.TaskRef{ + Name: "git-clone", + Kind: "ClusterTask", + }, + ServiceAccountName: "pipeline", + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "git clone", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "container": "step1", + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + }, + Annotations: nil, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/catalog", + Digest: common.DigestSet{"sha1": "x123"}, + EntryPoint: "git-clone.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskdefault"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "revision": {Type: "string", StringVal: ""}, + "url": {Type: "string", StringVal: "https://git.test.com"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "git-clone"}, + }, + }, + Results: []v1beta1.TaskRunResult{ + { + Name: "some-uri_DIGEST", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + }, + }, + { + Name: "some-uri", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "pkg:deb/debian/curl@7.50.3-1", + }, + }, + }, + }, + //nolint:dupl + { + Name: "build", + After: []string{"git-clone"}, + Ref: v1beta1.TaskRef{ + Name: "build", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + ServiceAccountName: "pipeline", + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test1/test1@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "container": "step1", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test2/test2@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "container": "step2", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test3/test3@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", + "container": "step3", + }, + Annotations: nil, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/test", + Digest: map[string]string{"sha1": "ab123"}, + EntryPoint: "build.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "build"}, + }, + }, + Results: []v1beta1.TaskRunResult{ + { + Name: "IMAGE_DIGEST", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "IMAGE_URL", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "gcr.io/my/image", + }, + }, + }, + }, + //nolint:dupl + { + Name: "build", + After: []string{"git-clone"}, + Ref: v1beta1.TaskRef{ + Name: "build", + Kind: "ClusterTask", + }, + StartedOn: e1BuildStart, + FinishedOn: e1BuildFinished, + ServiceAccountName: "pipeline", + Status: "Succeeded", + Steps: []attest.StepAttestation{ + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test4/test4@sha256:d4b63d3e24d6eef04a6dc0795cf8a73470688803d97c52cffa3c8d4efd3397b6", + "container": "step1", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test5/test5@sha256:4d6dd704ef58cb214dd826519929e92a978a57cdee43693006139c0080fd6fac", + "container": "step2", + }, + Annotations: nil, + }, + { + EntryPoint: "", + Arguments: []string(nil), + Environment: map[string]interface{}{ + "image": artifacts.OCIScheme + "gcr.io/test6/test6@sha256:f1a8b8549c179f41e27ff3db0fe1a1793e4b109da46586501a8343637b1d0478", + "container": "step3", + }, + Annotations: nil, + }, + }, + Invocation: slsa.ProvenanceInvocation{ + ConfigSource: slsa.ConfigSource{ + URI: "github.com/test", + Digest: map[string]string{"sha1": "ab123"}, + EntryPoint: "build.yaml", + }, + Parameters: map[string]v1beta1.ParamValue{ + "CHAINS-GIT_COMMIT": {Type: "string", StringVal: "sha:taskrun"}, + "CHAINS-GIT_URL": {Type: "string", StringVal: "https://git.test.com"}, + "IMAGE": {Type: "string", StringVal: "test.io/test/image"}, + }, + Environment: map[string]map[string]string{ + "labels": {"tekton.dev/pipelineTask": "build"}, + }, + }, + Results: []v1beta1.TaskRunResult{ + { + Name: "IMAGE_DIGEST", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "sha256:827521c857fdcd4374f4da5442fbae2edb01e7fbae285c3ec15673d4c1daecb7", + }, + }, + { + Name: "IMAGE_URL", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "gcr.io/my/image", + }, + }, + }, + }, + }, + } + ctx := logtesting.TestContextWithLogger(t) + tr4, err := objectloader.TaskRunV1Beta1FromFile("../../testdata/pipeline-v1beta1/taskrun4.json") + if err != nil { + panic(err) + } + tr5, err := objectloader.TaskRunV1Beta1FromFile("../../testdata/pipeline-v1beta1/taskrun5.json") + if err != nil { + panic(err) + } + pro.AppendTaskRun(tr4) + pro.AppendTaskRun(tr5) + got := buildConfig(ctx, pro) + if diff := cmp.Diff(expected, got); diff != "" { + t.Errorf("buildConfig(): -want +got: %s", diff) + } +} + func TestBuildConfigTaskOrder(t *testing.T) { BUILD_TASK := 1 tests := []struct {