In Rego, when does object.get beat a direct lookup of a field that may be missing?
answer
- three outcomes, not two
- the body stops rather than failing
- absence looks like a pass
- supply a value so evaluation continues
- object, key or path, default
basics
~20 sUse object.get whenever the field is optional and the rule must still reach a verdict. A direct reference to a missing key is undefined, so the whole rule body stops and yields nothing; object.get substitutes your default and evaluation continues.
solid answer
~40 sIn Rego a reference to a key that is not there is **undefined**, and undefined is not false — it halts that rule body, which then produces no result at all. So `input.metadata.labels.owner != "platform"` does not fire on an object with no `labels`: the comparison never runs, and the absence of the label, the thing you were checking for, is exactly what makes the check disappear. `object.get(input, ["metadata", "labels", "owner"], "")` takes the object, a key or an array of keys as a path, and a default; when the path is missing it returns the default, so the comparison still evaluates and the rule can decide. Reach for it whenever a field is genuinely optional. When the field's absence is itself the finding, the explicit `not input.metadata.labels.owner` reads better than a sentinel default.
code
rego · 11 linespackage example
# undefined when `labels` is absent, so the body stops and yields nothing
unowned_direct if {
input.metadata.labels.owner == ""
}
# "" is substituted for the missing path, so the comparison still runs
unowned_safe if {
object.get(input, ["metadata", "labels", "owner"], "") == ""
}go deeper
Memorise the three outcomes — true, false, undefined — and that a missing key gives you the third one, not false.
Explain what undefined does to the rest of the body, give the object.get signature including the array-path form, and name when explicit not is the better tool.
Demonstrate that you test the sparse input, not just the bad value, and that you can spot this defect while reviewing someone else's rule.
Own the systemic version: optional fields are everywhere, so make absence-case fixtures part of what a policy is expected to ship with rather than a habit some authors happen to have.
## Undefined is not false This is the single idea the question is testing. Rego has three outcomes for an expression, not two: true, false, and **undefined**. A reference into a key that does not exist — `input.metadata.labels.owner` on an object whose `metadata` has no `labels` — is undefined. It is not an error, it does not throw, and it does not evaluate to false. It simply stops that rule body: the remaining expressions are never reached and the rule produces no result. The consequence is the trap. Consider a rule meant to catch objects with no owner label: ``` unowned if { input.metadata.labels.owner == "" } ``` On an object with `labels: {owner: ""}` it works. On an object with no `labels` key at all — the worst case, the one with the least metadata, the one you most wanted to catch — the reference is undefined, the body dies at line one, and the rule quietly yields nothing. Whatever consumes that rule's result sees an absence, and an absence reads exactly like a clean pass. ## What `object.get` does `object.get(object, key, default)` returns the value at `key` if it is present, and `default` if it is not. The key may be a single key, or an array of keys treated as a path into nested objects. So: ``` object.get(input, ["metadata", "labels", "owner"], "") == "" ``` now evaluates on both shapes. When the path exists, you compare the real value. When any step of the path is missing, `""` is substituted, the comparison runs, and the rule reaches a verdict instead of evaporating. That is the whole benefit: **the body keeps evaluating**. Choose the default deliberately. It should be a value that makes the comparison come out the way you want for the missing case, and it should not collide with a legitimate value. If `""` is a real value someone might set, use a sentinel that cannot occur, or restructure the check. ## The alternatives, and when each is right - **`object.get` with a default** — the field is optional and you want to treat absence as a specific value. This is the workhorse. - **Explicit `not`** — `not input.metadata.labels.owner` succeeds precisely when the reference is undefined. When "the field is missing" *is* the finding, this states it directly and needs no sentinel. Note the subtlety: `not` also succeeds when the value is present but `false`, so it is a poor choice for boolean fields. - **A default rule** — `default owner := "unknown"` gives a complete rule a value when its body is undefined. It defaults *your rule*, not a lookup inside someone else's document, so it does not help with an optional input field directly, but it is how you stop a helper rule from propagating undefinedness to every caller. - **A membership test first** — `"owner" in object.keys(input.metadata.labels)` reads clearly when you want to branch on presence, though it still needs `labels` itself to exist. ## How this bites in practice Optional fields are the norm in the documents policy engines read. Labels and annotations may be absent entirely. Nested spec sections appear only when a feature is used. Resource limits are optional. Every one of those is a place where a direct reference makes a rule silently stop checking, and where the object that triggers it is the sparse, under-specified one your policy exists to catch. The defect is also invisible in the usual test: a policy author writes a fixture that has the field, sets it to a bad value, sees the rule fire, and ships. The fixture that has *no* field is the test that is missing. When you write a rule against an optional field, write the empty-object case as a test at the same time. ## What an interviewer is checking That you can say "undefined is not false" and then explain the operational consequence rather than just the slogan: a body that dies mid-way produces no result, the sparse object escapes, and nothing in the output says anything went wrong. `object.get` is the fix when you want a verdict; explicit `not` is the fix when absence is the verdict.
- When is an explicit `not` on the reference better than object.get with a default?When absence is itself the finding. `not input.metadata.labels.owner` succeeds exactly when the reference is undefined, and it says so without inventing a sentinel value that a reader has to decode. Be careful with boolean fields: `not` also succeeds when the value is present and `false`.
- How do you choose the default value you pass to object.get?Pick one that makes the missing case come out the way the policy intends, and that cannot legitimately appear in the document. If an empty string is a value someone could really set, you can no longer distinguish "absent" from "set to empty", and the rule starts reporting the wrong reason.
- How would you catch this bug before it ships?Test the sparse case explicitly. The usual fixture has the field set to a bad value and the rule fires, which proves nothing about absence. Add a fixture where the optional field, and the object containing it, are simply not there — that is the input the rule silently ignores.
A direct lookup is a form that stops processing the moment a box is blank. object.get is the same form with "if blank, write NONE and keep going" printed under the box.
saying these in an interview costs you the question
- Says a missing key evaluates to false
- Says a missing key raises an error
- Thinks the rest of the body still runs after an undefined expression
- Uses object.get everywhere, including where absence is the finding
- Picks a default that is also a legitimate value