220 lines
5.5 KiB
Go
220 lines
5.5 KiB
Go
package csprofiles
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
|
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
|
)
|
|
|
|
var (
|
|
scope = "Country"
|
|
typ = "ban"
|
|
boolFalse = false
|
|
boolTrue = true
|
|
duration = "1h"
|
|
|
|
value = "CH"
|
|
scenario = "ssh-bf"
|
|
)
|
|
|
|
func TestNewProfile(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
profileCfg *csconfig.ProfileCfg
|
|
expectedNbProfile int
|
|
}{
|
|
{
|
|
name: "filter ok and duration_expr ok",
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{
|
|
"1==1",
|
|
},
|
|
DurationExpr: "1==1",
|
|
Debug: &boolFalse,
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolTrue, Duration: &duration},
|
|
},
|
|
},
|
|
expectedNbProfile: 1,
|
|
},
|
|
{
|
|
name: "filter NOK and duration_expr ok",
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{
|
|
"1==1",
|
|
"unknownExprHelper() == 'foo'",
|
|
},
|
|
DurationExpr: "1==1",
|
|
Debug: &boolFalse,
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
|
|
},
|
|
},
|
|
expectedNbProfile: 0,
|
|
},
|
|
{
|
|
name: "filter ok and duration_expr NOK",
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{
|
|
"1==1",
|
|
},
|
|
DurationExpr: "unknownExprHelper() == 'foo'",
|
|
Debug: &boolFalse,
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
|
|
},
|
|
},
|
|
expectedNbProfile: 0,
|
|
},
|
|
{
|
|
name: "filter ok and duration_expr ok + DEBUG",
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{
|
|
"1==1",
|
|
},
|
|
DurationExpr: "1==1",
|
|
Debug: &boolTrue,
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
|
|
},
|
|
},
|
|
expectedNbProfile: 1,
|
|
},
|
|
{
|
|
name: "filter ok and no duration",
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{
|
|
"1==1",
|
|
},
|
|
Debug: &boolTrue,
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse},
|
|
},
|
|
},
|
|
expectedNbProfile: 1,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
profilesCfg := []*csconfig.ProfileCfg{
|
|
test.profileCfg,
|
|
}
|
|
profile, _ := NewProfile(profilesCfg)
|
|
fmt.Printf("expected : %+v | result : %+v", test.expectedNbProfile, len(profile))
|
|
require.Len(t, profile, test.expectedNbProfile)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEvaluateProfile(t *testing.T) {
|
|
type args struct {
|
|
profileCfg *csconfig.ProfileCfg
|
|
Alert *models.Alert
|
|
}
|
|
|
|
exprhelpers.Init(nil)
|
|
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
expectedDecisionCount int // count of expected decisions
|
|
expectedDuration string
|
|
expectedMatchStatus bool
|
|
}{
|
|
{
|
|
name: "simple pass single expr",
|
|
args: args{
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{fmt.Sprintf("Alert.GetScenario() == \"%s\"", scenario)},
|
|
Debug: &boolFalse,
|
|
},
|
|
Alert: &models.Alert{Remediation: true, Scenario: &scenario},
|
|
},
|
|
expectedDecisionCount: 0,
|
|
expectedMatchStatus: true,
|
|
},
|
|
{
|
|
name: "simple fail single expr",
|
|
args: args{
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{"Alert.GetScenario() == \"Foo\""},
|
|
},
|
|
Alert: &models.Alert{Remediation: true},
|
|
},
|
|
expectedDecisionCount: 0,
|
|
expectedMatchStatus: false,
|
|
},
|
|
{
|
|
name: "1 expr fail 1 expr pass should still eval to match",
|
|
args: args{
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{"1==1", "1!=1"},
|
|
},
|
|
Alert: &models.Alert{Remediation: true},
|
|
},
|
|
expectedDecisionCount: 0,
|
|
expectedMatchStatus: true,
|
|
},
|
|
{
|
|
name: "simple filter with 2 decision",
|
|
args: args{
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{"1==1"},
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolTrue, Duration: &duration},
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
|
|
},
|
|
},
|
|
Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
|
|
},
|
|
expectedDecisionCount: 2,
|
|
expectedMatchStatus: true,
|
|
},
|
|
{
|
|
name: "simple filter with decision_expr",
|
|
args: args{
|
|
profileCfg: &csconfig.ProfileCfg{
|
|
Filters: []string{"1==1"},
|
|
Decisions: []models.Decision{
|
|
{Type: &typ, Scope: &scope, Simulated: &boolFalse},
|
|
},
|
|
DurationExpr: "Sprintf('%dh', 4*4)",
|
|
},
|
|
Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
|
|
},
|
|
expectedDecisionCount: 1,
|
|
expectedDuration: "16h",
|
|
expectedMatchStatus: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
profilesCfg := []*csconfig.ProfileCfg{
|
|
tt.args.profileCfg,
|
|
}
|
|
profile, err := NewProfile(profilesCfg)
|
|
if err != nil {
|
|
t.Errorf("failed to get newProfile : %+v", err)
|
|
}
|
|
got, got1, _ := profile[0].EvaluateProfile(tt.args.Alert)
|
|
if !reflect.DeepEqual(len(got), tt.expectedDecisionCount) {
|
|
t.Errorf("EvaluateProfile() got = %+v, want %+v", got, tt.expectedDecisionCount)
|
|
}
|
|
if got1 != tt.expectedMatchStatus {
|
|
t.Errorf("EvaluateProfile() got1 = %v, want %v", got1, tt.expectedMatchStatus)
|
|
}
|
|
if tt.expectedDuration != "" {
|
|
require.Equal(t, tt.expectedDuration, *got[0].Duration, "The two durations should be the same")
|
|
}
|
|
})
|
|
}
|
|
}
|