alerts_service_test.go 14 KB

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