skip to content

How do you prove a Kyverno validate.pattern actually blocks, given that a bad anchor fails open?

level: seniorimportance: should knowfreq 38%

answer

  1. anchors fail open, not closed
  2. green proves nothing without a rejection
  3. write the negative fixture first
  4. assert the rule name and the field path
  5. zero violations estate-wide is a smell

basics

~20 s

Test with resources that must be rejected, not only ones that must pass. A bad anchor makes a Kyverno pattern assert nothing and report no violation, so green runs prove nothing until a non-compliant fixture is rejected.

solid answer

~50 s

The failure mode of pattern anchors is asymmetric. Misspell an unanchored key and everything fails loudly, so you find out in minutes. Anchor a key you meant to require, or write `"*"` where you meant `"?*"`, and the rule asserts nothing while looking correct — nobody ever finds out. So every rule ships with two fixtures: a compliant pod that must be admitted and a deliberately broken one that must be rejected, and the negative fixture asserts the rule name and the failing path, for example `/spec/containers/0/resources/limits/memory/`, not merely that something failed. Run both through the Kyverno CLI (`kyverno apply` against the resource, or a `kyverno test` file) in CI, so any later anchor edit re-runs them. Then sanity-check against real workloads: a rule reporting zero violations across a large estate is usually broken, not universally obeyed.

go deeper

for a junior

Know that a policy which never rejects anything is not automatically a working policy, and that you test it by feeding it a resource you know is non-compliant.

for a middle

Be able to list the ways a pattern silently asserts nothing — an anchor with no peers, a star that matches empty, a misspelled anchored key — and explain why none of them produces an error.

for a senior

Demonstrate the working practice: paired fixtures in the policy repo, the expected rule name and field path asserted, CLI evaluation in CI, and an estate-wide dry run whose violation count you actually reason about.

for a principal

Own the standard that no rule reaches enforcement without a fixture proving it rejects, because an untested guardrail costs the same to operate as a real one and buys nothing you can show an auditor.

## Why patterns fail open A validate pattern produces one of two outcomes: a violation, or nothing. There is no third outcome for "this rule was incoherent". Kyverno will happily accept a pattern that can never produce a violation, because such a pattern is syntactically valid — it is just a set of conditions with nothing hanging off them. That is the structural reason a policy author cannot trust silence. The catalogue of silent no-ops is short and worth memorising: - **A conditional anchor with no peers.** `(resources): {limits: {memory: "?*"}}` alone is an `if` with no `then`; it asserts nothing. - **A star where you meant question-mark-star.** `"*"` matches the empty string, so it barely checks anything beyond key presence. - **A conditional anchor where you needed an equality anchor.** With `()`, a key that exists with the wrong value skips the peers silently; with `=()` it fails. - **An existence anchor on a universal rule.** `^(containers)` lets one compliant container answer for all the others. - **A misspelled anchored key.** `(resoures)` is a condition that is never true, so the block is skipped forever. - **A rule that matches nothing.** If the match block never selects the kind you thought, the rule is never even evaluated. Notice the asymmetry: the misspelling that is *unanchored* — `resoures:` as a required key — rejects every pod on day one and someone pages you within the hour. The same typo inside an anchor is invisible for as long as the rule exists. The louder failure is the safer one. ## Negative fixtures are the actual test The habit that fixes this is to write the rejection first. For every rule, keep two manifests next to the policy in the same repository: 1. A compliant pod — every container with `resources.limits.memory` and `ephemeral-storage`, every `emptyDir` with a `sizeLimit` — that must be **admitted**. 2. A deliberately non-compliant pod — one container missing the memory limit, ideally not the first one — that must be **rejected**. Run both through the Kyverno CLI: `kyverno apply` evaluates a policy against resource manifests offline, and `kyverno test` runs a declarative test file that states the expected result per policy, rule and resource. Wire that into CI on the policy repository so that the day someone "simplifies" a pattern by wrapping a key in parentheses, the negative fixture goes green-when-it-should-be-red and the pipeline stops them. ## Assert the path, not just the failure A negative fixture that only checks "something was rejected" is weak. A pod can be rejected by the wrong rule, or by the right rule for the wrong reason — for example the missing `initContainers` key rather than the missing memory limit. So assert the message content: the rule name and the field path Kyverno reports, such as `rule require-container-limits failed at path /spec/containers/1/resources/limits/memory/`. The index in that path is what proves the check reached the container you intended, and not the compliant one at index zero. Putting a non-compliant container second in the fixture is a cheap way to catch a rule that only ever inspects the first element. ## The estate-level smell test Before a rule is trusted, run it across a dump of manifests from real namespaces rather than only your two fixtures. The signal to look for is the violation count. Nobody's estate is fully compliant with a new resource-limits rule; a rule that finds zero violations across hundreds of workloads is telling you about the rule, not about the workloads. Conversely, a rule that flags every pod including ones you know are correct usually has an unanchored optional key — the `initContainers` trap — rather than a fleet-wide problem. ## When it fires, make it legible All this verification pays off at the moment somebody's deploy stops. You want to be able to say: this rule, this field, this container, and here is the fixture that proves it has behaved this way since the day it merged. That converts an argument about whether the gate is broken into a two-minute fix, which is most of what keeps a policy library alive in a team that has to ship.

  • A rule reports zero violations across the whole estate. What do you check first?
    Whether it can produce a violation at all. Look for a conditional anchor with no peer keys, a `"*"` value that accepts empty, or a misspelled anchored key that is never true. Then confirm the match block actually selects the kind you tested. Only after all three come back clean is universal compliance a credible explanation.
  • Why is a misspelled unanchored key less dangerous than a misspelled anchored one?
    An unanchored key is required, so a typo makes it a field nobody has and every resource is rejected — the failure is immediate, obvious and reverted within the hour. A typo inside an anchor produces a condition that never holds, the block is skipped forever, and nothing ever looks wrong. Loud failures get fixed; silent ones ship.
  • What should the non-compliant fixture look like beyond simply being wrong?
    Put the defect somewhere the rule could plausibly miss: on the second container rather than the first, on an init container as well as an app container, and on one field at a time so each assertion is proven separately. Then assert the exact failing path in the expected message, so a refactor that moves the check elsewhere still trips the test.

saying these in an interview costs you the question

  • Only tests resources that should pass
  • Treats a green admission run as proof the rule works
  • Assumes Kyverno errors on a rule that asserts nothing
  • Reads zero violations as full compliance
  • Asserts only that something was rejected, not why

context