mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-10 20:05:55 +02:00
fix: avoid possible race condition while compiling expressions (#3582)
This commit is contained in:
parent
4004868245
commit
a0fab0ac5a
1 changed files with 80 additions and 27 deletions
|
@ -57,6 +57,13 @@ var dbClient *database.Client
|
|||
|
||||
var exprFunctionOptions []expr.Option
|
||||
|
||||
func init() { //nolint:gochecknoinits
|
||||
exprFunctionOptions = make([]expr.Option, len(exprFuncs))
|
||||
for i, fn := range exprFuncs {
|
||||
exprFunctionOptions[i] = expr.Function(fn.name, fn.function, fn.signature...)
|
||||
}
|
||||
}
|
||||
|
||||
var keyValuePattern = regexp.MustCompile(`(?P<key>[^=\s]+)=(?:"(?P<quoted_value>[^"\\]*(?:\\.[^"\\]*)*)"|(?P<value>[^=\s]+)|\s*)`)
|
||||
|
||||
var (
|
||||
|
@ -65,23 +72,13 @@ var (
|
|||
geoIPRangeReader *maxminddb.Reader
|
||||
)
|
||||
|
||||
func GetExprOptions(ctx map[string]interface{}) []expr.Option {
|
||||
if len(exprFunctionOptions) == 0 {
|
||||
exprFunctionOptions = []expr.Option{}
|
||||
for _, function := range exprFuncs {
|
||||
exprFunctionOptions = append(exprFunctionOptions,
|
||||
expr.Function(function.name,
|
||||
function.function,
|
||||
function.signature...,
|
||||
))
|
||||
}
|
||||
}
|
||||
func GetExprOptions(ctx map[string]any) []expr.Option {
|
||||
// copy the pre‑built options + one Env(...) for this call
|
||||
opts := make([]expr.Option, len(exprFunctionOptions)+1)
|
||||
copy(opts, exprFunctionOptions)
|
||||
opts[len(opts)-1] = expr.Env(ctx)
|
||||
|
||||
ret := []expr.Option{}
|
||||
ret = append(ret, exprFunctionOptions...)
|
||||
ret = append(ret, expr.Env(ctx))
|
||||
|
||||
return ret
|
||||
return opts
|
||||
}
|
||||
|
||||
func GeoIPInit(datadir string) error {
|
||||
|
@ -199,6 +196,7 @@ func FileInit(fileFolder string, filename string, fileType string) error {
|
|||
log.Debugf("ignored file %s%s because already loaded", fileFolder, filename)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -244,13 +242,13 @@ func Distinct(params ...any) (any, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
array := params[0].([]interface{})
|
||||
array := params[0].([]any)
|
||||
if array == nil {
|
||||
return []interface{}{}, nil
|
||||
return []any{}, nil
|
||||
}
|
||||
|
||||
exists := make(map[any]bool)
|
||||
ret := make([]interface{}, 0)
|
||||
ret := make([]any, 0)
|
||||
|
||||
for _, val := range array {
|
||||
if _, ok := exists[val]; !ok {
|
||||
|
@ -270,7 +268,7 @@ func Flatten(params ...any) (any, error) {
|
|||
return flatten(nil, reflect.ValueOf(params)), nil
|
||||
}
|
||||
|
||||
func flatten(args []interface{}, v reflect.Value) []interface{} {
|
||||
func flatten(args []any, v reflect.Value) []any {
|
||||
if v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
@ -501,9 +499,11 @@ func RegexpInFile(params ...any) (any, error) {
|
|||
|
||||
// func IpInRange(ip string, ipRange string) bool {
|
||||
func IpInRange(params ...any) (any, error) {
|
||||
var err error
|
||||
var ipParsed net.IP
|
||||
var ipRangeParsed *net.IPNet
|
||||
var (
|
||||
err error
|
||||
ipParsed net.IP
|
||||
ipRangeParsed *net.IPNet
|
||||
)
|
||||
|
||||
ip := params[0].(string)
|
||||
ipRange := params[1].(string)
|
||||
|
@ -513,13 +513,16 @@ func IpInRange(params ...any) (any, error) {
|
|||
log.Debugf("'%s' is not a valid IP", ip)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if _, ipRangeParsed, err = net.ParseCIDR(ipRange); err != nil {
|
||||
log.Debugf("'%s' is not a valid IP Range", ipRange)
|
||||
return false, nil //nolint:nilerr // This helper did not return an error before the move to expr.Function, we keep this behavior for backward compatibility
|
||||
}
|
||||
|
||||
if ipRangeParsed.Contains(ipParsed) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -527,6 +530,7 @@ func IpInRange(params ...any) (any, error) {
|
|||
func IsIPV6(params ...any) (any, error) {
|
||||
ip := params[0].(string)
|
||||
ipParsed := net.ParseIP(ip)
|
||||
|
||||
if ipParsed == nil {
|
||||
log.Debugf("'%s' is not a valid IP", ip)
|
||||
return false, nil
|
||||
|
@ -540,10 +544,12 @@ func IsIPV6(params ...any) (any, error) {
|
|||
func IsIPV4(params ...any) (any, error) {
|
||||
ip := params[0].(string)
|
||||
ipParsed := net.ParseIP(ip)
|
||||
|
||||
if ipParsed == nil {
|
||||
log.Debugf("'%s' is not a valid IP", ip)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return ipParsed.To4() != nil, nil
|
||||
}
|
||||
|
||||
|
@ -551,10 +557,12 @@ func IsIPV4(params ...any) (any, error) {
|
|||
func IsIP(params ...any) (any, error) {
|
||||
ip := params[0].(string)
|
||||
ipParsed := net.ParseIP(ip)
|
||||
|
||||
if ipParsed == nil {
|
||||
log.Debugf("'%s' is not a valid IP", ip)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
@ -563,6 +571,7 @@ func IpToRange(params ...any) (any, error) {
|
|||
ip := params[0].(string)
|
||||
cidr := params[1].(string)
|
||||
cidr = strings.TrimPrefix(cidr, "/")
|
||||
|
||||
mask, err := strconv.Atoi(cidr)
|
||||
if err != nil {
|
||||
log.Errorf("bad cidr '%s': %s", cidr, err)
|
||||
|
@ -574,11 +583,13 @@ func IpToRange(params ...any) (any, error) {
|
|||
log.Errorf("can't parse IP address '%s'", ip)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
ipRange := iplib.NewNet(ipAddr, mask)
|
||||
if ipRange.IP() == nil {
|
||||
log.Errorf("can't get cidr '%s' of '%s'", cidr, ip)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return ipRange.String(), nil
|
||||
}
|
||||
|
||||
|
@ -591,37 +602,42 @@ func TimeNow(params ...any) (any, error) {
|
|||
func ParseUri(params ...any) (any, error) {
|
||||
uri := params[0].(string)
|
||||
ret := make(map[string][]string)
|
||||
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
log.Errorf("Could not parse URI: %s", err)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
parsed, err := url.ParseQuery(u.RawQuery)
|
||||
if err != nil {
|
||||
log.Errorf("Could not parse query uri : %s", err)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
for k, v := range parsed {
|
||||
ret[k] = v
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// func KeyExists(key string, dict map[string]interface{}) bool {
|
||||
func KeyExists(params ...any) (any, error) {
|
||||
key := params[0].(string)
|
||||
dict := params[1].(map[string]interface{})
|
||||
dict := params[1].(map[string]any)
|
||||
_, ok := dict[key]
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// func GetDecisionsCount(value string) int {
|
||||
func GetDecisionsCount(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
|
||||
if dbClient == nil {
|
||||
log.Error("No database config to call GetDecisionsCount()")
|
||||
return 0, nil
|
||||
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
@ -631,6 +647,7 @@ func GetDecisionsCount(params ...any) (any, error) {
|
|||
log.Errorf("Failed to get decisions count from value '%s'", value)
|
||||
return 0, nil //nolint:nilerr // This helper did not return an error before the move to expr.Function, we keep this behavior for backward compatibility
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
|
@ -638,10 +655,12 @@ func GetDecisionsCount(params ...any) (any, error) {
|
|||
func GetDecisionsSinceCount(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
since := params[1].(string)
|
||||
|
||||
if dbClient == nil {
|
||||
log.Error("No database config to call GetDecisionsSinceCount()")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
sinceDuration, err := time.ParseDuration(since)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse since parameter '%s' : %s", since, err)
|
||||
|
@ -656,79 +675,95 @@ func GetDecisionsSinceCount(params ...any) (any, error) {
|
|||
log.Errorf("Failed to get decisions count from value '%s'", value)
|
||||
return 0, nil //nolint:nilerr // This helper did not return an error before the move to expr.Function, we keep this behavior for backward compatibility
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func GetActiveDecisionsCount(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
|
||||
if dbClient == nil {
|
||||
log.Error("No database config to call GetActiveDecisionsCount()")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
count, err := dbClient.CountActiveDecisionsByValue(ctx, value)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get active decisions count from value '%s'", value)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func GetActiveDecisionsTimeLeft(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
|
||||
if dbClient == nil {
|
||||
log.Error("No database config to call GetActiveDecisionsTimeLeft()")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
timeLeft, err := dbClient.GetActiveDecisionsTimeLeftByValue(ctx, value)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get active decisions time left from value '%s'", value)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return timeLeft, nil
|
||||
}
|
||||
|
||||
// func LookupHost(value string) []string {
|
||||
func LookupHost(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
|
||||
addresses, err := net.LookupHost(value)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to lookup host '%s' : %s", value, err)
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// func ParseUnixTime(value string) (time.Time, error) {
|
||||
func ParseUnixTime(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
//Splitting string here as some unix timestamp may have milliseconds and break ParseInt
|
||||
// Splitting string here as some unix timestamp may have milliseconds and break ParseInt
|
||||
i, err := strconv.ParseInt(strings.Split(value, ".")[0], 10, 64)
|
||||
if err != nil || i <= 0 {
|
||||
return time.Time{}, fmt.Errorf("unable to parse %s as unix timestamp", value)
|
||||
}
|
||||
|
||||
return time.Unix(i, 0), nil
|
||||
}
|
||||
|
||||
// func ParseUnix(value string) string {
|
||||
func ParseUnix(params ...any) (any, error) {
|
||||
value := params[0].(string)
|
||||
|
||||
t, err := ParseUnixTime(value)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return t.(time.Time).Format(time.RFC3339), nil
|
||||
}
|
||||
|
||||
// func ToString(value interface{}) string {
|
||||
func ToString(params ...any) (any, error) {
|
||||
value := params[0]
|
||||
|
||||
s, ok := value.(string)
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -736,6 +771,7 @@ func ToString(params ...any) (any, error) {
|
|||
func GetFromStash(params ...any) (any, error) {
|
||||
cacheName := params[0].(string)
|
||||
key := params[1].(string)
|
||||
|
||||
return cache.GetKey(cacheName, key)
|
||||
}
|
||||
|
||||
|
@ -745,6 +781,7 @@ func SetInStash(params ...any) (any, error) {
|
|||
key := params[1].(string)
|
||||
value := params[2].(string)
|
||||
expiration := params[3].(*time.Duration)
|
||||
|
||||
return cache.SetKey(cacheName, key, value, expiration), nil
|
||||
}
|
||||
|
||||
|
@ -763,12 +800,15 @@ func Match(params ...any) (any, error) {
|
|||
if pattern == "" {
|
||||
return name == "", nil
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
if pattern == "*" || pattern == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if pattern[0] == '*' {
|
||||
for i := 0; i <= len(name); i++ {
|
||||
matched, _ := Match(pattern[1:], name[i:])
|
||||
|
@ -776,11 +816,14 @@ func Match(params ...any) (any, error) {
|
|||
return matched, nil
|
||||
}
|
||||
}
|
||||
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
if pattern[0] == '?' || pattern[0] == name[0] {
|
||||
return Match(pattern[1:], name[1:])
|
||||
}
|
||||
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
|
@ -791,21 +834,24 @@ func FloatApproxEqual(params ...any) (any, error) {
|
|||
if math.Abs(float1-float2) < 1e-6 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func B64Decode(params ...any) (any, error) {
|
||||
encoded := params[0].(string)
|
||||
|
||||
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(decoded), nil
|
||||
}
|
||||
|
||||
func ParseKV(params ...any) (any, error) {
|
||||
blob := params[0].(string)
|
||||
target := params[1].(map[string]interface{})
|
||||
target := params[1].(map[string]any)
|
||||
prefix := params[2].(string)
|
||||
|
||||
matches := keyValuePattern.FindAllStringSubmatch(blob, -1)
|
||||
|
@ -813,6 +859,7 @@ func ParseKV(params ...any) (any, error) {
|
|||
log.Errorf("could not find any key/value pair in line")
|
||||
return nil, errors.New("invalid input format")
|
||||
}
|
||||
|
||||
if _, ok := target[prefix]; !ok {
|
||||
target[prefix] = make(map[string]string)
|
||||
} else {
|
||||
|
@ -822,9 +869,11 @@ func ParseKV(params ...any) (any, error) {
|
|||
return nil, errors.New("target is not a map[string]string")
|
||||
}
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
key := ""
|
||||
value := ""
|
||||
|
||||
for i, name := range keyValuePattern.SubexpNames() {
|
||||
if name == "key" {
|
||||
key = match[i]
|
||||
|
@ -834,9 +883,12 @@ func ParseKV(params ...any) (any, error) {
|
|||
value = match[i]
|
||||
}
|
||||
}
|
||||
|
||||
target[prefix].(map[string]string)[key] = value
|
||||
}
|
||||
|
||||
log.Tracef("unmarshaled KV: %+v", target[prefix])
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -845,5 +897,6 @@ func Hostname(params ...any) (any, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hostname, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue