Skip to content

Commit

Permalink
Merge pull request #849 from zregvart/issue/EC-254
Browse files Browse the repository at this point in the history
Required task must be from acceptable bundle
  • Loading branch information
lcarva authored Jan 9, 2024
2 parents 608185e + 862060f commit 521c7d4
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 6 deletions.
7 changes: 7 additions & 0 deletions policy/lib/bundles.rego
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ unacceptable_task_bundle(tasks) := {task |
_is_unacceptable(ref)
}

# Returns if the task uses an acceptable bundle reference
is_acceptable_task(task) if {
ref := image.parse(_bundle_ref(task, _task_bundles))

_record_exists(ref)
}

_is_unacceptable(ref) if {
not _record_exists(ref)
}
Expand Down
13 changes: 13 additions & 0 deletions policy/lib/bundles_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,16 @@ test_newer_version_exists_tags_as_versions_older if {
]}
bundles._newer_version_exists(ref) with data["task-bundles"] as acceptable
}

test_is_acceptable if {
acceptable := {"registry.io/repository/image": [{
"digest": "sha256:digest",
"tag": "",
"effective_on": "1962-04-11T00:00:00Z",
}]}
acceptable_task := {"name": "my-task", "taskRef": {"bundle": "registry.io/repository/image:tag@sha256:digest"}}
bundles.is_acceptable_task(acceptable_task) with data["task-bundles"] as acceptable

unacceptable_task := {"name": "my-task", "taskRef": {"bundle": "registry.io/other/image:tag@sha256:digest"}}
not bundles.is_acceptable_task(unacceptable_task) with data["task-bundles"] as acceptable
}
9 changes: 8 additions & 1 deletion policy/pipeline/required_tasks.rego
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import future.keywords.if
import future.keywords.in

import data.lib
import data.lib.bundles
import data.lib.tkn

# METADATA
Expand Down Expand Up @@ -103,8 +104,14 @@ deny contains result if {
# _missing_tasks returns a set of task names that are in the given
# required_tasks, but not in the pipeline definition.
_missing_tasks(required_tasks) := {task |
acceptable := [task_name |
some task in tkn.tasks(input)
bundles.is_acceptable_task(task)
some task_name in tkn.task_names(task)
]

some required_task in required_tasks
some task in _any_missing(required_task, tkn.tasks_names(input))
some task in _any_missing(required_task, acceptable)
}

_any_missing(required, tasks) := missing if {
Expand Down
35 changes: 35 additions & 0 deletions policy/pipeline/required_tasks_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import data.policy.pipeline.required_tasks
test_required_tasks_met if {
pipeline := _pipeline_with_tasks_and_label(_expected_required_tasks, [], [])
lib.assert_empty(required_tasks.deny) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

pipeline_finally := _pipeline_with_tasks_and_label([], _expected_required_tasks, [])
lib.assert_empty(required_tasks.deny) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline_finally
}

Expand All @@ -26,27 +28,32 @@ test_required_tasks_not_met if {
expected,
required_tasks.deny,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

test_future_required_tasks_met if {
pipeline := _pipeline_with_tasks_and_label(_expected_future_required_tasks, [], [])
lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

pipeline_finally := _pipeline_with_tasks_and_label([], _expected_future_required_tasks, [])
lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline_finally
}

test_not_warn_if_only_future_required_tasks if {
tasks := _time_based_pipeline_required_tasks_future_only
pipeline := _pipeline_with_tasks_and_label(_expected_future_required_tasks, [], [])
lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

pipeline_finally := _pipeline_with_tasks_and_label([], _expected_future_required_tasks, [])
lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline_finally
}

Expand All @@ -59,13 +66,15 @@ test_future_required_tasks_not_met if {
expected,
required_tasks.warn,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

test_extra_tasks_ignored if {
pipeline := _pipeline_with_tasks_and_label(_expected_future_required_tasks | {"spam"}, [], [])
all := required_tasks.deny | required_tasks.warn
lib.assert_empty(all) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -79,20 +88,24 @@ test_missing_pipeline_label if {
expected,
required_tasks.warn,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

test_default_required_task_met if {
pipeline := _pipeline_with_tasks(_expected_required_tasks, [], [])
lib.assert_empty(required_tasks.deny) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

pipeline_finally := _pipeline_with_tasks([], _expected_required_tasks, [])
lib.assert_empty(required_tasks.deny) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline_finally

expected_warn := _missing_pipeline_tasks_warning("fbc")
lib.assert_equal_results(expected_warn, required_tasks.warn) with data["required-tasks"] as _expected_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -102,10 +115,12 @@ test_default_required_tasks_not_met if {

expected := _missing_default_tasks_violation(missing_tasks)
lib.assert_equal_results(expected, required_tasks.deny) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

expected_warn := _missing_pipeline_tasks_warning("fbc")
lib.assert_equal_results(expected_warn, required_tasks.warn) with data["required-tasks"] as _expected_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -116,13 +131,15 @@ test_default_future_required_tasks_met if {
expected_warn,
required_tasks.warn,
) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

pipeline_finally := _pipeline_with_tasks([], _expected_future_required_tasks, [])
lib.assert_equal_results(
expected_warn,
required_tasks.warn,
) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline_finally
}

Expand All @@ -137,6 +154,7 @@ test_default_future_required_tasks_not_met if {
"term": "conftest-clair",
}}
lib.assert_equal_results(expected, required_tasks.warn) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -145,6 +163,7 @@ test_current_equal_latest if {
pipeline := _pipeline_with_tasks_and_label(_expected_future_required_tasks, [], [])

lib.assert_empty(required_tasks.deny | required_tasks.warn) with data["pipeline-required-tasks"] as req_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -153,6 +172,7 @@ test_current_equal_latest_also if {
pipeline := _pipeline_with_tasks_and_label(_expected_required_tasks, [], [])

lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as req_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline

required_tasks_denies := {"fbc": [{
Expand All @@ -164,6 +184,7 @@ test_current_equal_latest_also if {
expected_denies,
required_tasks.deny,
) with data["pipeline-required-tasks"] as required_tasks_denies
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -177,18 +198,21 @@ test_no_tasks_present if {
expected,
required_tasks.deny,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as {"kind": "Pipeline"}

lib.assert_equal_results(
expected,
required_tasks.deny,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as {"kind": "Pipeline", "spec": {}}

lib.assert_equal_results(
expected,
required_tasks.deny,
) with data["pipeline-required-tasks"] as _time_based_pipeline_required_tasks
with data["task-bundles"] as _expected_bundles
with input as {"kind": "Pipeline", "spec": {"tasks": [], "finally": []}}
}

Expand All @@ -215,6 +239,7 @@ test_parameterized if {

expected := _missing_default_tasks_violation({"label-check[POLICY_NAMESPACE=required_checks]"})
lib.assert_equal_results(expected, required_tasks.deny) with data["required-tasks"] as _time_based_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -236,6 +261,7 @@ test_one_of_required_tasks if {
"effective_on": "2009-01-02T00:00:00Z",
}]}
lib.assert_empty(required_tasks.deny) with data["pipeline-required-tasks"] as data_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -261,6 +287,7 @@ test_one_of_required_tasks_missing if {
}

lib.assert_equal_results(expected, required_tasks.deny) with data["pipeline-required-tasks"] as data_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand All @@ -271,6 +298,7 @@ test_future_one_of_required_tasks if {
"effective_on": "2099-01-02T00:00:00Z",
}]}
lib.assert_empty(required_tasks.warn) with data["pipeline-required-tasks"] as data_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand Down Expand Up @@ -298,6 +326,7 @@ test_future_one_of_required_tasks_missing if {
expected,
required_tasks.warn,
) with data["pipeline-required-tasks"] as data_required_tasks
with data["task-bundles"] as _expected_bundles
with input as pipeline
}

Expand Down Expand Up @@ -437,3 +466,9 @@ _time_based_required_tasks := [
]

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

_expected_bundles := {"registry.img/spam": [{
"digest": "sha256:4e388ab32b10dc8dbc7e28144f552830adc74787c1e2c0824032078a79f227fb",
"tag": "0.1",
"effective_on": "2000-01-01T00:00:00Z",
}]}
46 changes: 44 additions & 2 deletions policy/release/tasks.rego
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import future.keywords.if
import future.keywords.in

import data.lib
import data.lib.bundles
import data.lib.tkn

# METADATA
Expand Down Expand Up @@ -98,6 +99,36 @@ deny contains result if {
result := lib.result_helper_with_term(rego.metadata.chain(), [_format_missing(required_task, false)], required_task)
}

# METADATA
# title: All required tasks are from acceptable bundles
# description: >-
# Ensure that the all required tasks are resolved from acceptable bundles.
# custom:
# short_name: required_task_unacceptable_found
# failure_msg: '%s is required and present but not from an acceptable bundle'
# solution: >-
# Make sure all required tasks in the build pipeline are resolved from
# acceptable bundles.
# collections:
# - redhat
# depends_on:
# - tasks.pipeline_has_tasks
#
warn contains result if {
some att in lib.pipelinerun_attestations

# only tasks that are unacceptable
some unacceptable_task in bundles.unacceptable_task_bundle(tkn.tasks(att))
some missing_required_name in _missing_tasks(current_required_tasks)
some unacceptable_task_name in tkn.task_names(unacceptable_task)

unacceptable_task_name == missing_required_name
result := lib.result_helper_with_term(
rego.metadata.chain(), [_format_missing(unacceptable_task_name, false)],
unacceptable_task_name,
)
}

# METADATA
# title: Required tasks list for pipeline was provided
# description: >-
Expand Down Expand Up @@ -170,10 +201,21 @@ deny contains result if {
# required_tasks, but not in the PipelineRun attestation.
_missing_tasks(required_tasks) := {task |
some att in lib.pipelinerun_attestations
count(tkn.tasks(att)) > 0

# all tasks on a PipelineRun
tasks := tkn.tasks(att)
count(tasks) > 0

# only tasks that are acceptable, i.e. tasks that have a record in the
# acceptable bundles data
acceptable := [task_name |
some task in tasks
bundles.is_acceptable_task(task)
some task_name in tkn.task_names(task)
]

some required_task in required_tasks
some task in _any_missing(required_task, tkn.tasks_names(att))
some task in _any_missing(required_task, acceptable)
}

_any_missing(required, tasks) := missing if {
Expand Down
Loading

0 comments on commit 521c7d4

Please sign in to comment.