diff --git a/test/fuzz/fuzz_test.go b/test/fuzz/fuzz_test.go index e12c4d8e..79f9cedd 100644 --- a/test/fuzz/fuzz_test.go +++ b/test/fuzz/fuzz_test.go @@ -66,6 +66,7 @@ func FuzzExpr(f *testing.F) { regexp.MustCompile(`invalid order .*, expected asc or desc`), regexp.MustCompile(`unknown order, use asc or desc`), regexp.MustCompile(`cannot use .* as a key for groupBy: type is not comparable`), + regexp.MustCompile(`cannot use .* as a map key: type is not comparable`), } env := NewEnv() diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index 529fbdee..ff9c9e0d 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -63,7 +63,11 @@ func Fetch(from, i any) any { if i == nil { value = v.MapIndex(reflect.Zero(v.Type().Key())) } else { - value = v.MapIndex(reflect.ValueOf(i)) + key := reflect.ValueOf(i) + if !key.Type().Comparable() { + panic(fmt.Sprintf("cannot use %s as a map key: type is not comparable", key.Type())) + } + value = v.MapIndex(key) } if value.IsValid() { return value.Interface() @@ -233,7 +237,11 @@ func In(needle any, array any) bool { if needle == nil { value = v.MapIndex(reflect.Zero(v.Type().Key())) } else { - value = v.MapIndex(reflect.ValueOf(needle)) + key := reflect.ValueOf(needle) + if !key.Type().Comparable() { + panic(fmt.Sprintf("cannot use %s as a map key: type is not comparable", key.Type())) + } + value = v.MapIndex(key) } if value.IsValid() { return true diff --git a/vm/vm_test.go b/vm/vm_test.go index c86183ca..d8b4414e 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -518,6 +518,23 @@ func TestVM_GroupAndSortOperations(t *testing.T) { } } +// TestVM_NonComparableMapKey checks that indexing a map or using the in +// operator with a non-comparable key returns a clean error instead of a raw +// "hash of unhashable type" runtime panic. +func TestVM_NonComparableMapKey(t *testing.T) { + env := map[string]any{"list": []map[string]any{{"a": 1}}} + for _, code := range []string{ + `groupBy(list, 0)[reduce(list, B"")]`, + `B"" in groupBy(list, 0)`, + } { + program, err := expr.Compile(code, expr.Env(env)) + require.NoError(t, err) + _, err = vm.Run(program, env) + require.Error(t, err) + require.Contains(t, err.Error(), "not comparable") + } +} + // TestVM_SortBy_NonStringOrder tests that sortBy with non-string order // returns a proper error instead of panicking (regression test for OSS-Fuzz #477658245). func TestVM_SortBy_NonStringOrder(t *testing.T) {