5050 ErrProtectNotEnabled = errors .New ("the protect template function is not enabled in this mode" )
5151 ErrNewLinesNotAllowed = errors .New ("new lines are not allowed in the string passed to the toLiteral function" )
5252 ErrInvalidContextType = errors .New (
53- "the input context must be a struct with fields (recursively) of type string, map[string]string, " +
54- "map[string]interface{}, or struct" ,
53+ "the input context must be a struct that recurses to kinds bool, int, float, or string" ,
5554 )
5655 ErrMissingNamespace = errors .New (
5756 "the lookup of a single namespaced resource must have a namespace specified" ,
@@ -385,10 +384,10 @@ func getValidContext(value interface{}) (interface{}, error) {
385384
386385 // Require the context to be a struct
387386 if reflect .TypeOf (value ).Kind () != reflect .Struct {
388- return nil , fmt .Errorf ("%w, got %s" , ErrInvalidContextType , reflect .TypeOf (value ))
387+ return nil , fmt .Errorf ("%w, but found a parent of kind %s" , ErrInvalidContextType , reflect .TypeOf (value ))
389388 }
390389
391- // Require the context to have fields of strings or maps/ structs with string/map values/fields.
390+ // Require the context to recurse to primitive keys and values through structs, maps, or arrays
392391 err := getValidContextHelper (value )
393392 if err != nil {
394393 return nil , err
@@ -397,49 +396,85 @@ func getValidContext(value interface{}) (interface{}, error) {
397396 return value , nil
398397}
399398
400- func getValidContextHelper (value interface {}) error {
399+ // isPrimitive detects primitive types from the reflect package
400+ func isPrimitive (kind reflect.Kind ) bool {
401+ switch kind {
402+ case
403+ reflect .Bool ,
404+ reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
405+ reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
406+ reflect .Float32 , reflect .Float64 ,
407+ reflect .String :
408+ return true
409+
410+ default :
411+ return false
412+ }
413+ }
414+
415+ func getValidContextHelper (value any ) error {
401416 f := reflect .TypeOf (value )
402417
403- switch f . Kind () {
404- case reflect . String :
418+ // Allow primitive types (excludes complex numbers)
419+ if isPrimitive ( f . Kind ()) {
405420 return nil
421+ }
422+
423+ // Handle complex types
424+ switch f .Kind () {
425+ // Allow arrays and recurse into each item
426+ case reflect .Slice , reflect .Array :
427+ // Iterate over embedded maps and interfaces
428+ if f .Elem ().Kind () == reflect .Interface || f .Elem ().Kind () == reflect .Map {
429+ for i := range reflect .ValueOf (value ).Len () {
430+ err := getValidContextHelper (reflect .ValueOf (value ).Index (i ).Interface ())
431+ if err != nil {
432+ return err
433+ }
434+ }
435+ } else if ! isPrimitive (f .Elem ().Kind ()) {
436+ // Verify map values are primitive
437+ return fmt .Errorf ("%w, found an array with values of kind %s" , ErrInvalidContextType , reflect .TypeOf (value ))
438+ }
439+
440+ // Allow structs and recurse into fields
406441 case reflect .Struct :
407442 for i := range f .NumField () {
408- err := getValidContextHelper (reflect .ValueOf (value ).Field (i ).Interface ())
409- if err != nil {
410- return err
443+ // Only handle exported struct fields (causes a panic calling Interface())
444+ if f .Field (i ).IsExported () {
445+ err := getValidContextHelper (reflect .ValueOf (value ).Field (i ).Interface ())
446+ if err != nil {
447+ return err
448+ }
411449 }
412450 }
413451
414- return nil
452+ // Allow maps and recurse into keys
415453 case reflect .Map :
416- // Only allow string keys in maps
417- if f .Key ().Kind () != reflect . String {
418- return ErrInvalidContextType
454+ // Only allow primitive keys in maps
455+ if ! isPrimitive ( f .Key ().Kind ()) {
456+ return fmt . Errorf ( "%w, found a map with keys of kind %s" , ErrInvalidContextType , f . Key (). Kind ())
419457 }
420458
421- // Check if it's a map of maps/strings. This is to allow things such as the Kubernetes metadata field which has
422- // string (e.g. name) and map[string]string (e.g. labels) values.
459+ // Iterate over embedded maps and interfaces
423460 if f .Elem ().Kind () == reflect .Interface || f .Elem ().Kind () == reflect .Map {
424461 for _ , key := range reflect .ValueOf (value ).MapKeys () {
425462 err := getValidContextHelper (reflect .ValueOf (value ).MapIndex (key ).Interface ())
426463 if err != nil {
427464 return err
428465 }
429466 }
430-
431- return nil
432- }
433-
434- // Check if it's map[string]string
435- if f .Elem ().Kind () != reflect .String {
436- return ErrInvalidContextType
467+ } else if ! isPrimitive (f .Elem ().Kind ()) {
468+ // Verify map values are primitive
469+ return fmt .Errorf ("%w, found a map with values of kind %s" , ErrInvalidContextType , reflect .TypeOf (value ))
437470 }
438471
439- return nil
472+ // Disallow all else like pointers and channels
440473 default :
441- return ErrInvalidContextType
474+ return fmt . Errorf ( "%w, found value of kind %s" , ErrInvalidContextType , reflect . TypeOf ( value ))
442475 }
476+
477+ return nil
443478}
444479
445480// validateEncryptionConfig validates an EncryptionConfig struct to ensure that if encryption
0 commit comments