JMESPath Type Safety in Kyverno#
Kyverno defines ~50 custom JMESPath functions on top of the go-jmespath library. All handlers live in pkg/engine/jmespath/functions.go. Type safety β ensuring arguments are the right kind before a handler executes β is handled through two complementary patterns: the validateArg helper and inline type-switch-with-ok-guard assertions.
The validateArg Helper#
validateArg in pkg/engine/jmespath/utils.go is the primary safe-type-extraction utility. It takes a function name string, the argument slice, a zero-based index, and the expected reflect.Kind, and performs three checks in order:
- Bounds check β returns
argOutOfBoundsErrorifindex >= len(arguments) - Nil check β returns
invalidArgumentTypeErrorifarguments[index] == nil - Kind check β returns
invalidArgumentTypeErrorifreflect.TypeOf(arguments[index]).Kind() != expectedType
On success it returns the argument as a reflect.Value, giving the caller a type-safe handle. The majority of single-type-argument handlers (e.g., jpfCompare, jpBase64Decode, jpSha256) chain validateArg calls at the top, returning early on any error before touching the value.
Error Formatting#
Error messages are assembled by formatError in pkg/engine/jmespath/error.go, which injects the function name into a fixed-format string. The key constants are :
invalidArgumentTypeErrorβ"JMESPath function '%s': argument #%d is not of type %s"argOutOfBoundsErrorβ"JMESPath function '%s': %d argument is out of bounds (%d)"genericErrorβ"JMESPath function '%s': %s"
Type-Switch-with-ok-Guard Pattern#
For functions that accept multiple concrete types (e.g., jpLookup, jpItems, jpObjectFromLists, jpLabelMatch), the codebase uses Go's two-value type assertion v, ok := arg.(T) or a switch arg.(type) to dispatch safely:
jpLabelMatchasserts both arguments tomap[string]anywith ok-guards, returninginvalidArgumentTypeErroron failure.jpLookupuses aswitch input := arguments[0].(type)to branch onmap[string]anyvs[]any, then ok-guards the key argument separately.jpItemsfollows the same switch pattern for object/array dispatch.
Known Unsafe Patterns and Bugs#
Bare type assertions (panic risk)#
jpRandom uses a bare (non-ok) type assertion arguments[0].(string) β this panics if the argument is not a string, bypassing both validateArg and the ok-guard convention.
Wrong function-name constants passed to validateArg#
Several handlers pass the wrong name constant, so invalidArgumentTypeError messages would name the wrong function :
jpPatternMatchpassesregexMatchinstead ofpatternMatch.jpIsExternalURLpassespathCanonicalizeinstead ofisExternalURL.jpBase64Decode,jpBase64Encode,jpSha256,jpSha1, andjpMd5all pass""β which renders asJMESPath function '': .... The correct constants (base64Decode,base64Encode,SHA256,SHA1,MD5) already exist in the function-name block.
validateArg errors are currently unreachable in practice#
The go-jmespath library runs its own type checker against the declared argSpec before invoking the handler. For functions that declare concrete jpType constraints (e.g., jpString, jpNumber), the library rejects the call before the handler body is ever reached, making validateArg type/arity errors dead code for those functions . As a result, users see the raw library error (Invalid type for: <nil>, expected: []jmespath.JpType{"string"}) rather than Kyverno's richer message.
Functions that declare jpAny (arithmetic ops: add, subtract, multiply, divide, modulo) or jpObject (label_match) bypass the library's type pre-check, so their validateArg and ok-guard paths are reachable.
The label_match Panic Fix (PR #15827)#
Before PR #15827, jpLabelMatch used val != value to compare label values. This panics at runtime when a label value is a non-comparable type (map, slice) β e.g., when annotations or ownerReferences are present in the metadata object. The fix replaced the direct comparison with !reflect.DeepEqual(val, value) .
Key Files#
| File | Purpose |
|---|---|
pkg/engine/jmespath/functions.go | All custom JMESPath handler implementations |
pkg/engine/jmespath/utils.go | validateArg and ifaceToString helpers |
pkg/engine/jmespath/error.go | Error format constants and formatError |
pkg/engine/jmespath/new.go | QueryProxy.Search and function registration via newImplementation |
Open Issues#
Issue #16766 tracks two related problems: (1) the library pre-empts validateArg, so user-facing type errors show raw Go syntax ([]jmespath.JpType{"string"}) instead of Kyverno's formatted messages; and (2) the wrong/empty function-name constants make the error messages misleading once that path becomes reachable. The issue proposes enriching the error at QueryProxy.Search in new.go, which is the single choke point for all engine and CLI evaluations.