Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ Make sure to use the date format in RFC3339 format in the "build.appstudio.redha
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `Expires on time is not in RFC3339 format: %q`
* Code: `annotations.expires_on_format`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/annotations/annotations.rego#L14[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/annotations/annotations.rego#L15[Source, window="_blank"]
4 changes: 2 additions & 2 deletions antora/docs/modules/ROOT/pages/packages/task_kind.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Confirm the task definition includes the kind field.
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `Required field 'kind' not found`
* Code: `kind.kind_present`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/kind/kind.rego#L29[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/kind/kind.rego#L31[Source, window="_blank"]

[#kind__expected_kind]
=== link:#kind__expected_kind[Task definition has expected kind]
Expand All @@ -26,4 +26,4 @@ Confirm the task definition has the kind "Task".
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `Unexpected kind '%s' for task definition`
* Code: `kind.expected_kind`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/kind/kind.rego#L16[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/kind/kind.rego#L17[Source, window="_blank"]
4 changes: 2 additions & 2 deletions antora/docs/modules/ROOT/pages/packages/task_results.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Verify if Task defines the required result. This is controlled by the `required_
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `%s`
* Code: `results.required`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/results/results.rego#L15[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/results/results.rego#L16[Source, window="_blank"]

[#results__rule_data_provided]
=== link:#results__rule_data_provided[Rule data provided]
Expand All @@ -28,4 +28,4 @@ Confirm the expected `required_task_results` rule data key has been provided in
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `%s`
* Code: `results.rule_data_provided`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/results/results.rego#L29[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/results/results.rego#L31[Source, window="_blank"]
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Confirm the `allowed_step_image_registry_prefixes` rule data was provided, since
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `%s`
* Code: `step_image_registries.step_image_registry_prefix_list_provided`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_image_registries/step_image_registries.rego#L45[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_image_registries/step_image_registries.rego#L47[Source, window="_blank"]

[#step_image_registries__step_images_permitted]
=== link:#step_image_registries__step_images_permitted[Step images come from permitted registry]
Expand All @@ -30,4 +30,4 @@ Confirm that each step in the Task uses a container image with a URL that matche
* Rule type: [rule-type-indicator failure]#FAILURE#
* FAILURE message: `Step %d uses disallowed image ref '%s'`
* Code: `step_image_registries.step_images_permitted`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_image_registries/step_image_registries.rego#L18[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_image_registries/step_image_registries.rego#L19[Source, window="_blank"]
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ Confirm that each step in the Task uses a container image that is accessible.
* FAILURE message: `Step %d uses inaccessible image ref '%s'`
* Code: `step_images.step_images_accessible`
* Effective from: `2025-02-10T00:00:00Z`
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_images/step_images.rego#L14[Source, window="_blank"]
* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/task/step_images/step_images.rego#L15[Source, window="_blank"]
25 changes: 25 additions & 0 deletions policy/lib/tkn_bundle/tkn_bundle.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package lib.tkn_bundle

import rego.v1

# tasks returns task definitions from either:
# - Direct task YAML input (ec validate input)
# - Task bundle OCI image layers (ec validate image)
tasks contains input if {
input.apiVersion
}

tasks contains task if {
manifest := ec.oci.image_manifest(input.image.ref)
manifest != null
some layer in manifest.layers
layer.annotations["dev.tekton.image.kind"] == "task"
task_name := layer.annotations["dev.tekton.image.name"]

parts := split(input.image.ref, "@")
repo := parts[0]
blob_ref := sprintf("%s@%s", [repo, layer.digest])

files := ec.oci.blob_files(blob_ref, [task_name])
task := files[task_name]
}
4 changes: 3 additions & 1 deletion policy/task/annotations/annotations.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package annotations
import rego.v1

import data.lib.metadata
import data.lib.tkn_bundle

# METADATA
# title: Task definition uses expires-on annotation in RFC3339 format
Expand All @@ -22,7 +23,8 @@ import data.lib.metadata
# Expires on time is not in RFC3339 format: %q
#
deny contains result if {
expires_on := input.metadata.annotations[_expires_on_annotation]
some task in tkn_bundle.tasks
expires_on := task.metadata.annotations[_expires_on_annotation]

not time.parse_rfc3339_ns(expires_on)

Expand Down
6 changes: 4 additions & 2 deletions policy/task/annotations/annotations_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import data.annotations

test_valid_expiry_dates if {
# regal ignore:line-length
assertions.assert_empty(annotations.deny) with input.metadata.annotations as {annotations._expires_on_annotation: "2000-01-02T03:04:05Z"}
assertions.assert_empty(annotations.deny) with input as _task({annotations._expires_on_annotation: "2000-01-02T03:04:05Z"})
}

test_invalid_expiry_dates if {
assertions.assert_equal_results(annotations.deny, {{
"code": "annotations.expires_on_format",
"msg": `Expires on time is not in RFC3339 format: "meh"`,
}}) with input.metadata.annotations as {annotations._expires_on_annotation: "meh"}
}}) with input as _task({annotations._expires_on_annotation: "meh"})
}

_task(annots) := {"apiVersion": "tekton.dev/v1", "kind": "Task", "metadata": {"annotations": annots}}
9 changes: 6 additions & 3 deletions policy/task/kind/kind.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package kind
import rego.v1

import data.lib.metadata
import data.lib.tkn_bundle

expected_kind := "Task"

Expand All @@ -22,8 +23,9 @@ expected_kind := "Task"
# failure_msg: Unexpected kind '%s' for task definition
#
deny contains result if {
expected_kind != input.kind
result := metadata.result_helper(rego.metadata.chain(), [input.kind])
some task in tkn_bundle.tasks
expected_kind != task.kind
result := metadata.result_helper(rego.metadata.chain(), [task.kind])
}

# METADATA
Expand All @@ -35,6 +37,7 @@ deny contains result if {
# failure_msg: Required field 'kind' not found
#
deny contains result if {
not input.kind
some task in tkn_bundle.tasks
not task.kind
result := metadata.result_helper(rego.metadata.chain(), [])
}
30 changes: 27 additions & 3 deletions policy/task/kind/kind_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,40 @@ test_unexpected_kind if {
assertions.assert_equal_results(kind.deny, {{
"code": "kind.expected_kind",
"msg": "Unexpected kind 'Foo' for task definition",
}}) with input.kind as "Foo"
}}) with input as {"apiVersion": "tekton.dev/v1", "kind": "Foo"}
}

test_expected_kind if {
assertions.assert_empty(kind.deny) with input as {"kind": "Task"}
assertions.assert_empty(kind.deny) with input as {"apiVersion": "tekton.dev/v1", "kind": "Task"}
}

test_kind_not_found if {
assertions.assert_equal_results(kind.deny, {{
"code": "kind.kind_present",
"msg": "Required field 'kind' not found",
}}) with input as {"bad": "Foo"}
}}) with input as {"apiVersion": "tekton.dev/v1", "bad": "Foo"}
}

test_skipped_without_api_version if {
assertions.assert_empty(kind.deny) with input as {"image": {"ref": "example.com/img"}}
with ec.oci.image_manifest as null
}

test_task_bundle_wrong_kind if {
_manifest := {"layers": [{
"digest": "sha256:abc",
"annotations": {
"dev.tekton.image.kind": "task",
"dev.tekton.image.name": "my-task",
"dev.tekton.image.apiVersion": "v1",
},
}]}
_task := {"apiVersion": "tekton.dev/v1", "kind": "Pipeline"}

assertions.assert_equal_results(kind.deny, {{
"code": "kind.expected_kind",
"msg": "Unexpected kind 'Pipeline' for task definition",
}}) with input.image.ref as "registry.example.com/bundle@sha256:aaa"
with ec.oci.image_manifest as _manifest
with ec.oci.blob_files as {"my-task": _task}
}
30 changes: 15 additions & 15 deletions policy/task/results/results.rego
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import rego.v1

import data.lib.metadata
import data.lib.rule_data
import data.lib.tkn_bundle

import data.lib.json as j

Expand All @@ -22,7 +23,8 @@ import data.lib.json as j
# failure_msg: '%s'
#
deny contains result if {
some err in errors
some task in tkn_bundle.tasks
some err in _errors(task)
result := metadata.result_helper(rego.metadata.chain(), [err])
}

Expand All @@ -41,34 +43,32 @@ deny contains result if {
result := metadata.result_helper_with_severity(rego.metadata.chain(), [e.message], e.severity)
}

errors contains err if {
version := object.get(input.metadata, ["labels", "app.kubernetes.io/version"], "")
_errors(task) := {err |
version := object.get(task.metadata, ["labels", "app.kubernetes.io/version"], "")
version_constraints := {r.version | some r in rule_data.get(_rule_data_key)}
not version in version_constraints

some required in {r |
some r in rule_data.get(_rule_data_key)
input.metadata.name == r.task
task.metadata.name == r.task
not r.version
}
found := [result |
some result in input.spec.results
result.name == required.result
found := [r |
some r in task.spec.results
r.name == required.result
]
count(found) == 0
err := sprintf("%q result not found in %q Task%s (all versions)", [required.result, required.task, _vstr(version)])
}

errors contains err if {
version := object.get(input.metadata, ["labels", "app.kubernetes.io/version"], "")
} | {err |
version := object.get(task.metadata, ["labels", "app.kubernetes.io/version"], "")
some required in {r |
some r in rule_data.get(_rule_data_key)
input.metadata.name == r.task
task.metadata.name == r.task
r.version == version
}
found := [result |
some result in input.spec.results
result.name == required.result
found := [r |
some r in task.spec.results
r.name == required.result
]
count(found) == 0
err := sprintf("%q result not found in %q Task/v%s", [required.result, required.task, version])
Expand Down
8 changes: 5 additions & 3 deletions policy/task/step_image_registries/step_image_registries.rego
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import rego.v1

import data.lib.metadata
import data.lib.rule_data
import data.lib.tkn_bundle

import data.lib.json as j
import data.lib.k8s
Expand All @@ -28,17 +29,18 @@ import data.lib.k8s
# Make sure the container image used in each step of the Task comes from an approved registry.
#
deny contains result if {
input.kind == "Task"
some task in tkn_bundle.tasks
task.kind == "Task"

some step_index, step in input.spec.steps
some step_index, step in task.spec.steps
image_ref := step.image
allowed_registry_prefixes := rule_data.get(_rule_data_key)
not image_ref_permitted(image_ref, allowed_registry_prefixes)

result := metadata.result_helper_with_term(
rego.metadata.chain(),
[step_index, image_ref],
k8s.name_version(input),
k8s.name_version(task),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ bad_image := "hackz.io/openshift-pipelines/pipelines-git-init-rhel8@sha256:af7dd

test_step_images_permitted_success if {
task := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"spec": {"steps": [{"image": good_image}, {"image": good_image}]},
}

Expand All @@ -23,7 +23,7 @@ test_step_images_permitted_success if {

test_step_images_permitted_failure if {
task := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"metadata": {"labels": {"app.kubernetes.io/version": "1.0"}, "name": "git-clone"},
"spec": {"steps": [{"image": bad_image}, {"image": good_image}, {"image": bad_image}]},
}
Expand All @@ -48,7 +48,7 @@ test_step_images_permitted_failure if {

test_step_images_missing_name_version if {
task_no_name := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"metadata": {"labels": {"app.kubernetes.io/version": "1.0"}},
"spec": {"steps": [{"image": bad_image}]},
}
Expand All @@ -61,7 +61,7 @@ test_step_images_missing_name_version if {
}}) with input as task_no_name

task_no_version := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"metadata": {"name": "git-clone"},
"spec": {"steps": [{"image": bad_image}]},
}
Expand All @@ -74,7 +74,7 @@ test_step_images_missing_name_version if {
}}) with input as task_no_version

task_no_name_no_version := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"spec": {"steps": [{"image": bad_image}]},
}

Expand All @@ -97,7 +97,7 @@ test_step_images_permitted_skipped if {

test_step_images_permitted_prefix_list_empty if {
task := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"metadata": {"labels": {"app.kubernetes.io/version": "1.0"}, "name": "git-clone"},
"spec": {"steps": [{"image": good_image}]},
}
Expand Down
6 changes: 4 additions & 2 deletions policy/task/step_images/step_images.rego
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package step_images
import rego.v1

import data.lib.metadata
import data.lib.tkn_bundle

# METADATA
# title: Step images are valid
Expand All @@ -24,9 +25,10 @@ import data.lib.metadata
# effective_on: 2025-02-10T00:00:00Z
#
deny contains result if {
input.kind == "Task"
some task in tkn_bundle.tasks
task.kind == "Task"

some step_index, step in input.spec.steps
some step_index, step in task.spec.steps
image_ref := step.image
not ec.oci.image_manifest(image_ref)

Expand Down
6 changes: 3 additions & 3 deletions policy/task/step_images/step_images_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import data.step_images

test_looks_at_tasks_only if {
pipeline := {
"kind": "Pipeline",
"apiVersion": "tekton.dev/v1", "kind": "Pipeline",
"spec": {"steps": [{"image": "registry.io/repository/not_ok"}]},
}

Expand All @@ -23,7 +23,7 @@ test_task_with_no_steps if {

test_task_with_valid_steps if {
task := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"spec": {"steps": [
{"image": "registry.io/repository/ok:1"},
{"image": "registry.io/repository/ok:2"},
Expand All @@ -36,7 +36,7 @@ test_task_with_valid_steps if {

test_task_with_invalid_steps if {
task := {
"kind": "Task",
"apiVersion": "tekton.dev/v1", "kind": "Task",
"spec": {"steps": [
{"image": "registry.io/repository/ok:1"},
{"image": "registry.io/repository/not_ok:2"},
Expand Down
Loading