github.com/crowdsecurity/crowdsec@v1.6.1/pkg/apiclient/alerts_service_test.go (about)

     1  package apiclient
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"testing"
     9  
    10  	log "github.com/sirupsen/logrus"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/crowdsecurity/go-cs-lib/cstest"
    15  	"github.com/crowdsecurity/go-cs-lib/ptr"
    16  	"github.com/crowdsecurity/go-cs-lib/version"
    17  
    18  	"github.com/crowdsecurity/crowdsec/pkg/models"
    19  )
    20  
    21  func TestAlertsListAsMachine(t *testing.T) {
    22  	log.SetLevel(log.DebugLevel)
    23  
    24  	mux, urlx, teardown := setup()
    25  	mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
    26  		w.WriteHeader(http.StatusOK)
    27  		w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
    28  	})
    29  
    30  	log.Printf("URL is %s", urlx)
    31  
    32  	apiURL, err := url.Parse(urlx + "/")
    33  	require.NoError(t, err)
    34  
    35  	client, err := NewClient(&Config{
    36  		MachineID:     "test_login",
    37  		Password:      "test_password",
    38  		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
    39  		URL:           apiURL,
    40  		VersionPrefix: "v1",
    41  	})
    42  	require.NoError(t, err)
    43  
    44  	defer teardown()
    45  
    46  	mux.HandleFunc("/alerts", func(w http.ResponseWriter, r *http.Request) {
    47  		if r.URL.RawQuery == "ip=1.2.3.4" {
    48  			testMethod(t, r, "GET")
    49  			w.WriteHeader(http.StatusOK)
    50  			fmt.Fprintf(w, `null`)
    51  
    52  			return
    53  		}
    54  
    55  		testMethod(t, r, "GET")
    56  		w.WriteHeader(http.StatusOK)
    57  		fmt.Fprint(w, `[
    58  			{"capacity":5,"created_at":"2020-11-28T10:20:47+01:00",
    59  			 "decisions":[
    60  				  {"duration":"59m49.264032632s",
    61  				  "id":1,
    62  				  "origin":"crowdsec",
    63  				  "scenario":"crowdsecurity/ssh-bf",
    64  				  "scope":"Ip",
    65  				  "simulated":false,
    66  				  "type":"ban",
    67  				  "value":"1.1.1.172"}
    68  				  ],
    69  			 "events":[
    70  				 {"meta":[
    71  					  {"key":"target_user","value":"netflix"},
    72  					  {"key":"service","value":"ssh"}
    73  					],
    74  					"timestamp":"2020-11-28 10:20:46 +0000 UTC"},
    75  				 {"meta":[
    76  					 {"key":"target_user","value":"netflix"},
    77  					 {"key":"service","value":"ssh"}
    78  					 ],
    79  					 "timestamp":"2020-11-28 10:20:46 +0000 UTC"}
    80  				],
    81  				"events_count":6,
    82  				"id":1,
    83  				"labels":null,
    84  				"leakspeed":"10s",
    85  				"machine_id":"test",
    86  				"message":"Ip 1.1.1.172 performed 'crowdsecurity/ssh-bf' (6 events over 2.920062ms) at 2020-11-28 10:20:46.845619968 +0100 CET m=+5.903899761",
    87  				"scenario":"crowdsecurity/ssh-bf",
    88  				"scenario_hash":"4441dcff07020f6690d998b7101e642359ba405c2abb83565bbbdcee36de280f",
    89  				"scenario_version":"0.1",
    90  				"simulated":false,
    91  				"source":{
    92  					"as_name":"Cloudflare Inc",
    93  					"cn":"AU",
    94  					"ip":"1.1.1.172",
    95  					"latitude":-37.7,
    96  					"longitude":145.1833,
    97  					"range":"1.1.1.0/24",
    98  					"scope":"Ip",
    99  					"value":"1.1.1.172"
   100  					},
   101  				"start_at":"2020-11-28 10:20:46.842701127 +0100 +0100",
   102  				"stop_at":"2020-11-28 10:20:46.845621385 +0100 +0100"
   103  			}
   104  		]`)
   105  	})
   106  
   107  	tscenario := "crowdsecurity/ssh-bf"
   108  	tscope := "Ip"
   109  	tvalue := "1.1.1.172"
   110  	ttimestamp := "2020-11-28 10:20:46 +0000 UTC"
   111  	tmessage := "Ip 1.1.1.172 performed 'crowdsecurity/ssh-bf' (6 events over 2.920062ms) at 2020-11-28 10:20:46.845619968 +0100 CET m=+5.903899761"
   112  
   113  	expected := models.GetAlertsResponse{
   114  		&models.Alert{
   115  			Capacity:  ptr.Of(int32(5)),
   116  			CreatedAt: "2020-11-28T10:20:47+01:00",
   117  			Decisions: []*models.Decision{
   118  				{
   119  					Duration: ptr.Of("59m49.264032632s"),
   120  					ID:       1,
   121  					Origin:   ptr.Of("crowdsec"),
   122  					Scenario: &tscenario,
   123  
   124  					Scope:     &tscope,
   125  					Simulated: ptr.Of(false),
   126  					Type:      ptr.Of("ban"),
   127  					Value:     &tvalue,
   128  				},
   129  			},
   130  			Events: []*models.Event{
   131  				{
   132  					Meta: models.Meta{
   133  						&models.MetaItems0{
   134  							Key:   "target_user",
   135  							Value: "netflix",
   136  						},
   137  						&models.MetaItems0{
   138  							Key:   "service",
   139  							Value: "ssh",
   140  						},
   141  					},
   142  					Timestamp: &ttimestamp,
   143  				}, {
   144  					Meta: models.Meta{
   145  						&models.MetaItems0{
   146  							Key:   "target_user",
   147  							Value: "netflix",
   148  						},
   149  						&models.MetaItems0{
   150  							Key:   "service",
   151  							Value: "ssh",
   152  						},
   153  					},
   154  					Timestamp: &ttimestamp,
   155  				},
   156  			},
   157  			EventsCount:     ptr.Of(int32(6)),
   158  			ID:              1,
   159  			Leakspeed:       ptr.Of("10s"),
   160  			MachineID:       "test",
   161  			Message:         &tmessage,
   162  			Remediation:     false,
   163  			Scenario:        &tscenario,
   164  			ScenarioHash:    ptr.Of("4441dcff07020f6690d998b7101e642359ba405c2abb83565bbbdcee36de280f"),
   165  			ScenarioVersion: ptr.Of("0.1"),
   166  			Simulated:       ptr.Of(false),
   167  			Source: &models.Source{
   168  				AsName:    "Cloudflare Inc",
   169  				AsNumber:  "",
   170  				Cn:        "AU",
   171  				IP:        "1.1.1.172",
   172  				Latitude:  -37.7,
   173  				Longitude: 145.1833,
   174  				Range:     "1.1.1.0/24",
   175  				Scope:     &tscope,
   176  				Value:     &tvalue,
   177  			},
   178  			StartAt: ptr.Of("2020-11-28 10:20:46.842701127 +0100 +0100"),
   179  			StopAt:  ptr.Of("2020-11-28 10:20:46.845621385 +0100 +0100"),
   180  		},
   181  	}
   182  
   183  	//log.Debugf("data : -> %s", spew.Sdump(alerts))
   184  	//log.Debugf("resp : -> %s", spew.Sdump(resp))
   185  	//log.Debugf("expected : -> %s", spew.Sdump(expected))
   186  	//first one returns data
   187  	alerts, resp, err := client.Alerts.List(context.Background(), AlertsListOpts{})
   188  	require.NoError(t, err)
   189  	assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
   190  	assert.Equal(t, expected, *alerts)
   191  
   192  	//this one doesn't
   193  	filter := AlertsListOpts{IPEquals: ptr.Of("1.2.3.4")}
   194  
   195  	alerts, resp, err = client.Alerts.List(context.Background(), filter)
   196  	require.NoError(t, err)
   197  	assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
   198  	assert.Empty(t, *alerts)
   199  }
   200  
   201  func TestAlertsGetAsMachine(t *testing.T) {
   202  	log.SetLevel(log.DebugLevel)
   203  
   204  	mux, urlx, teardown := setup()
   205  	mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
   206  		w.WriteHeader(http.StatusOK)
   207  		w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
   208  	})
   209  	log.Printf("URL is %s", urlx)
   210  
   211  	apiURL, err := url.Parse(urlx + "/")
   212  	require.NoError(t, err)
   213  
   214  	client, err := NewClient(&Config{
   215  		MachineID:     "test_login",
   216  		Password:      "test_password",
   217  		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
   218  		URL:           apiURL,
   219  		VersionPrefix: "v1",
   220  	})
   221  	require.NoError(t, err)
   222  
   223  	defer teardown()
   224  
   225  	mux.HandleFunc("/alerts/2", func(w http.ResponseWriter, r *http.Request) {
   226  		testMethod(t, r, "GET")
   227  		w.WriteHeader(http.StatusNotFound)
   228  		fmt.Fprintf(w, `{"message":"object not found"}`)
   229  	})
   230  
   231  	mux.HandleFunc("/alerts/1", func(w http.ResponseWriter, r *http.Request) {
   232  		testMethod(t, r, "GET")
   233  		w.WriteHeader(http.StatusOK)
   234  		fmt.Fprint(w, `{"capacity":5,"created_at":"2020-11-28T10:20:47+01:00",
   235  			 "decisions":[
   236  				  {"duration":"59m49.264032632s",
   237  				  "end_ip":16843180,
   238  				  "id":1,
   239  				  "origin":"crowdsec",
   240  				  "scenario":"crowdsecurity/ssh-bf",
   241  				  "scope":"Ip",
   242  				  "simulated":false,
   243  				  "start_ip":16843180,
   244  				  "type":"ban",
   245  				  "value":"1.1.1.172"}
   246  				  ],
   247  			 "events":[
   248  				 {"meta":[
   249  					  {"key":"target_user","value":"netflix"},
   250  					  {"key":"service","value":"ssh"}
   251  					],
   252  					"timestamp":"2020-11-28 10:20:46 +0000 UTC"},
   253  				 {"meta":[
   254  					 {"key":"target_user","value":"netflix"},
   255  					 {"key":"service","value":"ssh"}
   256  					 ],
   257  					 "timestamp":"2020-11-28 10:20:46 +0000 UTC"}
   258  				],
   259  				"events_count":6,
   260  				"id":1,
   261  				"labels":null,
   262  				"leakspeed":"10s",
   263  				"machine_id":"test",
   264  				"message":"Ip 1.1.1.172 performed 'crowdsecurity/ssh-bf' (6 events over 2.920062ms) at 2020-11-28 10:20:46.845619968 +0100 CET m=+5.903899761",
   265  				"scenario":"crowdsecurity/ssh-bf",
   266  				"scenario_hash":"4441dcff07020f6690d998b7101e642359ba405c2abb83565bbbdcee36de280f",
   267  				"scenario_version":"0.1",
   268  				"simulated":false,
   269  				"source":{
   270  					"as_name":"Cloudflare Inc",
   271  					"cn":"AU",
   272  					"ip":"1.1.1.172",
   273  					"latitude":-37.7,
   274  					"longitude":145.1833,
   275  					"range":"1.1.1.0/24",
   276  					"scope":"Ip",
   277  					"value":"1.1.1.172"
   278  					},
   279  				"start_at":"2020-11-28 10:20:46.842701127 +0100 +0100",
   280  				"stop_at":"2020-11-28 10:20:46.845621385 +0100 +0100"
   281  			}`)
   282  	})
   283  
   284  	tscenario := "crowdsecurity/ssh-bf"
   285  	tscope := "Ip"
   286  	ttype := "ban"
   287  	tvalue := "1.1.1.172"
   288  	ttimestamp := "2020-11-28 10:20:46 +0000 UTC"
   289  
   290  	expected := &models.Alert{
   291  		Capacity:  ptr.Of(int32(5)),
   292  		CreatedAt: "2020-11-28T10:20:47+01:00",
   293  		Decisions: []*models.Decision{
   294  			{
   295  				Duration: ptr.Of("59m49.264032632s"),
   296  				ID:       1,
   297  				Origin:   ptr.Of("crowdsec"),
   298  				Scenario: &tscenario,
   299  
   300  				Scope:     &tscope,
   301  				Simulated: ptr.Of(false),
   302  				Type:      &ttype,
   303  				Value:     &tvalue,
   304  			},
   305  		},
   306  		Events: []*models.Event{
   307  			{
   308  				Meta: models.Meta{
   309  					&models.MetaItems0{
   310  						Key:   "target_user",
   311  						Value: "netflix",
   312  					},
   313  					&models.MetaItems0{
   314  						Key:   "service",
   315  						Value: "ssh",
   316  					},
   317  				},
   318  				Timestamp: &ttimestamp,
   319  			}, {
   320  				Meta: models.Meta{
   321  					&models.MetaItems0{
   322  						Key:   "target_user",
   323  						Value: "netflix",
   324  					},
   325  					&models.MetaItems0{
   326  						Key:   "service",
   327  						Value: "ssh",
   328  					},
   329  				},
   330  				Timestamp: &ttimestamp,
   331  			},
   332  		},
   333  		EventsCount:     ptr.Of(int32(6)),
   334  		ID:              1,
   335  		Leakspeed:       ptr.Of("10s"),
   336  		MachineID:       "test",
   337  		Message:         ptr.Of("Ip 1.1.1.172 performed 'crowdsecurity/ssh-bf' (6 events over 2.920062ms) at 2020-11-28 10:20:46.845619968 +0100 CET m=+5.903899761"),
   338  		Remediation:     false,
   339  		Scenario:        &tscenario,
   340  		ScenarioHash:    ptr.Of("4441dcff07020f6690d998b7101e642359ba405c2abb83565bbbdcee36de280f"),
   341  		ScenarioVersion: ptr.Of("0.1"),
   342  		Simulated:       ptr.Of(false),
   343  		Source: &models.Source{
   344  			AsName:    "Cloudflare Inc",
   345  			AsNumber:  "",
   346  			Cn:        "AU",
   347  			IP:        "1.1.1.172",
   348  			Latitude:  -37.7,
   349  			Longitude: 145.1833,
   350  			Range:     "1.1.1.0/24",
   351  			Scope:     &tscope,
   352  			Value:     &tvalue,
   353  		},
   354  		StartAt: ptr.Of("2020-11-28 10:20:46.842701127 +0100 +0100"),
   355  		StopAt:  ptr.Of("2020-11-28 10:20:46.845621385 +0100 +0100"),
   356  	}
   357  
   358  	alerts, resp, err := client.Alerts.GetByID(context.Background(), 1)
   359  	require.NoError(t, err)
   360  	assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
   361  	assert.Equal(t, *expected, *alerts)
   362  
   363  	//fail
   364  	_, _, err = client.Alerts.GetByID(context.Background(), 2)
   365  	cstest.RequireErrorMessage(t, err, "API error: object not found")
   366  }
   367  
   368  func TestAlertsCreateAsMachine(t *testing.T) {
   369  	log.SetLevel(log.DebugLevel)
   370  
   371  	mux, urlx, teardown := setup()
   372  	mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
   373  		w.WriteHeader(http.StatusOK)
   374  		w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
   375  	})
   376  
   377  	mux.HandleFunc("/alerts", func(w http.ResponseWriter, r *http.Request) {
   378  		testMethod(t, r, "POST")
   379  		w.WriteHeader(http.StatusOK)
   380  		w.Write([]byte(`["3"]`))
   381  	})
   382  
   383  	log.Printf("URL is %s", urlx)
   384  
   385  	apiURL, err := url.Parse(urlx + "/")
   386  	require.NoError(t, err)
   387  
   388  	client, err := NewClient(&Config{
   389  		MachineID:     "test_login",
   390  		Password:      "test_password",
   391  		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
   392  		URL:           apiURL,
   393  		VersionPrefix: "v1",
   394  	})
   395  	require.NoError(t, err)
   396  
   397  	defer teardown()
   398  
   399  	alert := models.AddAlertsRequest{}
   400  	alerts, resp, err := client.Alerts.Add(context.Background(), alert)
   401  	require.NoError(t, err)
   402  
   403  	expected := &models.AddAlertsResponse{"3"}
   404  
   405  	assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
   406  	assert.Equal(t, *expected, *alerts)
   407  }
   408  
   409  func TestAlertsDeleteAsMachine(t *testing.T) {
   410  	log.SetLevel(log.DebugLevel)
   411  
   412  	mux, urlx, teardown := setup()
   413  	mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
   414  		w.WriteHeader(http.StatusOK)
   415  		w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
   416  	})
   417  
   418  	mux.HandleFunc("/alerts", func(w http.ResponseWriter, r *http.Request) {
   419  		testMethod(t, r, "DELETE")
   420  		assert.Equal(t, "ip=1.2.3.4", r.URL.RawQuery)
   421  		w.WriteHeader(http.StatusOK)
   422  		w.Write([]byte(`{"message":"0 deleted alerts"}`))
   423  	})
   424  
   425  	log.Printf("URL is %s", urlx)
   426  
   427  	apiURL, err := url.Parse(urlx + "/")
   428  	require.NoError(t, err)
   429  
   430  	client, err := NewClient(&Config{
   431  		MachineID:     "test_login",
   432  		Password:      "test_password",
   433  		UserAgent:     fmt.Sprintf("crowdsec/%s", version.String()),
   434  		URL:           apiURL,
   435  		VersionPrefix: "v1",
   436  	})
   437  	require.NoError(t, err)
   438  
   439  	defer teardown()
   440  
   441  	alert := AlertsDeleteOpts{IPEquals: ptr.Of("1.2.3.4")}
   442  	alerts, resp, err := client.Alerts.Delete(context.Background(), alert)
   443  	require.NoError(t, err)
   444  
   445  	expected := &models.DeleteAlertsResponse{NbDeleted: ""}
   446  
   447  	assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
   448  	assert.Equal(t, *expected, *alerts)
   449  }