Skip to content

Commit eb70f94

Browse files
authored
Add checks for nil before string casts in VM (#654)
1 parent 1659c23 commit eb70f94

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

expr_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -2678,3 +2678,37 @@ func TestExpr_crash(t *testing.T) {
26782678
_, err = expr.Compile(string(content))
26792679
require.Error(t, err)
26802680
}
2681+
2682+
func TestExpr_nil_op_str(t *testing.T) {
2683+
// Let's test operators, which do `.(string)` in VM, also check for nil.
2684+
2685+
var str *string = nil
2686+
env := map[string]any{
2687+
"nilString": str,
2688+
}
2689+
2690+
tests := []struct{ code string }{
2691+
{`nilString == "str"`},
2692+
{`nilString contains "str"`},
2693+
{`nilString matches "str"`},
2694+
{`nilString startsWith "str"`},
2695+
{`nilString endsWith "str"`},
2696+
2697+
{`"str" == nilString`},
2698+
{`"str" contains nilString`},
2699+
{`"str" matches nilString`},
2700+
{`"str" startsWith nilString`},
2701+
{`"str" endsWith nilString`},
2702+
}
2703+
2704+
for _, tt := range tests {
2705+
t.Run(tt.code, func(t *testing.T) {
2706+
program, err := expr.Compile(tt.code)
2707+
require.NoError(t, err)
2708+
2709+
output, err := expr.Run(program, env)
2710+
require.NoError(t, err)
2711+
require.Equal(t, false, output)
2712+
})
2713+
}
2714+
}

vm/vm.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -274,31 +274,50 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
274274
case OpMatches:
275275
b := vm.pop()
276276
a := vm.pop()
277+
if runtime.IsNil(a) || runtime.IsNil(b) {
278+
vm.push(false)
279+
break
280+
}
277281
match, err := regexp.MatchString(b.(string), a.(string))
278282
if err != nil {
279283
panic(err)
280284
}
281-
282285
vm.push(match)
283286

284287
case OpMatchesConst:
285288
a := vm.pop()
289+
if runtime.IsNil(a) {
290+
vm.push(false)
291+
break
292+
}
286293
r := program.Constants[arg].(*regexp.Regexp)
287294
vm.push(r.MatchString(a.(string)))
288295

289296
case OpContains:
290297
b := vm.pop()
291298
a := vm.pop()
299+
if runtime.IsNil(a) || runtime.IsNil(b) {
300+
vm.push(false)
301+
break
302+
}
292303
vm.push(strings.Contains(a.(string), b.(string)))
293304

294305
case OpStartsWith:
295306
b := vm.pop()
296307
a := vm.pop()
308+
if runtime.IsNil(a) || runtime.IsNil(b) {
309+
vm.push(false)
310+
break
311+
}
297312
vm.push(strings.HasPrefix(a.(string), b.(string)))
298313

299314
case OpEndsWith:
300315
b := vm.pop()
301316
a := vm.pop()
317+
if runtime.IsNil(a) || runtime.IsNil(b) {
318+
vm.push(false)
319+
break
320+
}
302321
vm.push(strings.HasSuffix(a.(string), b.(string)))
303322

304323
case OpSlice:

0 commit comments

Comments
 (0)