Skip to content

Commit 71564b1

Browse files
committed
Use manifest version annotation for trusted rules
The version extracted from the task ref can't be relied upon, since it is easily counterfeitable. Instead, as per ADR decision (see attached link), we are going to extract the version annotation from inside the task manifest, and use that to evaluate the version constraints in the trusted task rules. Ref: https://github.com/konflux-ci/architecture/blob/main/ADR/0054-task-versioning.md#mark-each-meaningful-change-with-a-version-update
1 parent 435d938 commit 71564b1

File tree

2 files changed

+62
-31
lines changed

2 files changed

+62
-31
lines changed

policy/lib/tekton/trusted.rego

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -458,14 +458,13 @@ _trusted_task_rules_schema := {
458458
# Returns true if the task reference version satisfies ALL semver constraints in the rule.
459459
# This is intended for use in allow rules, where the rule is effective if all constraints match.
460460
# Supports constraints like: >=v2, <3, >3.1.0, <v4.2, >=1.2.3
461-
# Returns true if rule has no "versions" field, or if ref.tagged_ref satisfies all constraints
461+
# Returns true if rule has no "versions" field, or if task version satisfies all constraints
462462
# Returns false for git references (no version tag) or invalid semver
463463
_version_satisfies_all_rule_constraints(ref, rule) if {
464464
not "versions" in object.keys(rule)
465465
} else if {
466-
# Extract version from task reference (e.g., "0.4" from "oci://registry.io/task:0.4")
467-
"tagged_ref" in object.keys(ref)
468-
version := _normalize_version(ref.tagged_ref)
466+
manifest_version := _get_manifest_version_annotation(ref.key)
467+
version := _normalize_version(manifest_version)
469468
semver.is_valid(version)
470469

471470
constraints := rule.versions
@@ -483,14 +482,13 @@ _version_satisfies_all_rule_constraints(ref, rule) if {
483482
# Returns true if the task reference version satisfies AT LEAST ONE semver constraint in the rule.
484483
# This is intended for use in deny rules, where the rule is effective if at least one constraint match.
485484
# Supports constraints like: >=v2, <3, >3.1.0, <v4.2, >=1.2.3
486-
# Returns true if rule has no "versions" field, or if ref.tagged_ref satisfies all constraints
485+
# Returns true if rule has no "versions" field, or if task version satisfies all constraints
487486
# Returns false for git references (no version tag) or invalid semver
488487
_version_satisfies_any_rule_constraints(ref, rule) if {
489488
not "versions" in object.keys(rule)
490489
} else if {
491-
# Extract version from task reference (e.g., "0.4" from "oci://registry.io/task:0.4")
492-
"tagged_ref" in object.keys(ref)
493-
version := _normalize_version(ref.tagged_ref)
490+
manifest_version := _get_manifest_version_annotation(ref.key)
491+
version := _normalize_version(manifest_version)
494492
semver.is_valid(version)
495493

496494
constraints := rule.versions
@@ -545,6 +543,11 @@ _result_satisfies_operator(result, constraint) if {
545543
result < 0
546544
} else := false
547545

546+
_get_manifest_version_annotation(ref) := version if {
547+
task_manifest := ec.oci.image_manifest(ref)
548+
version := task_manifest.annotations["org.opencontainers.image.version"]
549+
}
550+
548551
# =============================================================================
549552
# BEGIN LEGACY SYSTEM (trusted_tasks)
550553
# Explicit allow list with expiry dates.

policy/lib/tekton/trusted_test.rego

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,16 @@ test_is_trusted_task_with_rules if {
223223
{"name": "kind", "value": "task"},
224224
]}}}
225225
tekton.is_trusted_task(allowed_task) with data.rule_data.trusted_task_rules as trusted_task_rules
226+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "0.4"}}
226227

227228
# Task that matches deny rule should not be trusted (deny takes precedence)
228229
denied_task := {"spec": {"taskRef": {"resolver": "bundles", "params": [
229-
{"name": "bundle", "value": "quay.io/konflux-ci/tekton-catalog/task-buildah"},
230+
{"name": "bundle", "value": "quay.io/konflux-ci/tekton-catalog/task-buildah:0.3@sha256:digest"},
230231
{"name": "name", "value": "task-buildah"},
231232
{"name": "kind", "value": "task"},
232233
]}}}
233234
not tekton.is_trusted_task(denied_task) with data.rule_data.trusted_task_rules as trusted_task_rules
235+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "0.3"}}
234236

235237
# Task that matches allow pattern (registry.local) should be trusted
236238
# Note: The key format is oci://registry.local/trusty:1.0 (with tag), so pattern oci://registry.local/* matches
@@ -240,6 +242,7 @@ test_is_trusted_task_with_rules if {
240242
{"name": "kind", "value": "task"},
241243
]}}}
242244
tekton.is_trusted_task(registry_local_task) with data.rule_data.trusted_task_rules as trusted_task_rules
245+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.0"}}
243246

244247
# Task that doesn't match any allow rule should not be trusted
245248
# Note: This task uses a different path (untrusted) that doesn't match the pattern
@@ -249,6 +252,7 @@ test_is_trusted_task_with_rules if {
249252
{"name": "kind", "value": "task"},
250253
]}}}
251254
not tekton.is_trusted_task(not_allowed_task) with data.rule_data.trusted_task_rules as trusted_task_rules
255+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.0"}}
252256

253257
# Tasks satisfying at least one deny rule version constraints should be denied
254258
deny_constrained_task_denied_version := {"spec": {"taskRef": {"resolver": "bundles", "params": [
@@ -257,6 +261,7 @@ test_is_trusted_task_with_rules if {
257261
{"name": "kind", "value": "task"},
258262
]}}}
259263
not tekton.is_trusted_task(deny_constrained_task_denied_version) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
264+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.5"}}
260265

261266
# Task not satisfying any deny rule version constraints should not be denied
262267
deny_constrained_task_valid_version := {"spec": {"taskRef": {"resolver": "bundles", "params": [
@@ -265,6 +270,7 @@ test_is_trusted_task_with_rules if {
265270
{"name": "kind", "value": "task"},
266271
]}}}
267272
tekton.is_trusted_task(deny_constrained_task_valid_version) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
273+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.2.3"}}
268274

269275
# Tasks satisfying all the allow-rule version constraints should be allowed
270276
allow_constrained_task_valid_version := {"spec": {"taskRef": {"resolver": "bundles", "params": [
@@ -273,6 +279,7 @@ test_is_trusted_task_with_rules if {
273279
{"name": "kind", "value": "task"},
274280
]}}}
275281
tekton.is_trusted_task(allow_constrained_task_valid_version) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
282+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.5"}}
276283

277284
# Tasks *NOT* satisfying all the allow-rule version constraints should be denied
278285
allow_constrained_task_denied_version := {"spec": {"taskRef": {"resolver": "bundles", "params": [
@@ -281,6 +288,27 @@ test_is_trusted_task_with_rules if {
281288
{"name": "kind", "value": "task"},
282289
]}}}
283290
not tekton.is_trusted_task(allow_constrained_task_denied_version) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
291+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.2.3"}}
292+
293+
# Task with mismatching versions between ref and manifest annotations.
294+
# Only the manifest annotation is taken into consideration
295+
allow_constrained_task_denied_version_mismatching_1 := {"spec": {"taskRef": {"resolver": "bundles", "params": [
296+
{"name": "bundle", "value": "quay.io/konflux-ci/another-catalog/allow-task-constrained:1.5@sha256:digest"},
297+
{"name": "name", "value": "constrained"},
298+
{"name": "kind", "value": "task"},
299+
]}}}
300+
not tekton.is_trusted_task(allow_constrained_task_denied_version_mismatching_1) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
301+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.2.3"}}
302+
303+
# Task with mismatching versions between ref and manifest annotations.
304+
# Only the manifest annotation is taken into consideration
305+
allow_constrained_task_denied_version_mismatching_2 := {"spec": {"taskRef": {"resolver": "bundles", "params": [
306+
{"name": "bundle", "value": "quay.io/konflux-ci/another-catalog/allow-task-constrained:1.2.3@sha256:digest"},
307+
{"name": "name", "value": "constrained"},
308+
{"name": "kind", "value": "task"},
309+
]}}}
310+
tekton.is_trusted_task(allow_constrained_task_denied_version_mismatching_2) with data.rule_data.trusted_task_rules as trusted_task_rules # regal ignore:line-length
311+
with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.5"}}
284312
}
285313

286314
test_trusted_task_records if {
@@ -862,46 +890,46 @@ unsorted_trusted_task := {"oci://registry.local/trusty:1.0": [
862890

863891
test_version_satisfies_all_rule_constraints if {
864892
# No version constraints in rule - should always pass
865-
tekton._version_satisfies_all_rule_constraints({"tagged_ref": "1.2.3"}, {})
893+
tekton._version_satisfies_all_rule_constraints({"key": "1.2.3"}, {})
866894

867895
# Has version constraints and valid semver
868-
tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.2.3"}, {"versions": [">=1.1", "<3"]})
869-
tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.1.0"}, {"versions": [">=1.1", "<3"]})
870-
tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.1.1"}, {"versions": [">1.1", "<3"]})
871-
tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v3.0.0"}, {"versions": [">1.1", "<=3"]})
896+
tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.2.3"}} # regal ignore:line-length
897+
tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "1.1.0"}} # regal ignore:line-length
898+
tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.1.1"}} # regal ignore:line-length
899+
tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<=3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v3.0.0"}} # regal ignore:line-length
872900

873901
# Version doesn't match all the constraints
874-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.5.0"}, {"versions": [">=2"]})
875-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.1.0"}, {"versions": [">1.1", "<3"]})
876-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v3.0.0"}, {"versions": [">1.1", "<3"]})
877-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.5.0"}, {"versions": ["<2", ">=1.5.1"]})
902+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.5.0"}} # regal ignore:line-length
903+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.1.0"}} # regal ignore:line-length
904+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v3.0.0"}} # regal ignore:line-length
905+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": ["<2", ">=1.5.1"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.5.0"}} # regal ignore:line-length
878906

879907
# Invalid inputs - should fail
880-
not tekton._version_satisfies_all_rule_constraints({}, {"versions": [">=2"]})
881-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "latest"}, {"versions": [">=2"]})
908+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {}} # regal ignore:line-length
909+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "invalid"}} # regal ignore:line-length
882910
}
883911

884912
test_version_satisfies_any_rule_constraints if {
885913
# No version constraints in rule - should always pass
886-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "1.2.3"}, {})
914+
tekton._version_satisfies_any_rule_constraints({"key": "1.2.3"}, {})
887915

888916
# Has version constraints and valid semver
889-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v1.2.3"}, {"versions": [">=1.1", "<3"]})
890-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v1.1.0"}, {"versions": [">=1.1", "<3"]})
891-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v1.1.1"}, {"versions": [">1.1", "<3"]})
892-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v3.0.0"}, {"versions": [">1.1", "<=3"]})
917+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">=1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.2.3"}} # regal ignore:line-length
918+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">=1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.1.0"}} # regal ignore:line-length
919+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.1.1"}} # regal ignore:line-length
920+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<=3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v3.0.0"}} # regal ignore:line-length
893921

894922
# Version doesn't match all the constraints, but still passes
895-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v1.1.0"}, {"versions": [">1.1", "<3"]})
896-
tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v3.0.0"}, {"versions": [">1.1", "<3"]})
923+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.1.0"}} # regal ignore:line-length
924+
tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">1.1", "<3"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v3.0.0"}} # regal ignore:line-length
897925

898926
# Version doesn't match any constraint
899-
not tekton._version_satisfies_all_rule_constraints({"tagged_ref": "v1.5.0"}, {"versions": [">=2"]})
900-
not tekton._version_satisfies_any_rule_constraints({"tagged_ref": "v1.5.0"}, {"versions": ["<1", ">=1.5.1"]})
927+
not tekton._version_satisfies_all_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.5.0"}} # regal ignore:line-length
928+
not tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": ["<1", ">=1.5.1"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "v1.5.0"}} # regal ignore:line-length
901929

902930
# Invalid inputs - should fail
903-
not tekton._version_satisfies_any_rule_constraints({}, {"versions": [">=2"]})
904-
not tekton._version_satisfies_any_rule_constraints({"tagged_ref": "latest"}, {"versions": [">=2"]})
931+
not tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {}} # regal ignore:line-length
932+
not tekton._version_satisfies_any_rule_constraints({"key": "taskref"}, {"versions": [">=2"]}) with ec.oci.image_manifest as {"annotations": {"org.opencontainers.image.version": "invalid"}} # regal ignore:line-length
905933
}
906934

907935
test_normalize_version if {

0 commit comments

Comments
 (0)