diff --git a/expr_test.go b/expr_test.go index bd099a276..8b7856a43 100644 --- a/expr_test.go +++ b/expr_test.go @@ -2678,3 +2678,37 @@ func TestExpr_crash(t *testing.T) { _, err = expr.Compile(string(content)) require.Error(t, err) } + +func TestExpr_nil_op_str(t *testing.T) { + // Let's test operators, which do `.(string)` in VM, also check for nil. + + var str *string = nil + env := map[string]any{ + "nilString": str, + } + + tests := []struct{ code string }{ + {`nilString == "str"`}, + {`nilString contains "str"`}, + {`nilString matches "str"`}, + {`nilString startsWith "str"`}, + {`nilString endsWith "str"`}, + + {`"str" == nilString`}, + {`"str" contains nilString`}, + {`"str" matches nilString`}, + {`"str" startsWith nilString`}, + {`"str" endsWith nilString`}, + } + + for _, tt := range tests { + t.Run(tt.code, func(t *testing.T) { + program, err := expr.Compile(tt.code) + require.NoError(t, err) + + output, err := expr.Run(program, env) + require.NoError(t, err) + require.Equal(t, false, output) + }) + } +} diff --git a/vm/vm.go b/vm/vm.go index 7e933ce74..fa1223b42 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -274,31 +274,50 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) { case OpMatches: b := vm.pop() a := vm.pop() + if runtime.IsNil(a) || runtime.IsNil(b) { + vm.push(false) + break + } match, err := regexp.MatchString(b.(string), a.(string)) if err != nil { panic(err) } - vm.push(match) case OpMatchesConst: a := vm.pop() + if runtime.IsNil(a) { + vm.push(false) + break + } r := program.Constants[arg].(*regexp.Regexp) vm.push(r.MatchString(a.(string))) case OpContains: b := vm.pop() a := vm.pop() + if runtime.IsNil(a) || runtime.IsNil(b) { + vm.push(false) + break + } vm.push(strings.Contains(a.(string), b.(string))) case OpStartsWith: b := vm.pop() a := vm.pop() + if runtime.IsNil(a) || runtime.IsNil(b) { + vm.push(false) + break + } vm.push(strings.HasPrefix(a.(string), b.(string))) case OpEndsWith: b := vm.pop() a := vm.pop() + if runtime.IsNil(a) || runtime.IsNil(b) { + vm.push(false) + break + } vm.push(strings.HasSuffix(a.(string), b.(string))) case OpSlice: