Skip to content

Commit b04309b

Browse files
committed
deref multi-levels if interface is satisfied by pointer
If the underlying type is an interface satisfied by a pointer, we need to deref it multiple times until we reach to the struct value. This PR does that by calling `val.Elem()` multiple times while `val.Kind()` is an interface or pointer. Signed-off-by: Tiago Silva <tiago.silva@goteleport.com>
1 parent 664e4d3 commit b04309b

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

lib.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ func getFieldByTag(val reflect.Value, tagName string, fieldNames []string) (inte
166166
return nil, trace.BadParameter("missing field names")
167167
}
168168

169-
if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
169+
for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
170+
if val.IsNil() {
171+
return nil, &notFoundError{fieldNames: fieldNames}
172+
}
170173
val = val.Elem()
171174
}
172175

parse_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,55 @@ func (s *PredicateSuite) TestContainsUnexportedFieldAvoidPanic() {
439439
s.True(pr.(BoolPredicate)())
440440
}
441441

442+
func (s *PredicateSuite) TestContainsInterfacePTR() {
443+
type LocalTestStruct struct {
444+
Param struct {
445+
Key1 map[string][]string `json:"key1,omitempty"`
446+
Key2 map[string]string `json:"key2,omitempty"`
447+
} `json:"param,omitempty"`
448+
}
449+
val := LocalTestStruct{
450+
Param: struct {
451+
Key1 map[string][]string "json:\"key1,omitempty\""
452+
Key2 map[string]string "json:\"key2,omitempty\""
453+
}{
454+
Key1: map[string][]string{"key": {"a", "b", "c"}},
455+
},
456+
}
457+
458+
type testInterface interface{}
459+
460+
var iface testInterface = &val // Use a pointer to the struct
461+
462+
getID := func(fields []string) (interface{}, error) {
463+
return GetFieldByTag(iface, "json", fields[1:])
464+
}
465+
p := s.getParserWithOpts(getID, GetStringMapValue)
466+
467+
pr, err := p.Parse(`Contains(val.param.key1["key"], "a")`)
468+
s.NoError(err)
469+
s.True(pr.(BoolPredicate)())
470+
}
471+
472+
func (s *PredicateSuite) TestNilInterfaceContentCheck() {
473+
type LocalTestStruct struct {
474+
Param struct {
475+
Key1 map[string][]string `json:"key1,omitempty"`
476+
Key2 map[string]string `json:"key2,omitempty"`
477+
} `json:"param,omitempty"`
478+
}
479+
480+
var iface any = (*LocalTestStruct)(nil) // Use a nil pointer to the struct
481+
482+
getID := func(fields []string) (interface{}, error) {
483+
return GetFieldByTag(iface, "json", fields[1:])
484+
}
485+
p := s.getParserWithOpts(getID, GetStringMapValue)
486+
487+
_, err := p.Parse(`Contains(val.param.key1["key"], "a")`)
488+
s.Error(err)
489+
}
490+
442491
func (s *PredicateSuite) TestEquals() {
443492
val := TestStruct{}
444493
val.Param.Key2 = map[string]string{"key": "a"}

0 commit comments

Comments
 (0)