csprofiles: fix default decision duration, lint (#2703)

* return nil with errors
* errors.Wrap -> fmt.Errorf
* var -> const
* fix default decision duration
* lint (whitespace)
This commit is contained in:
mmetc 2024-01-12 15:18:59 +01:00 committed by GitHub
parent 0ef5f20aa7
commit 733f5e165b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -6,7 +6,6 @@ import (
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/vm"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
@ -22,19 +21,23 @@ type Runtime struct {
Logger *log.Entry `json:"-" yaml:"-"`
}
var defaultDuration = "4h"
const defaultDuration = "4h"
func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
var err error
profilesRuntime := make([]*Runtime, 0)
for _, profile := range profilesCfg {
var runtimeFilter, runtimeDurationExpr *vm.Program
runtime := &Runtime{}
xlog := log.New()
if err := types.ConfigureLogger(xlog); err != nil {
log.Fatalf("While creating profiles-specific logger : %s", err)
}
xlog.SetLevel(log.InfoLevel)
runtime.Logger = xlog.WithFields(log.Fields{
"type": "profile",
@ -43,17 +46,20 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
runtime.RuntimeFilters = make([]*vm.Program, len(profile.Filters))
runtime.Cfg = profile
if runtime.Cfg.OnSuccess != "" && runtime.Cfg.OnSuccess != "continue" && runtime.Cfg.OnSuccess != "break" {
return []*Runtime{}, fmt.Errorf("invalid 'on_success' for '%s': %s", profile.Name, runtime.Cfg.OnSuccess)
}
if runtime.Cfg.OnFailure != "" && runtime.Cfg.OnFailure != "continue" && runtime.Cfg.OnFailure != "break" && runtime.Cfg.OnFailure != "apply" {
return []*Runtime{}, fmt.Errorf("invalid 'on_failure' for '%s' : %s", profile.Name, runtime.Cfg.OnFailure)
}
for fIdx, filter := range profile.Filters {
if runtimeFilter, err = expr.Compile(filter, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
return []*Runtime{}, errors.Wrapf(err, "error compiling filter of '%s'", profile.Name)
if runtime.Cfg.OnSuccess != "" && runtime.Cfg.OnSuccess != "continue" && runtime.Cfg.OnSuccess != "break" {
return nil, fmt.Errorf("invalid 'on_success' for '%s': %s", profile.Name, runtime.Cfg.OnSuccess)
}
if runtime.Cfg.OnFailure != "" && runtime.Cfg.OnFailure != "continue" && runtime.Cfg.OnFailure != "break" && runtime.Cfg.OnFailure != "apply" {
return nil, fmt.Errorf("invalid 'on_failure' for '%s' : %s", profile.Name, runtime.Cfg.OnFailure)
}
for fIdx, filter := range profile.Filters {
if runtimeFilter, err = expr.Compile(filter, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
return nil, fmt.Errorf("error compiling filter of '%s': %w", profile.Name, err)
}
runtime.RuntimeFilters[fIdx] = runtimeFilter
if profile.Debug != nil && *profile.Debug {
runtime.Logger.Logger.SetLevel(log.DebugLevel)
@ -62,8 +68,9 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
if profile.DurationExpr != "" {
if runtimeDurationExpr, err = expr.Compile(profile.DurationExpr, exprhelpers.GetExprOptions(map[string]interface{}{"Alert": &models.Alert{}})...); err != nil {
return []*Runtime{}, errors.Wrapf(err, "error compiling duration_expr of %s", profile.Name)
return nil, fmt.Errorf("error compiling duration_expr of %s: %w", profile.Name, err)
}
runtime.RuntimeDurationExpr = runtimeDurationExpr
}
@ -76,14 +83,16 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
runtime.Logger.Warningf("No duration specified for %s, using default duration %s", profile.Name, defaultDuration)
duration = defaultDuration
}
if _, err := time.ParseDuration(duration); err != nil {
return []*Runtime{}, errors.Wrapf(err, "error parsing duration '%s' of %s", duration, profile.Name)
return nil, fmt.Errorf("error parsing duration '%s' of %s: %w", duration, profile.Name, err)
}
}
}
profilesRuntime = append(profilesRuntime, runtime)
}
return profilesRuntime, nil
}
@ -110,30 +119,29 @@ func (Profile *Runtime) GenerateDecisionFromProfile(Alert *models.Alert) ([]*mod
*decision.Scope = *Alert.Source.Scope
}
/*some fields are populated from the reference object : duration, scope, type*/
decision.Duration = new(string)
if refDecision.Duration != nil {
*decision.Duration = *refDecision.Duration
}
if Profile.Cfg.DurationExpr != "" && Profile.RuntimeDurationExpr != nil {
profileDebug := false
if Profile.Cfg.Debug != nil && *Profile.Cfg.Debug {
profileDebug = true
}
duration, err := exprhelpers.Run(Profile.RuntimeDurationExpr, map[string]interface{}{"Alert": Alert}, Profile.Logger, profileDebug)
if err != nil {
Profile.Logger.Warningf("Failed to run duration_expr : %v", err)
*decision.Duration = *refDecision.Duration
} else {
durationStr := fmt.Sprint(duration)
if _, err := time.ParseDuration(durationStr); err != nil {
Profile.Logger.Warningf("Failed to parse expr duration result '%s'", duration)
*decision.Duration = *refDecision.Duration
} else {
*decision.Duration = durationStr
}
}
} else {
if refDecision.Duration == nil {
*decision.Duration = defaultDuration
}
*decision.Duration = *refDecision.Duration
}
decision.Type = new(string)
@ -144,13 +152,16 @@ func (Profile *Runtime) GenerateDecisionFromProfile(Alert *models.Alert) ([]*mod
*decision.Value = *Alert.Source.Value
decision.Origin = new(string)
*decision.Origin = types.CrowdSecOrigin
if refDecision.Origin != nil {
*decision.Origin = fmt.Sprintf("%s/%s", *decision.Origin, *refDecision.Origin)
}
decision.Scenario = new(string)
*decision.Scenario = *Alert.Scenario
decisions = append(decisions, &decision)
}
return decisions, nil
}
@ -159,16 +170,19 @@ func (Profile *Runtime) EvaluateProfile(Alert *models.Alert) ([]*models.Decision
var decisions []*models.Decision
matched := false
for eIdx, expression := range Profile.RuntimeFilters {
debugProfile := false
if Profile.Cfg.Debug != nil && *Profile.Cfg.Debug {
debugProfile = true
}
output, err := exprhelpers.Run(expression, map[string]interface{}{"Alert": Alert}, Profile.Logger, debugProfile)
if err != nil {
Profile.Logger.Warningf("failed to run profile expr for %s: %v", Profile.Cfg.Name, err)
return nil, matched, errors.Wrapf(err, "while running expression %s", Profile.Cfg.Filters[eIdx])
return nil, matched, fmt.Errorf("while running expression %s: %w", Profile.Cfg.Filters[eIdx], err)
}
switch out := output.(type) {
case bool:
if out {
@ -176,7 +190,7 @@ func (Profile *Runtime) EvaluateProfile(Alert *models.Alert) ([]*models.Decision
/*the expression matched, create the associated decision*/
subdecisions, err := Profile.GenerateDecisionFromProfile(Alert)
if err != nil {
return nil, matched, errors.Wrapf(err, "while generating decision from profile %s", Profile.Cfg.Name)
return nil, matched, fmt.Errorf("while generating decision from profile %s: %w", Profile.Cfg.Name, err)
}
decisions = append(decisions, subdecisions...)
@ -189,9 +203,7 @@ func (Profile *Runtime) EvaluateProfile(Alert *models.Alert) ([]*models.Decision
default:
return nil, matched, fmt.Errorf("unexpected type %t (%v) while running '%s'", output, output, Profile.Cfg.Filters[eIdx])
}
}
return decisions, matched, nil