github.com/crowdsecurity/crowdsec@v1.6.1/pkg/csprofiles/csprofiles_test.go (about)

     1  package csprofiles
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
    11  	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
    12  	"github.com/crowdsecurity/crowdsec/pkg/models"
    13  )
    14  
    15  var (
    16  	scope     = "Country"
    17  	typ       = "ban"
    18  	boolFalse = false
    19  	boolTrue  = true
    20  	duration  = "1h"
    21  
    22  	value    = "CH"
    23  	scenario = "ssh-bf"
    24  )
    25  
    26  func TestNewProfile(t *testing.T) {
    27  	tests := []struct {
    28  		name              string
    29  		profileCfg        *csconfig.ProfileCfg
    30  		expectedNbProfile int
    31  	}{
    32  		{
    33  			name: "filter ok and duration_expr ok",
    34  			profileCfg: &csconfig.ProfileCfg{
    35  				Filters: []string{
    36  					"1==1",
    37  				},
    38  				DurationExpr: "1==1",
    39  				Debug:        &boolFalse,
    40  				Decisions: []models.Decision{
    41  					{Type: &typ, Scope: &scope, Simulated: &boolTrue, Duration: &duration},
    42  				},
    43  			},
    44  			expectedNbProfile: 1,
    45  		},
    46  		{
    47  			name: "filter NOK and duration_expr ok",
    48  			profileCfg: &csconfig.ProfileCfg{
    49  				Filters: []string{
    50  					"1==1",
    51  					"unknownExprHelper() == 'foo'",
    52  				},
    53  				DurationExpr: "1==1",
    54  				Debug:        &boolFalse,
    55  				Decisions: []models.Decision{
    56  					{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
    57  				},
    58  			},
    59  			expectedNbProfile: 0,
    60  		},
    61  		{
    62  			name: "filter ok and duration_expr NOK",
    63  			profileCfg: &csconfig.ProfileCfg{
    64  				Filters: []string{
    65  					"1==1",
    66  				},
    67  				DurationExpr: "unknownExprHelper() == 'foo'",
    68  				Debug:        &boolFalse,
    69  				Decisions: []models.Decision{
    70  					{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
    71  				},
    72  			},
    73  			expectedNbProfile: 0,
    74  		},
    75  		{
    76  			name: "filter ok and duration_expr ok + DEBUG",
    77  			profileCfg: &csconfig.ProfileCfg{
    78  				Filters: []string{
    79  					"1==1",
    80  				},
    81  				DurationExpr: "1==1",
    82  				Debug:        &boolTrue,
    83  				Decisions: []models.Decision{
    84  					{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
    85  				},
    86  			},
    87  			expectedNbProfile: 1,
    88  		},
    89  		{
    90  			name: "filter ok and no duration",
    91  			profileCfg: &csconfig.ProfileCfg{
    92  				Filters: []string{
    93  					"1==1",
    94  				},
    95  				Debug: &boolTrue,
    96  				Decisions: []models.Decision{
    97  					{Type: &typ, Scope: &scope, Simulated: &boolFalse},
    98  				},
    99  			},
   100  			expectedNbProfile: 1,
   101  		},
   102  	}
   103  
   104  	for _, test := range tests {
   105  		test := test
   106  		t.Run(test.name, func(t *testing.T) {
   107  			profilesCfg := []*csconfig.ProfileCfg{
   108  				test.profileCfg,
   109  			}
   110  			profile, _ := NewProfile(profilesCfg)
   111  			fmt.Printf("expected : %+v | result : %+v", test.expectedNbProfile, len(profile))
   112  			require.Len(t, profile, test.expectedNbProfile)
   113  		})
   114  	}
   115  }
   116  
   117  func TestEvaluateProfile(t *testing.T) {
   118  	type args struct {
   119  		profileCfg *csconfig.ProfileCfg
   120  		Alert      *models.Alert
   121  	}
   122  
   123  	exprhelpers.Init(nil)
   124  
   125  	tests := []struct {
   126  		name                  string
   127  		args                  args
   128  		expectedDecisionCount int // count of expected decisions
   129  		expectedDuration      string
   130  		expectedMatchStatus   bool
   131  	}{
   132  		{
   133  			name: "simple pass single expr",
   134  			args: args{
   135  				profileCfg: &csconfig.ProfileCfg{
   136  					Filters: []string{fmt.Sprintf("Alert.GetScenario() == \"%s\"", scenario)},
   137  					Debug:   &boolFalse,
   138  				},
   139  				Alert: &models.Alert{Remediation: true, Scenario: &scenario},
   140  			},
   141  			expectedDecisionCount: 0,
   142  			expectedMatchStatus:   true,
   143  		},
   144  		{
   145  			name: "simple fail single expr",
   146  			args: args{
   147  				profileCfg: &csconfig.ProfileCfg{
   148  					Filters: []string{"Alert.GetScenario() == \"Foo\""},
   149  				},
   150  				Alert: &models.Alert{Remediation: true},
   151  			},
   152  			expectedDecisionCount: 0,
   153  			expectedMatchStatus:   false,
   154  		},
   155  		{
   156  			name: "1 expr fail 1 expr pass should still eval to match",
   157  			args: args{
   158  				profileCfg: &csconfig.ProfileCfg{
   159  					Filters: []string{"1==1", "1!=1"},
   160  				},
   161  				Alert: &models.Alert{Remediation: true},
   162  			},
   163  			expectedDecisionCount: 0,
   164  			expectedMatchStatus:   true,
   165  		},
   166  		{
   167  			name: "simple filter with  2 decision",
   168  			args: args{
   169  				profileCfg: &csconfig.ProfileCfg{
   170  					Filters: []string{"1==1"},
   171  					Decisions: []models.Decision{
   172  						{Type: &typ, Scope: &scope, Simulated: &boolTrue, Duration: &duration},
   173  						{Type: &typ, Scope: &scope, Simulated: &boolFalse, Duration: &duration},
   174  					},
   175  				},
   176  				Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
   177  			},
   178  			expectedDecisionCount: 2,
   179  			expectedMatchStatus:   true,
   180  		},
   181  		{
   182  			name: "simple filter with decision_expr",
   183  			args: args{
   184  				profileCfg: &csconfig.ProfileCfg{
   185  					Filters: []string{"1==1"},
   186  					Decisions: []models.Decision{
   187  						{Type: &typ, Scope: &scope, Simulated: &boolFalse},
   188  					},
   189  					DurationExpr: "Sprintf('%dh', 4*4)",
   190  				},
   191  				Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
   192  			},
   193  			expectedDecisionCount: 1,
   194  			expectedDuration:      "16h",
   195  			expectedMatchStatus:   true,
   196  		},
   197  	}
   198  	for _, tt := range tests {
   199  		tt := tt
   200  		t.Run(tt.name, func(t *testing.T) {
   201  			profilesCfg := []*csconfig.ProfileCfg{
   202  				tt.args.profileCfg,
   203  			}
   204  			profile, err := NewProfile(profilesCfg)
   205  			if err != nil {
   206  				t.Errorf("failed to get newProfile : %+v", err)
   207  			}
   208  			got, got1, _ := profile[0].EvaluateProfile(tt.args.Alert)
   209  			if !reflect.DeepEqual(len(got), tt.expectedDecisionCount) {
   210  				t.Errorf("EvaluateProfile() got = %+v, want %+v", got, tt.expectedDecisionCount)
   211  			}
   212  			if got1 != tt.expectedMatchStatus {
   213  				t.Errorf("EvaluateProfile() got1 = %v, want %v", got1, tt.expectedMatchStatus)
   214  			}
   215  			if tt.expectedDuration != "" {
   216  				require.Equal(t, tt.expectedDuration, *got[0].Duration, "The two durations should be the same")
   217  			}
   218  		})
   219  	}
   220  }