fix: avoid possible race condition while compiling expressions (#3582)

This commit is contained in:
mmetc 2025-04-17 17:34:40 +02:00 committed by GitHub
parent 4004868245
commit a0fab0ac5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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 prebuilt 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
}