Skip to content

Commit 0b1574b

Browse files
authored
Merge pull request #1574 from joejstuart/EC-1537
Add schema validation for trusted_task_rules
2 parents 4a96661 + 3c6848c commit 0b1574b

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

policy/lib/tekton/trusted.rego

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,111 @@ data_errors contains error if {
202202
},
203203
)
204204
}
205+
206+
# Validate trusted_task_rules data format using the schema defined in
207+
# trusted_tasks/trusted_task_rules.schema.json
208+
# Skip validation if trusted_task_rules is not provided (null or empty list []).
209+
# lib_rule_data returns [] when a key is not found, so we only validate when
210+
# the value is actually an object (the expected type).
211+
data_errors contains error if {
212+
trusted_task_rules_data := lib_rule_data("trusted_task_rules")
213+
is_object(trusted_task_rules_data) # Only validate if it's an object (skip null and [])
214+
some e in j.validate_schema(trusted_task_rules_data, _trusted_task_rules_schema)
215+
error := {
216+
"message": sprintf("trusted_task_rules data has unexpected format: %s", [e.message]),
217+
"severity": e.severity,
218+
}
219+
}
220+
221+
# Schema for trusted_task_rules as defined in trusted_tasks/trusted_task_rules.schema.json
222+
# This schema validates the rule-based trusted tasks configuration (ADR 53)
223+
_trusted_task_rules_schema := {
224+
"$schema": "http://json-schema.org/draft-07/schema#",
225+
"$id": "https://konflux.io/schemas/trusted_task_rules.json",
226+
"title": "Trusted Task Rules Schema",
227+
"description": "Schema for trusted_task_rules configuration as defined in ADR 53",
228+
"type": "object",
229+
"properties": {
230+
"allow": {
231+
"type": "array",
232+
"description": "Rules that allow tasks matching the pattern",
233+
"items": {
234+
"type": "object",
235+
"required": ["name", "pattern"],
236+
"properties": {
237+
"name": {
238+
"type": "string",
239+
"description": "Human-readable name for the rule",
240+
},
241+
"pattern": {
242+
"type": "string",
243+
# regal ignore:line-length
244+
"description": "URL pattern to match task references. Must not include version tags (e.g., 'oci://quay.io/konflux-ci/tekton-catalog/*' not 'oci://quay.io/konflux-ci/tekton-catalog/task-buildah:0.4*'). Supports wildcards (*).",
245+
"pattern": "^(oci://|git\\+)",
246+
},
247+
"effective_on": {
248+
"type": "string",
249+
"format": "date",
250+
# regal ignore:line-length
251+
"description": "Date when this rule becomes effective (e.g., '2025-02-01'). Rules with future effective_on dates are not considered. If omitted, rule is effective immediately.",
252+
},
253+
"versions": {
254+
"type": "array",
255+
# regal ignore:line-length
256+
"description": "Version constraints to apply. Only tasks matching these version constraints are allowed. Non-semver tags never match version constraints.",
257+
"items": {
258+
"type": "string",
259+
"description": "Version constraint using semver syntax (e.g., '<0.5', '>=2,<2.1.0')",
260+
},
261+
"minItems": 1,
262+
},
263+
},
264+
"additionalProperties": true,
265+
},
266+
"default": [],
267+
},
268+
"deny": {
269+
"type": "array",
270+
"description": "Rules that deny tasks matching the pattern. Deny rules take precedence over allow rules.",
271+
"items": {
272+
"type": "object",
273+
"required": ["name", "pattern"],
274+
"properties": {
275+
"name": {
276+
"type": "string",
277+
"description": "Human-readable name for the rule",
278+
},
279+
"pattern": {
280+
"type": "string",
281+
# regal ignore:line-length
282+
"description": "URL pattern to match task references. Must not include version tags (e.g., 'oci://quay.io/konflux-ci/tekton-catalog/task-buildah*' not 'oci://quay.io/konflux-ci/tekton-catalog/task-buildah:0.4*'). Supports wildcards (*).",
283+
"pattern": "^(oci://|git\\+)",
284+
},
285+
"effective_on": {
286+
"type": "string",
287+
"format": "date",
288+
# regal ignore:line-length
289+
"description": "Date when this rule becomes effective (e.g., '2025-11-15'). Rules with future effective_on dates are not considered. If omitted, rule is effective immediately.",
290+
},
291+
"message": {
292+
"type": "string",
293+
"description": "User-visible message explaining why the task is denied (e.g., deprecation notice)",
294+
},
295+
"versions": {
296+
"type": "array",
297+
# regal ignore:line-length
298+
"description": "Version constraints to apply. Only tasks matching these version constraints are denied. Non-semver tags never match version constraints.",
299+
"items": {
300+
"type": "string",
301+
"description": "Version constraint using semver syntax (e.g., '<0.5', '>=2,<2.1.0')",
302+
},
303+
"minItems": 1,
304+
},
305+
},
306+
"additionalProperties": true,
307+
},
308+
"default": [],
309+
},
310+
},
311+
"additionalProperties": false,
312+
}

policy/lib/tekton/trusted_test.rego

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,91 @@ test_task_expiry_warning_days_data if {
238238
lib.assert_empty(tekton.data_errors) with data.rule_data.task_expiry_warning_days as 14
239239
}
240240

241+
test_trusted_task_rules_data_errors if {
242+
# When trusted_task_rules is not provided (defaults to []), validation should be skipped
243+
lib.assert_empty(tekton.data_errors)
244+
245+
# Valid empty object should pass
246+
lib.assert_empty(tekton.data_errors) with data.rule_data.trusted_task_rules as {}
247+
248+
# Valid trusted_task_rules should pass
249+
valid_rules := {
250+
"allow": [{
251+
"name": "Allow all konflux tasks",
252+
"pattern": "oci://quay.io/konflux-ci/tekton-catalog/*",
253+
}],
254+
"deny": [{
255+
"name": "Deny old buildah",
256+
"pattern": "oci://quay.io/konflux-ci/tekton-catalog/task-buildah*",
257+
"versions": ["<0.5"],
258+
"effective_on": "2025-11-15",
259+
"message": "Deprecated",
260+
}],
261+
}
262+
lib.assert_empty(tekton.data_errors) with data.rule_data.trusted_task_rules as valid_rules
263+
264+
# Missing required fields
265+
invalid_rules := {"allow": [{}]} # missing name and pattern
266+
expected := {
267+
{
268+
"message": "trusted_task_rules data has unexpected format: allow.0: name is required",
269+
"severity": "failure",
270+
},
271+
{
272+
"message": "trusted_task_rules data has unexpected format: allow.0: pattern is required",
273+
"severity": "failure",
274+
},
275+
}
276+
lib.assert_equal(tekton.data_errors, expected) with data.rule_data.trusted_task_rules as invalid_rules
277+
278+
# Invalid pattern validation is not tested here because JSON schema
279+
# pattern validation may not be enforced by the OPA json.match_schema
280+
# function. Pattern validation should be implemented separately in the
281+
# rule evaluation logic when trusted_task_rules is used.
282+
283+
# Invalid effective_on date format
284+
invalid_date_rules := {"allow": [{
285+
"name": "Invalid date",
286+
"pattern": "oci://quay.io/konflux-ci/tekton-catalog/*",
287+
"effective_on": "not-a-date",
288+
}]}
289+
expected_date := {{
290+
# regal ignore:line-length
291+
"message": "trusted_task_rules data has unexpected format: allow.0.effective_on: Does not match format 'date'",
292+
"severity": "failure",
293+
}}
294+
lib.assert_equal(tekton.data_errors, expected_date) with data.rule_data.trusted_task_rules as invalid_date_rules
295+
296+
# Invalid structure - not an object
297+
lib.assert_empty(tekton.data_errors) with data.rule_data.trusted_task_rules as [] # Empty list is skipped
298+
299+
# Invalid allow/deny - not arrays
300+
invalid_structure := {
301+
"allow": "not-an-array",
302+
"deny": [{
303+
"name": "Valid deny",
304+
"pattern": "oci://quay.io/konflux-ci/tekton-catalog/*",
305+
}],
306+
}
307+
expected_structure := {{
308+
"message": "trusted_task_rules data has unexpected format: allow: Invalid type. Expected: array, given: string",
309+
"severity": "failure",
310+
}}
311+
lib.assert_equal(tekton.data_errors, expected_structure) with data.rule_data.trusted_task_rules as invalid_structure
312+
313+
# Empty versions array (should fail minItems: 1)
314+
invalid_versions := {"allow": [{
315+
"name": "Empty versions",
316+
"pattern": "oci://quay.io/konflux-ci/tekton-catalog/*",
317+
"versions": [],
318+
}]}
319+
expected_versions := {{
320+
"message": "trusted_task_rules data has unexpected format: allow.0.versions: Array must have at least 1 items",
321+
"severity": "failure",
322+
}}
323+
lib.assert_equal(tekton.data_errors, expected_versions) with data.rule_data.trusted_task_rules as invalid_versions
324+
}
325+
241326
trusted_bundle_task := {"spec": {"taskRef": {"resolver": "bundles", "params": [
242327
{"name": "bundle", "value": "registry.local/trusty:1.0@sha256:digest"},
243328
{"name": "name", "value": "trusty"},

0 commit comments

Comments
 (0)