From 7b5406eedf5ef40ac9c6a916af0520888ee9933a Mon Sep 17 00:00:00 2001 From: netliomax25-code Date: Tue, 2 Jun 2026 12:00:39 +0530 Subject: [PATCH] check struct field name is a string in Fetch and get --- builtin/builtin_test.go | 20 ++++++++++++++++---- builtin/lib.go | 5 ++++- vm/runtime/runtime.go | 5 ++++- vm/vm_test.go | 16 ++++++++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/builtin/builtin_test.go b/builtin/builtin_test.go index 0d0dec35..58451fce 100644 --- a/builtin/builtin_test.go +++ b/builtin/builtin_test.go @@ -211,6 +211,18 @@ func TestBuiltin(t *testing.T) { } } +func TestBuiltin_get_struct_non_string_key(t *testing.T) { + type anyEnv struct{ Foo any } + env := anyEnv{Foo: mock.Foo{Value: "a"}} + + program, err := expr.Compile(`get(Foo, 1)`, expr.Env(env)) + require.NoError(t, err) + + out, err := expr.Run(program, env) + require.NoError(t, err) + require.Nil(t, out) +} + func TestBuiltin_works_with_any(t *testing.T) { config := map[string]struct { arity int @@ -874,10 +886,10 @@ func TestAbs_UnsignedIntegers(t *testing.T) { // Test that abs() correctly handles unsigned integers // Unsigned integers are always non-negative, so abs() should return them unchanged tests := []struct { - name string - env map[string]any - expr string - want any + name string + env map[string]any + expr string + want any }{ {"uint", map[string]any{"x": uint(42)}, "abs(x)", uint(42)}, {"uint8", map[string]any{"x": uint8(42)}, "abs(x)", uint8(42)}, diff --git a/builtin/lib.go b/builtin/lib.go index 61748da0..27118b43 100644 --- a/builtin/lib.go +++ b/builtin/lib.go @@ -595,7 +595,10 @@ func get(params ...any) (out any, err error) { } case reflect.Struct: - fieldName := i.(string) + fieldName, ok := i.(string) + if !ok { + break + } t := v.Type() field, ok := t.FieldByNameFunc(func(name string) bool { f, _ := t.FieldByName(name) diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index 529fbdee..cad4f6c7 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -73,7 +73,10 @@ func Fetch(from, i any) any { } case reflect.Struct: - fieldName := i.(string) + fieldName, ok := i.(string) + if !ok { + break + } t := v.Type() key := fieldCacheKey{ t: t, diff --git a/vm/vm_test.go b/vm/vm_test.go index c86183ca..36c17d00 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -212,6 +212,22 @@ func TestRun_MethodWithError(t *testing.T) { require.Equal(t, "error", selfErr.Error()) } +func TestRun_FetchStructNonStringKey(t *testing.T) { + type Foo struct{ Value string } + type anyEnv struct { + Foo any + I any + } + env := anyEnv{Foo: Foo{Value: "a"}, I: 1} + + program, err := expr.Compile(`Foo[I]`, expr.Env(env)) + require.NoError(t, err) + + _, err = expr.Run(program, env) + require.Error(t, err) + require.Contains(t, err.Error(), "cannot fetch") +} + func TestRun_FastMethods(t *testing.T) { input := `hello() + world()`