update expr (#3144)

This commit is contained in:
blotus 2024-07-22 12:14:46 +02:00 committed by GitHub
parent 30c0d8997d
commit a3d7900b5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 90 additions and 131 deletions

View file

@ -43,7 +43,7 @@ jobs:
- name: Run tests
run: |
go install github.com/kyoh86/richgo@v0.3.10
go test -coverprofile coverage.out -covermode=atomic ./... > out.txt
go test -tags expr_debug -coverprofile coverage.out -covermode=atomic ./... > out.txt
if(!$?) { cat out.txt | sed 's/ *coverage:.*of statements in.*//' | richgo testfilter; Exit 1 }
cat out.txt | sed 's/ *coverage:.*of statements in.*//' | richgo testfilter

View file

@ -77,7 +77,8 @@ ifneq (,$(DOCKER_BUILD))
LD_OPTS_VARS += -X 'github.com/crowdsecurity/go-cs-lib/version.System=docker'
endif
GO_TAGS := netgo,osusergo,sqlite_omit_load_extension
#expr_debug tag is required to enable the debug mode in expr
GO_TAGS := netgo,osusergo,sqlite_omit_load_extension,expr_debug
# this will be used by Go in the make target, some distributions require it
export PKG_CONFIG_PATH:=/usr/local/lib/pkgconfig:$(PKG_CONFIG_PATH)
@ -220,11 +221,11 @@ testenv:
.PHONY: test
test: testenv ## Run unit tests with localstack
$(GOTEST) $(LD_OPTS) ./...
$(GOTEST) --tags=$(GO_TAGS) $(LD_OPTS) ./...
.PHONY: go-acc
go-acc: testenv ## Run unit tests with localstack + coverage
go-acc ./... -o coverage.out --ignore database,notifications,protobufs,cwversion,cstest,models -- $(LD_OPTS)
go-acc ./... -o coverage.out --ignore database,notifications,protobufs,cwversion,cstest,models --tags $(GO_TAGS) -- $(LD_OPTS)
check_docker:
@if ! docker info > /dev/null 2>&1; then \

View file

@ -6,7 +6,7 @@ import (
"os"
"text/template"
"github.com/antonmedv/expr"
"github.com/expr-lang/expr"
"github.com/sanity-io/litter"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

1
go.mod
View file

@ -111,6 +111,7 @@ require (
github.com/creack/pty v1.1.18 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/expr-lang/expr v1.16.9 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect

2
go.sum
View file

@ -127,6 +127,8 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=

View file

@ -7,8 +7,8 @@ import (
"os"
"strings"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"

View file

@ -6,8 +6,8 @@ import (
"slices"
"strconv"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"

View file

@ -6,8 +6,8 @@ import (
"os"
"regexp"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"

View file

@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"

View file

@ -5,8 +5,9 @@ import (
"strconv"
"strings"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/file"
"github.com/expr-lang/expr/vm"
log "github.com/sirupsen/logrus"
)
@ -106,62 +107,30 @@ func (o *OpOutput) String() string {
return ret + ""
}
func (erp ExprRuntimeDebug) extractCode(ip int, program *vm.Program, parts []string) string {
func (erp ExprRuntimeDebug) extractCode(ip int, program *vm.Program) string {
locations := program.Locations()
src := string(program.Source())
//log.Tracef("# extracting code for ip %d [%s]", ip, parts[1])
if program.Locations[ip].Line == 0 { //it seems line is zero when it's not actual code (ie. op push at the beginning)
log.Tracef("zero location ?")
return ""
}
startLine := program.Locations[ip].Line
startColumn := program.Locations[ip].Column
lines := strings.Split(program.Source.Content(), "\n")
currentInstruction := locations[ip]
endCol := 0
endLine := 0
var closest *file.Location
for i := ip + 1; i < len(program.Locations); i++ {
if program.Locations[i].Line > startLine || (program.Locations[i].Line == startLine && program.Locations[i].Column > startColumn) {
//we didn't had values yet and it's superior to current one, take it
if endLine == 0 && endCol == 0 {
endLine = program.Locations[i].Line
endCol = program.Locations[i].Column
for i := ip + 1; i < len(locations); i++ {
if locations[i].From > currentInstruction.From {
if closest == nil || locations[i].From < closest.From {
closest = &locations[i]
}
//however, we are looking for the closest upper one
if program.Locations[i].Line < endLine || (program.Locations[i].Line == endLine && program.Locations[i].Column < endCol) {
endLine = program.Locations[i].Line
endCol = program.Locations[i].Column
}
}
}
//maybe it was the last instruction ?
if endCol == 0 && endLine == 0 {
endLine = len(lines)
endCol = len(lines[endLine-1])
}
code_snippet := ""
startLine -= 1 //line count starts at 1
endLine -= 1
for i := startLine; i <= endLine; i++ {
if i == startLine {
if startLine != endLine {
code_snippet += lines[i][startColumn:]
continue
}
code_snippet += lines[i][startColumn:endCol]
break
}
if i == endLine {
code_snippet += lines[i][:endCol]
break
}
code_snippet += lines[i]
var end int
if closest == nil {
end = len(src)
} else {
end = closest.From
}
log.Tracef("#code extract for ip %d [%s] -> '%s'", ip, parts[1], code_snippet)
return cleanTextForDebug(code_snippet)
return cleanTextForDebug(src[locations[ip].From:end])
}
func autoQuote(v any) string {
@ -189,7 +158,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
prevIdxOut = IdxOut - 1
currentDepth = outputs[prevIdxOut].CodeDepth
if outputs[prevIdxOut].Func && !outputs[prevIdxOut].Finalized {
stack := vm.Stack()
stack := vm.Stack
num_items := 1
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
outputs[prevIdxOut].FuncResults = append(outputs[prevIdxOut].FuncResults, autoQuote(stack[i]))
@ -197,7 +166,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
}
outputs[prevIdxOut].Finalized = true
} else if (outputs[prevIdxOut].Comparison || outputs[prevIdxOut].Condition) && !outputs[prevIdxOut].Finalized {
stack := vm.Stack()
stack := vm.Stack
outputs[prevIdxOut].StrConditionResult = fmt.Sprintf("%+v", stack)
if val, ok := stack[0].(bool); ok {
outputs[prevIdxOut].ConditionResult = new(bool)
@ -207,10 +176,10 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
}
}
erp.Logger.Tracef("[STEP %d:%s] (stack:%+v) (parts:%+v) {depth:%d}", ip, parts[1], vm.Stack(), parts, currentDepth)
erp.Logger.Tracef("[STEP %d:%s] (stack:%+v) (parts:%+v) {depth:%d}", ip, parts[1], vm.Stack, parts, currentDepth)
out := OpOutput{}
out.CodeDepth = currentDepth
out.Code = erp.extractCode(ip, program, parts)
out.Code = erp.extractCode(ip, program)
switch parts[1] {
case "OpBegin":
@ -221,8 +190,8 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.CodeDepth -= IndentStep
out.BlockEnd = true
//OpEnd can carry value, if it's any/all/count etc.
if len(vm.Stack()) > 0 {
out.StrConditionResult = fmt.Sprintf("%v", vm.Stack())
if len(vm.Stack) > 0 {
out.StrConditionResult = fmt.Sprintf("%v", vm.Stack)
}
outputs = append(outputs, out)
case "OpNot":
@ -241,7 +210,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.StrConditionResult = "false"
outputs = append(outputs, out)
case "OpJumpIfTrue": //OR
stack := vm.Stack()
stack := vm.Stack
out.JumpIf = true
out.IfTrue = true
out.StrConditionResult = fmt.Sprintf("%v", stack[0])
@ -252,7 +221,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
}
outputs = append(outputs, out)
case "OpJumpIfFalse": //AND
stack := vm.Stack()
stack := vm.Stack
out.JumpIf = true
out.IfFalse = true
out.StrConditionResult = fmt.Sprintf("%v", stack[0])
@ -264,7 +233,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
case "OpCall1": //Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack()
stack := vm.Stack
num_items := 1
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
@ -274,7 +243,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
case "OpCall2": //Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack()
stack := vm.Stack
num_items := 2
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
@ -284,7 +253,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
case "OpCall3": //Op for function calls
out.Func = true
out.FuncName = parts[3]
stack := vm.Stack()
stack := vm.Stack
num_items := 3
for i := len(stack) - 1; i >= 0 && num_items > 0; i-- {
out.Args = append(out.Args, autoQuote(stack[i]))
@ -297,7 +266,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
case "OpCallN": //Op for function calls with more than 3 args
out.Func = true
out.FuncName = parts[1]
stack := vm.Stack()
stack := vm.Stack
//for OpCallN, we get the number of args
if len(program.Arguments) >= ip {
@ -310,19 +279,19 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
}
}
} else { //let's blindly take the items on stack
for _, val := range vm.Stack() {
for _, val := range vm.Stack {
out.Args = append(out.Args, autoQuote(val))
}
}
outputs = append(outputs, out)
case "OpEqualString", "OpEqual", "OpEqualInt": //comparisons
stack := vm.Stack()
stack := vm.Stack
out.Comparison = true
out.Left = autoQuote(stack[0])
out.Right = autoQuote(stack[1])
outputs = append(outputs, out)
case "OpIn": //in operator
stack := vm.Stack()
stack := vm.Stack
out.Condition = true
out.ConditionIn = true
//seems that we tend to receive stack[1] as a map.
@ -332,7 +301,7 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
out.Args = append(out.Args, autoQuote(stack[1]))
outputs = append(outputs, out)
case "OpContains": //kind OpIn , but reverse
stack := vm.Stack()
stack := vm.Stack
out.Condition = true
out.ConditionContains = true
//seems that we tend to receive stack[1] as a map.
@ -347,7 +316,10 @@ func (erp ExprRuntimeDebug) ipDebug(ip int, vm *vm.VM, program *vm.Program, part
func (erp ExprRuntimeDebug) ipSeek(ip int) []string {
for i := range len(erp.Lines) {
parts := strings.Split(erp.Lines[i], "\t")
parts := strings.Fields(erp.Lines[i])
if len(parts) == 0 {
continue
}
if parts[0] == strconv.Itoa(ip) {
return parts
}
@ -371,7 +343,7 @@ func cleanTextForDebug(text string) string {
}
func DisplayExprDebug(program *vm.Program, outputs []OpOutput, logger *log.Entry, ret any) {
logger.Debugf("dbg(result=%v): %s", ret, cleanTextForDebug(program.Source.Content()))
logger.Debugf("dbg(result=%v): %s", ret, cleanTextForDebug(string(program.Source())))
for _, output := range outputs {
logger.Debugf("%s", output.String())
}
@ -383,57 +355,41 @@ func RunWithDebug(program *vm.Program, env interface{}, logger *log.Entry) ([]Op
erp := ExprRuntimeDebug{
Logger: logger,
}
debugErr := make(chan error)
var buf strings.Builder
vm := vm.Debug()
done := false
program.Opcodes(&buf)
lines := strings.Split(buf.String(), "\n")
opcodes := program.Disassemble()
lines := strings.Split(opcodes, "\n")
erp.Lines = lines
go func() {
//We must never return until the execution of the program is done
var err error
erp.Logger.Tracef("[START] ip 0")
ops := erp.ipSeek(0)
if ops == nil {
debugErr <- fmt.Errorf("failed getting ops for ip 0")
return
log.Warningf("error while debugging expr: failed getting ops for ip 0")
}
if outputs, err = erp.ipDebug(0, vm, program, ops, outputs); err != nil {
debugErr <- fmt.Errorf("error while debugging at ip 0")
log.Warningf("error while debugging expr: error while debugging at ip 0")
}
vm.Step()
for ip := range vm.Position() {
ops := erp.ipSeek(ip)
if ops == nil { //we reached the end of the program, we shouldn't throw an error
if ops == nil {
erp.Logger.Tracef("[DONE] ip %d", ip)
debugErr <- nil
return
break
}
if outputs, err = erp.ipDebug(ip, vm, program, ops, outputs); err != nil {
debugErr <- fmt.Errorf("error while debugging at ip %d", ip)
return
}
if done {
debugErr <- nil
return
log.Warningf("error while debugging expr: error while debugging at ip %d", ip)
}
vm.Step()
}
debugErr <- nil
}()
var return_error error
ret, err := vm.Run(program, env)
done = true
//if the expr runtime failed, we don't need to wait for the debug to finish
if err != nil {
return_error = err
} else {
err = <-debugErr
if err != nil {
log.Warningf("error while debugging expr: %s", err)
}
}
//the overall result of expression is the result of last op ?
if len(outputs) > 0 {

View file

@ -5,8 +5,8 @@ import (
"strings"
"testing"
"github.com/antonmedv/expr"
"github.com/davecgh/go-spew/spew"
"github.com/expr-lang/expr"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/types"
@ -52,6 +52,7 @@ type teststruct struct {
Foo string
}
// You need to add the tag expr_debug when running the tests
func TestBaseDbg(t *testing.T) {
defaultEnv := map[string]interface{}{
"queue": &types.Queue{},
@ -265,7 +266,6 @@ func TestBaseDbg(t *testing.T) {
{Code: "Upper(base_string)", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"hello world\""}, FuncResults: []string{"\"HELLO WORLD\""}, ConditionResult: (*bool)(nil), Finalized: true},
{Code: "Upper('/someotherurl?account-name=admin&account-status=1&ow=cmd') )", CodeDepth: 0, Func: true, FuncName: "Upper", Args: []string{"\"/someotherurl?account-name=admin&account...\""}, FuncResults: []string{"\"/SOMEOTHERURL?ACCOUNT-NAME=ADMIN&ACCOUNT...\""}, ConditionResult: (*bool)(nil), Finalized: true},
{Code: "contains Upper('/someotherurl?account-name=admin&account-status=1&ow=cmd') )", CodeDepth: 0, Args: []string{"\"HELLO WORLD\"", "\"/SOMEOTHERURL?ACCOUNT-NAME=ADMIN&ACCOUNT...\""}, Condition: true, ConditionContains: true, StrConditionResult: "[false]", ConditionResult: boolPtr(false), Finalized: true},
{Code: "and", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: false},
{Code: "and", CodeDepth: 0, JumpIf: true, IfFalse: true, StrConditionResult: "false", ConditionResult: boolPtr(false), Finalized: true},
},
},

View file

@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/antonmedv/expr"
"github.com/expr-lang/expr"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View file

@ -15,11 +15,11 @@ import (
"strings"
"time"
"github.com/antonmedv/expr"
"github.com/bluele/gcache"
"github.com/c-robinson/iplib"
"github.com/cespare/xxhash/v2"
"github.com/davecgh/go-spew/spew"
"github.com/expr-lang/expr"
"github.com/oschwald/geoip2-golang"
"github.com/oschwald/maxminddb-golang"
"github.com/prometheus/client_golang/prometheus"

View file

@ -3,7 +3,7 @@ package exprhelpers
import (
"testing"
"github.com/antonmedv/expr"
"github.com/expr-lang/expr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View file

@ -7,7 +7,7 @@ import (
"os"
"strings"
"github.com/antonmedv/expr"
"github.com/expr-lang/expr"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"

View file

@ -9,7 +9,7 @@ import (
"sort"
"strings"
"github.com/antonmedv/expr"
"github.com/expr-lang/expr"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"

View file

@ -3,8 +3,8 @@ package leakybucket
import (
"fmt"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
"github.com/crowdsecurity/crowdsec/pkg/types"

View file

@ -4,8 +4,8 @@ import (
"fmt"
"sync"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
"github.com/crowdsecurity/crowdsec/pkg/types"

View file

@ -11,9 +11,9 @@ import (
"sync"
"time"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/davecgh/go-spew/spew"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/goombaio/namegenerator"
log "github.com/sirupsen/logrus"
"gopkg.in/tomb.v2"

View file

@ -3,8 +3,8 @@ package leakybucket
import (
"fmt"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
"github.com/crowdsecurity/crowdsec/pkg/types"

View file

@ -3,8 +3,8 @@ package leakybucket
import (
"sync"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
"github.com/crowdsecurity/crowdsec/pkg/types"

View file

@ -3,8 +3,8 @@ package leakybucket
import (
"sync"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
"github.com/crowdsecurity/crowdsec/pkg/types"

View file

@ -3,7 +3,7 @@ package parser
import (
"time"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr/vm"
"github.com/crowdsecurity/grokky"
)

View file

@ -6,9 +6,9 @@ import (
"strings"
"time"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/davecgh/go-spew/spew"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2"
@ -202,7 +202,6 @@ func (n *Node) processWhitelist(cachedExprEnv map[string]interface{}, p *types.E
return isWhitelisted, nil
}
func (n *Node) processGrok(p *types.Event, cachedExprEnv map[string]any) (bool, bool, error) {
// Process grok if present, should be exclusive with nodes :)
clog := n.Logger

View file

@ -4,8 +4,8 @@ import (
"fmt"
"net"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
"github.com/prometheus/client_golang/prometheus"
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"

View file

@ -10,8 +10,8 @@ import (
"sort"
"github.com/Masterminds/semver/v3"
"github.com/antonmedv/expr"
"github.com/blackfireio/osinfo"
"github.com/expr-lang/expr"
"github.com/shirou/gopsutil/v3/process"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"

View file

@ -4,7 +4,7 @@ import (
"net"
"time"
"github.com/antonmedv/expr/vm"
"github.com/expr-lang/expr/vm"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/models"