-
Notifications
You must be signed in to change notification settings - Fork 54
Merge pipeline-required-tasks from ruleData config #1786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9b01177
ed762e5
13a0b93
66eb73c
6c0586b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # Rule Data Merge Pattern | ||
|
|
||
| Merging data-source values with ruleData configuration values using `object.union`. | ||
| Used by `pipeline_required_tasks` and `trusted_tasks` in `policy/lib/tekton/`. | ||
|
|
||
| ## Why does `rule_data.get()` need an explicit `{}` default for merged keys? | ||
|
|
||
| `rule_data.get(key)` falls back to `[]` (empty array) when a key isn't found in any | ||
| data source. But `object.union` requires both arguments to be objects — if either is | ||
| an array, OPA returns `undefined` at runtime (see next section). So any key that will | ||
| be passed to `object.union` must have an explicit `{}` default registered in the | ||
| `defaults` map in `policy/lib/rule_data/rule_data.rego`. Without it, the merge silently | ||
| produces `undefined` and all downstream rules stop evaluating with no error output. | ||
|
|
||
| When adding a new `object.union` merge, register the key in rule_data.rego's `defaults`: | ||
|
|
||
| ```rego | ||
| defaults := { | ||
| "pipeline-required-tasks": {}, # must be {} not [] for object.union | ||
| "trusted_tasks": {}, # same reason | ||
| ... | ||
| } | ||
| ``` | ||
|
|
||
| ## Why does `object.union` fail silently instead of erroring? | ||
|
|
||
| OPA has two behaviors depending on when it can determine types: | ||
|
|
||
| - **Static types known** (literals in code): `object.union({"a": 1}, [])` produces a | ||
| compile-time `rego_type_error` — policy fails to load. | ||
| - **Dynamic types from `data`** (the common case): both arguments are typed `any`, so | ||
| OPA skips compile-time checking. At runtime, `object.union` with a non-object argument | ||
| returns `undefined` — no error, no diagnostic. The rule becomes undefined and all | ||
| consumers silently skip it. | ||
|
|
||
| This means a misconfigured data source that provides an array instead of an object will | ||
| cause policy rules to silently stop evaluating, producing zero violations and zero errors. | ||
| The `{}` default in rule_data.rego is the primary defense against this. | ||
|
|
||
| ## How does the override precedence work in the merge? | ||
|
|
||
| `object.union(A, B)` gives B full override for matching keys. In the merge pattern: | ||
|
|
||
| ```rego | ||
| pipeline_required_tasks := object.union(data["pipeline-required-tasks"], rule_data.get("pipeline-required-tasks")) | ||
| ``` | ||
|
|
||
| - A = `data["pipeline-required-tasks"]` (from data sources) | ||
| - B = `rule_data.get(...)` which checks, in priority order: | ||
| 1. `data.rule_data__configuration__` (ECP policy ruleData) | ||
| 2. `data.rule_data_custom` (custom user sources) | ||
| 3. `data.rule_data` (default data sources) | ||
| 4. `defaults` map (hardcoded `{}`) | ||
|
|
||
| When B has a matching selector key (e.g. `"docker"`), it replaces A's value entirely — | ||
| the arrays are not merged. This is intentional and matches the trusted_tasks precedent. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,12 +2,15 @@ package lib.tekton | |
|
|
||
| import rego.v1 | ||
|
|
||
| import data.lib.rule_data | ||
| import data.lib.time as ectime | ||
|
|
||
| pipeline_label := "pipelines.openshift.io/runtime" | ||
|
|
||
| task_label := "build.appstudio.redhat.com/build_type" | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] test adequacy The new Suggested fix: Add at least one test that provides pipeline-required-tasks via both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] override semantics object.union(A, B) gives ruleData config (B) full override precedence over data sources (A) for matching selector keys. This is consistent with the existing trusted_tasks pattern and is by design, but a ruleData entry for a selector key replaces the data-source value entirely rather than merging arrays. Suggested fix: Consider documenting that ruleData configuration intentionally overrides data-source required tasks per selector key, matching the trusted_tasks precedent. |
||
| pipeline_required_tasks := object.union(data["pipeline-required-tasks"], rule_data.get("pipeline-required-tasks")) | ||
|
|
||
| latest_required_pipeline_tasks(pipeline) := _merged_required_task_list(pipeline, "newest") | ||
|
|
||
| current_required_pipeline_tasks(pipeline) := _merged_required_task_list(pipeline, "most_current") | ||
|
|
@@ -19,7 +22,7 @@ required_task_list(pipeline) := pipeline_data if { | |
| selectors := pipeline_label_selectors(pipeline) | ||
| pipeline_data := [entry | | ||
| some selector in selectors | ||
| entries := object.get(data["pipeline-required-tasks"], selector, []) | ||
| entries := object.get(pipeline_required_tasks, selector, []) | ||
| some entry in entries | ||
| ] | ||
| count(pipeline_data) > 0 | ||
|
|
@@ -37,7 +40,7 @@ _merged_required_task_list(pipeline, mode) := { | |
|
|
||
| resolved := [r | | ||
| some selector in selectors | ||
| entries := object.get(data["pipeline-required-tasks"], selector, []) | ||
| entries := object.get(pipeline_required_tasks, selector, []) | ||
| count(entries) > 0 | ||
| r := _resolve(entries, mode) | ||
| ] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Clarify that
{}does not protect against wrong-typed values.The
defaultsmap is consulted only when the key is absent. If a data source explicitly supplies an array,rule_data.get(...)still returns that array andobject.unionremains undefined. Document this limitation and require type validation or a safe fallback for present, invalid values; otherwise this can still fail open with zero policy violations.🤖 Prompt for AI Agents