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