Skip to content

Commit

Permalink
Merge pull request #811 from zregvart/issue/EC-277
Browse files Browse the repository at this point in the history
Support more than one git-clone and build task
  • Loading branch information
zregvart authored Nov 28, 2023
2 parents 22ed931 + fab6481 commit b861394
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 37 deletions.
9 changes: 7 additions & 2 deletions policy/lib/tekton/pipeline.rego
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import future.keywords.contains
import future.keywords.if
import future.keywords.in

import data.lib
import data.lib.time as ectime

pipeline_label := "pipelines.openshift.io/runtime"
Expand Down Expand Up @@ -33,12 +34,16 @@ pipeline_label_selector(pipeline) := value if {
not is_fbc # given that the build task is shared between fbc and docker builds we can't rely on the task's label

# Labels of the build Task from the SLSA Provenance v1.0 of a PipelineRun
value := build_task(pipeline).metadata.labels[task_label]
values := [l | some build_task in build_tasks(pipeline); l := build_task.metadata.labels[task_label]]
count(lib.to_set(values)) == 1
value := values[0]
} else := value if {
not is_fbc # given that the build task is shared between fbc and docker builds we can't rely on the task's label

# Labels of the build Task from the SLSA Provenance v0.2 of a PipelineRun
value := build_task(pipeline).invocation.environment.labels[task_label]
values := [l | some build_task in build_tasks(pipeline); l := build_task.invocation.environment.labels[task_label]]
count(lib.to_set(values)) == 1
value := values[0]
} else := value if {
# PipelineRun labels found in the SLSA Provenance v1.0
value := pipeline.statement.predicate.buildDefinition.internalParameters.labels[pipeline_label]
Expand Down
8 changes: 4 additions & 4 deletions policy/lib/tekton/task.rego
Original file line number Diff line number Diff line change
Expand Up @@ -152,25 +152,25 @@ task_step_image_ref(step) := step.environment.image
task_step_image_ref(step) := step.imageID

# build_task returns the build task found in the attestation
build_task(attestation) := task if {
build_tasks(attestation) := [task |
some task in tasks(attestation)

image_url := task_result(task, "IMAGE_URL")
count(trim_space(image_url)) > 0

image_digest := task_result(task, "IMAGE_DIGEST")
count(trim_space(image_digest)) > 0
}
]

git_clone_task(attestation) := task if {
git_clone_tasks(attestation) := [task |
some task in tasks(attestation)

commit := task_result(task, "commit")
count(trim_space(commit)) > 0

url := task_result(task, "url")
count(trim_space(url)) > 0
}
]

# task_data returns the data relating to the task. If the task is
# referenced from a bundle, the "bundle" attribute is included.
Expand Down
84 changes: 76 additions & 8 deletions policy/lib/tekton/task_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ test_tasks_from_pipeline_with_spam if {

test_build_task if {
expected := _good_build_task
lib.assert_equal(expected, tkn.build_task(_good_attestation))
lib.assert_equal([expected], tkn.build_tasks(_good_attestation))
}

test_build_task_not_found if {
Expand All @@ -312,22 +312,56 @@ test_build_task_not_found if {
"path": "/statement/predicate/buildConfig/tasks/0/results/0/name",
"value": "IMAGE_URL_SKIP",
}])
not tkn.build_task(missing_image_url)
count(tkn.build_tasks(missing_image_url)) == 0

missing_image_digest := json.patch(_good_attestation, [{
"op": "add",
"path": "/statement/predicate/buildConfig/tasks/0/results/1/name",
"value": "IMAGE_DIGEST_SKIP",
}])
not tkn.build_task(missing_image_digest)
count(tkn.build_tasks(missing_image_digest)) == 0

missing_results := json.remove(_good_attestation, ["/statement/predicate/buildConfig/tasks/0/results"])
not tkn.build_task(missing_results)
count(tkn.build_tasks(missing_results)) == 0
}

test_multiple_build_tasks if {
task1 := json.patch(_good_build_task, [{
"op": "replace",
"path": "/ref/name",
"value": "buildah-1",
}])

task2 := json.patch(_good_build_task, [{
"op": "replace",
"path": "/ref/name",
"value": "buildah-2",
}])

task3 := json.patch(_good_build_task, [{
"op": "replace",
"path": "/ref/name",
"value": "buildah-3",
}])

attestation3 := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, task2, task3]},
}}}

count(tkn.build_tasks(attestation3)) == 3

attestation2 := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, _good_git_clone_task, task3]},
}}}

count(tkn.build_tasks(attestation2)) == 2
}

test_git_clone_task if {
expected := _good_git_clone_task
lib.assert_equal(expected, tkn.git_clone_task(_good_attestation))
lib.assert_equal([expected], tkn.git_clone_tasks(_good_attestation))
}

test_git_clone_task_not_found if {
Expand All @@ -336,17 +370,51 @@ test_git_clone_task_not_found if {
"path": "/statement/predicate/buildConfig/tasks/1/results/0/name",
"value": "you-argh-el",
}])
not tkn.git_clone_task(missing_url)
count(tkn.git_clone_tasks(missing_url)) == 0

missing_commit := json.patch(_good_attestation, [{
"op": "add",
"path": "/statement/predicate/buildConfig/tasks/1/results/1/name",
"value": "bachelor",
}])
not tkn.git_clone_task(missing_commit)
count(tkn.git_clone_tasks(missing_commit)) == 0

missing_results := json.remove(_good_attestation, ["/statement/predicate/buildConfig/tasks/1/results"])
not tkn.git_clone_task(missing_results)
count(tkn.git_clone_tasks(missing_results)) == 0
}

test_multiple_git_clone_tasks if {
task1 := json.patch(_good_git_clone_task, [{
"op": "replace",
"path": "/ref/name",
"value": "git-clone-1",
}])

task2 := json.patch(_good_git_clone_task, [{
"op": "replace",
"path": "/ref/name",
"value": "git-clone-2",
}])

task3 := json.patch(_good_git_clone_task, [{
"op": "replace",
"path": "/ref/name",
"value": "git-clone-3",
}])

attestation3 := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, task2, task3]},
}}}

count(tkn.git_clone_tasks(attestation3)) == 3

attestation2 := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, _good_build_task, task3]},
}}}

count(tkn.git_clone_tasks(attestation2)) == 2
}

test_task_data_bundle_ref if {
Expand Down
8 changes: 3 additions & 5 deletions policy/release/hermetic_build_task.rego
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,12 @@ import data.lib.tkn
# - attestation_type.known_attestation_type
#
deny contains result if {
hermetic_build != "true"
_hermetic_build != {"true"}
result := lib.result_helper(rego.metadata.chain(), [])
}

default hermetic_build := "false"

hermetic_build := value if {
_hermetic_build contains value if {
some attestation in lib.pipelinerun_attestations
task := tkn.build_task(attestation)
some task in tkn.build_tasks(attestation)
value := tkn.task_param(task, "HERMETIC")
}
63 changes: 63 additions & 0 deletions policy/release/hermetic_build_task_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,69 @@ test_not_hermetic_build if {
lib.assert_equal_results(expected, hermetic_build_task.deny) with input.attestations as [hermetic_missing]
}

test_hermetic_build_many_build_tasks if {
task1 := {
"results": [
{"name": "IMAGE_URL", "value": "registry/repo"},
{"name": "IMAGE_DIGEST", "value": "digest"},
],
"ref": {"kind": "Task", "name": "build-1", "bundle": "reg.img/spam@sha256:abc"},
"invocation": {"parameters": {"HERMETIC": "true"}},
}

task2 := {
"results": [
{"name": "IMAGE_URL", "value": "registry/repo"},
{"name": "IMAGE_DIGEST", "value": "digest"},
],
"ref": {"kind": "Task", "name": "build-2", "bundle": "reg.img/spam@sha256:abc"},
"invocation": {"parameters": {"HERMETIC": "true"}},
}

attestation := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, task2]},
}}}
lib.assert_empty(hermetic_build_task.deny) with input.attestations as [attestation]

attestation_mixed_hermetic := json.patch(
{"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, task2]},
}}},
[{
"op": "replace",
"path": "/statement/predicate/buildConfig/tasks/0/invocation/parameters/HERMETIC",
"value": "false",
}],
)
expected := {{
"code": "hermetic_build_task.build_task_hermetic",
"msg": "Build task was not invoked with the hermetic parameter set",
}}
lib.assert_equal_results(expected, hermetic_build_task.deny) with input.attestations as [attestation_mixed_hermetic]

attestation_non_hermetic := json.patch(
{"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [task1, task2]},
}}},
[
{
"op": "replace",
"path": "/statement/predicate/buildConfig/tasks/0/invocation/parameters/HERMETIC",
"value": "false",
},
{
"op": "replace",
"path": "/statement/predicate/buildConfig/tasks/1/invocation/parameters/HERMETIC",
"value": "false",
},
],
)
lib.assert_equal_results(expected, hermetic_build_task.deny) with input.attestations as [attestation_non_hermetic]
}

_good_attestation := {"statement": {"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [{
Expand Down
8 changes: 4 additions & 4 deletions policy/release/provenance_materials.rego
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import data.lib.tkn
#
deny contains result if {
some attestation in lib.pipelinerun_attestations
not tkn.git_clone_task(attestation)
count(tkn.git_clone_tasks(attestation)) == 0
result := lib.result_helper(rego.metadata.chain(), [])
}

Expand All @@ -56,9 +56,9 @@ deny contains result if {
deny contains result if {
some attestation in lib.pipelinerun_attestations

t := tkn.git_clone_task(attestation)
url := _normalize_git_url(tkn.task_result(t, "url"))
commit := tkn.task_result(t, "commit")
some task in tkn.git_clone_tasks(attestation)
url := _normalize_git_url(tkn.task_result(task, "url"))
commit := tkn.task_result(task, "commit")

materials := [m |
some m in attestation.statement.predicate.materials
Expand Down
40 changes: 40 additions & 0 deletions policy/release/provenance_materials_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,46 @@ test_commit_and_url_mismatch if {
lib.assert_equal_results(expected, provenance_materials.deny) with input.attestations as [_mock_attestation(tasks)]
}

test_provenance_many_git_clone_tasks if {
task := {
"results": [
{"name": "url", "value": _git_url},
{"name": "commit", "value": _git_commit},
],
"ref": {"bundle": _bundle},
"steps": [{"entrypoint": "/bin/bash"}],
}

task1 := json.patch(task, [{
"op": "add",
"path": "name",
"value": "git-clone-1",
}])

task2 := json.patch(task, [{
"op": "add",
"path": "name",
"value": "git-clone-2",
}])

attestation := _mock_attestation([task1, task2])

# all good
lib.assert_empty(provenance_materials.deny) with input.attestations as [attestation]

# one task's cloned digest doesn't match
expected := {{
"code": "provenance_materials.git_clone_source_matches_provenance",
# regal ignore:line-length
"msg": `Entry in materials for the git repo "git+https://gitforge/repo.git" and commit "big-bada-boom" not found`,
}}
lib.assert_equal_results(expected, provenance_materials.deny) with input.attestations as [json.patch(attestation, [{
"op": "replace",
"path": "/statement/predicate/buildConfig/tasks/0/results/1/value",
"value": "big-bada-boom",
}])]
}

_bundle := "registry.img/spam@sha256:4e388ab32b10dc8dbc7e28144f552830adc74787c1e2c0824032078a79f227fb"

_git_url := "https://gitforge/repo"
Expand Down
Loading

0 comments on commit b861394

Please sign in to comment.