decisions_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package apiserver
  2. import (
  3. "testing"
  4. "github.com/stretchr/testify/assert"
  5. )
  6. func TestDeleteDecisionRange(t *testing.T) {
  7. lapi := SetupLAPITest(t)
  8. // Create Valid Alert
  9. lapi.InsertAlertFromFile("./tests/alert_minibulk.json")
  10. // delete by ip wrong
  11. w := lapi.RecordResponse("DELETE", "/v1/decisions?range=1.2.3.0/24", emptyBody)
  12. assert.Equal(t, 200, w.Code)
  13. assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
  14. // delete by range
  15. w = lapi.RecordResponse("DELETE", "/v1/decisions?range=91.121.79.0/24&contains=false", emptyBody)
  16. assert.Equal(t, 200, w.Code)
  17. assert.Equal(t, `{"nbDeleted":"2"}`, w.Body.String())
  18. // delete by range : ensure it was already deleted
  19. w = lapi.RecordResponse("DELETE", "/v1/decisions?range=91.121.79.0/24", emptyBody)
  20. assert.Equal(t, 200, w.Code)
  21. assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
  22. }
  23. func TestDeleteDecisionFilter(t *testing.T) {
  24. lapi := SetupLAPITest(t)
  25. // Create Valid Alert
  26. lapi.InsertAlertFromFile("./tests/alert_minibulk.json")
  27. // delete by ip wrong
  28. w := lapi.RecordResponse("DELETE", "/v1/decisions?ip=1.2.3.4", emptyBody)
  29. assert.Equal(t, 200, w.Code)
  30. assert.Equal(t, `{"nbDeleted":"0"}`, w.Body.String())
  31. // delete by ip good
  32. w = lapi.RecordResponse("DELETE", "/v1/decisions?ip=91.121.79.179", emptyBody)
  33. assert.Equal(t, 200, w.Code)
  34. assert.Equal(t, `{"nbDeleted":"1"}`, w.Body.String())
  35. // delete by scope/value
  36. w = lapi.RecordResponse("DELETE", "/v1/decisions?scopes=Ip&value=91.121.79.178", emptyBody)
  37. assert.Equal(t, 200, w.Code)
  38. assert.Equal(t, `{"nbDeleted":"1"}`, w.Body.String())
  39. }
  40. func TestGetDecisionFilters(t *testing.T) {
  41. lapi := SetupLAPITest(t)
  42. // Create Valid Alert
  43. lapi.InsertAlertFromFile("./tests/alert_minibulk.json")
  44. // Get Decision
  45. w := lapi.RecordResponse("GET", "/v1/decisions", emptyBody)
  46. assert.Equal(t, 200, w.Code)
  47. decisions, code, err := readDecisionsGetResp(w)
  48. assert.Nil(t, err)
  49. assert.Equal(t, 200, code)
  50. assert.Equal(t, 2, len(decisions))
  51. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[0].Scenario)
  52. assert.Equal(t, "91.121.79.179", *decisions[0].Value)
  53. assert.Equal(t, int64(1), decisions[0].ID)
  54. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[1].Scenario)
  55. assert.Equal(t, "91.121.79.178", *decisions[1].Value)
  56. assert.Equal(t, int64(2), decisions[1].ID)
  57. // Get Decision : type filter
  58. w = lapi.RecordResponse("GET", "/v1/decisions?type=ban", emptyBody)
  59. assert.Equal(t, 200, w.Code)
  60. decisions, code, err = readDecisionsGetResp(w)
  61. assert.Nil(t, err)
  62. assert.Equal(t, 200, code)
  63. assert.Equal(t, 2, len(decisions))
  64. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[0].Scenario)
  65. assert.Equal(t, "91.121.79.179", *decisions[0].Value)
  66. assert.Equal(t, int64(1), decisions[0].ID)
  67. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[1].Scenario)
  68. assert.Equal(t, "91.121.79.178", *decisions[1].Value)
  69. assert.Equal(t, int64(2), decisions[1].ID)
  70. // assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
  71. // assert.Contains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
  72. // Get Decision : scope/value
  73. w = lapi.RecordResponse("GET", "/v1/decisions?scopes=Ip&value=91.121.79.179", emptyBody)
  74. assert.Equal(t, 200, w.Code)
  75. decisions, code, err = readDecisionsGetResp(w)
  76. assert.Nil(t, err)
  77. assert.Equal(t, 200, code)
  78. assert.Equal(t, 1, len(decisions))
  79. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[0].Scenario)
  80. assert.Equal(t, "91.121.79.179", *decisions[0].Value)
  81. assert.Equal(t, int64(1), decisions[0].ID)
  82. // assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
  83. // assert.NotContains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
  84. // Get Decision : ip filter
  85. w = lapi.RecordResponse("GET", "/v1/decisions?ip=91.121.79.179", emptyBody)
  86. assert.Equal(t, 200, w.Code)
  87. decisions, code, err = readDecisionsGetResp(w)
  88. assert.Nil(t, err)
  89. assert.Equal(t, 200, code)
  90. assert.Equal(t, 1, len(decisions))
  91. assert.Equal(t, "crowdsecurity/ssh-bf", *decisions[0].Scenario)
  92. assert.Equal(t, "91.121.79.179", *decisions[0].Value)
  93. assert.Equal(t, int64(1), decisions[0].ID)
  94. // assert.Contains(t, w.Body.String(), `"id":1,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.179"`)
  95. // assert.NotContains(t, w.Body.String(), `"id":2,"origin":"crowdsec","scenario":"crowdsecurity/ssh-bf","scope":"Ip","type":"ban","value":"91.121.79.178"`)
  96. // Get decision : by range
  97. w = lapi.RecordResponse("GET", "/v1/decisions?range=91.121.79.0/24&contains=false", emptyBody)
  98. assert.Equal(t, 200, w.Code)
  99. decisions, code, err = readDecisionsGetResp(w)
  100. assert.Nil(t, err)
  101. assert.Equal(t, 200, code)
  102. assert.Equal(t, 2, len(decisions))
  103. assert.Contains(t, []string{*decisions[0].Value, *decisions[1].Value}, "91.121.79.179")
  104. assert.Contains(t, []string{*decisions[0].Value, *decisions[1].Value}, "91.121.79.178")
  105. }
  106. func TestGetDecision(t *testing.T) {
  107. lapi := SetupLAPITest(t)
  108. // Create Valid Alert
  109. lapi.InsertAlertFromFile("./tests/alert_sample.json")
  110. // Get Decision
  111. w := lapi.RecordResponse("GET", "/v1/decisions", emptyBody)
  112. assert.Equal(t, 200, w.Code)
  113. decisions, code, err := readDecisionsGetResp(w)
  114. assert.Nil(t, err)
  115. assert.Equal(t, 200, code)
  116. assert.Equal(t, 3, len(decisions))
  117. /*decisions get doesn't perform deduplication*/
  118. assert.Equal(t, "crowdsecurity/test", *decisions[0].Scenario)
  119. assert.Equal(t, "127.0.0.1", *decisions[0].Value)
  120. assert.Equal(t, int64(1), decisions[0].ID)
  121. assert.Equal(t, "crowdsecurity/test", *decisions[1].Scenario)
  122. assert.Equal(t, "127.0.0.1", *decisions[1].Value)
  123. assert.Equal(t, int64(2), decisions[1].ID)
  124. assert.Equal(t, "crowdsecurity/test", *decisions[2].Scenario)
  125. assert.Equal(t, "127.0.0.1", *decisions[2].Value)
  126. assert.Equal(t, int64(3), decisions[2].ID)
  127. // Get Decision with invalid filter. It should ignore this filter
  128. w = lapi.RecordResponse("GET", "/v1/decisions?test=test", emptyBody)
  129. assert.Equal(t, 200, w.Code)
  130. assert.Equal(t, 3, len(decisions))
  131. }
  132. func TestDeleteDecisionByID(t *testing.T) {
  133. lapi := SetupLAPITest(t)
  134. // Create Valid Alert
  135. lapi.InsertAlertFromFile("./tests/alert_sample.json")
  136. //Have one alerts
  137. w := lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  138. decisions, code, err := readDecisionsStreamResp(w)
  139. assert.Equal(t, err, nil)
  140. assert.Equal(t, code, 200)
  141. assert.Equal(t, len(decisions["deleted"]), 0)
  142. assert.Equal(t, len(decisions["new"]), 1)
  143. // Delete alert with Invalid ID
  144. w = lapi.RecordResponse("DELETE", "/v1/decisions/test", emptyBody)
  145. assert.Equal(t, 400, w.Code)
  146. err_resp, _, err := readDecisionsErrorResp(w)
  147. assert.NoError(t, err)
  148. assert.Equal(t, err_resp["message"], "decision_id must be valid integer")
  149. // Delete alert with ID that not exist
  150. w = lapi.RecordResponse("DELETE", "/v1/decisions/100", emptyBody)
  151. assert.Equal(t, 500, w.Code)
  152. err_resp, _, err = readDecisionsErrorResp(w)
  153. assert.NoError(t, err)
  154. assert.Equal(t, err_resp["message"], "decision with id '100' doesn't exist: unable to delete")
  155. //Have one alerts
  156. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  157. decisions, code, err = readDecisionsStreamResp(w)
  158. assert.Equal(t, err, nil)
  159. assert.Equal(t, code, 200)
  160. assert.Equal(t, len(decisions["deleted"]), 0)
  161. assert.Equal(t, len(decisions["new"]), 1)
  162. // Delete alert with valid ID
  163. w = lapi.RecordResponse("DELETE", "/v1/decisions/1", emptyBody)
  164. assert.Equal(t, 200, w.Code)
  165. resp, _, err := readDecisionsDeleteResp(w)
  166. assert.NoError(t, err)
  167. assert.Equal(t, resp.NbDeleted, "1")
  168. //Have one alert (because we delete an alert that has dup targets)
  169. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  170. decisions, code, err = readDecisionsStreamResp(w)
  171. assert.Equal(t, err, nil)
  172. assert.Equal(t, code, 200)
  173. assert.Equal(t, len(decisions["deleted"]), 0)
  174. assert.Equal(t, len(decisions["new"]), 1)
  175. }
  176. func TestDeleteDecision(t *testing.T) {
  177. lapi := SetupLAPITest(t)
  178. // Create Valid Alert
  179. lapi.InsertAlertFromFile("./tests/alert_sample.json")
  180. // Delete alert with Invalid filter
  181. w := lapi.RecordResponse("DELETE", "/v1/decisions?test=test", emptyBody)
  182. assert.Equal(t, 500, w.Code)
  183. err_resp, _, err := readDecisionsErrorResp(w)
  184. assert.NoError(t, err)
  185. assert.Equal(t, err_resp["message"], "'test' doesn't exist: invalid filter")
  186. // Delete all alert
  187. w = lapi.RecordResponse("DELETE", "/v1/decisions", emptyBody)
  188. assert.Equal(t, 200, w.Code)
  189. resp, _, err := readDecisionsDeleteResp(w)
  190. assert.NoError(t, err)
  191. assert.Equal(t, resp.NbDeleted, "3")
  192. }
  193. func TestStreamStartDecisionDedup(t *testing.T) {
  194. //Ensure that at stream startup we only get the longest decision
  195. lapi := SetupLAPITest(t)
  196. // Create Valid Alert : 3 decisions for 127.0.0.1, longest has id=3
  197. lapi.InsertAlertFromFile("./tests/alert_sample.json")
  198. // Get Stream, we only get one decision (the longest one)
  199. w := lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  200. decisions, code, err := readDecisionsStreamResp(w)
  201. assert.Equal(t, err, nil)
  202. assert.Equal(t, code, 200)
  203. assert.Equal(t, len(decisions["deleted"]), 0)
  204. assert.Equal(t, len(decisions["new"]), 1)
  205. assert.Equal(t, decisions["new"][0].ID, int64(3))
  206. assert.Equal(t, *decisions["new"][0].Origin, "test")
  207. assert.Equal(t, *decisions["new"][0].Value, "127.0.0.1")
  208. // id=3 decision is deleted, this won't affect `deleted`, because there are decisions on the same ip
  209. w = lapi.RecordResponse("DELETE", "/v1/decisions/3", emptyBody)
  210. assert.Equal(t, 200, w.Code)
  211. // Get Stream, we only get one decision (the longest one, id=2)
  212. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  213. decisions, code, err = readDecisionsStreamResp(w)
  214. assert.Equal(t, err, nil)
  215. assert.Equal(t, code, 200)
  216. assert.Equal(t, len(decisions["deleted"]), 0)
  217. assert.Equal(t, len(decisions["new"]), 1)
  218. assert.Equal(t, decisions["new"][0].ID, int64(2))
  219. assert.Equal(t, *decisions["new"][0].Origin, "test")
  220. assert.Equal(t, *decisions["new"][0].Value, "127.0.0.1")
  221. // We delete another decision, yet don't receive it in stream, since there's another decision on same IP
  222. w = lapi.RecordResponse("DELETE", "/v1/decisions/2", emptyBody)
  223. assert.Equal(t, 200, w.Code)
  224. // And get the remaining decision (1)
  225. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  226. decisions, code, err = readDecisionsStreamResp(w)
  227. assert.Equal(t, err, nil)
  228. assert.Equal(t, code, 200)
  229. assert.Equal(t, len(decisions["deleted"]), 0)
  230. assert.Equal(t, len(decisions["new"]), 1)
  231. assert.Equal(t, decisions["new"][0].ID, int64(1))
  232. assert.Equal(t, *decisions["new"][0].Origin, "test")
  233. assert.Equal(t, *decisions["new"][0].Value, "127.0.0.1")
  234. // We delete the last decision, we receive the delete order
  235. w = lapi.RecordResponse("DELETE", "/v1/decisions/1", emptyBody)
  236. assert.Equal(t, 200, w.Code)
  237. //and now we only get a deleted decision
  238. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  239. decisions, code, err = readDecisionsStreamResp(w)
  240. assert.Equal(t, err, nil)
  241. assert.Equal(t, code, 200)
  242. assert.Equal(t, len(decisions["deleted"]), 1)
  243. assert.Equal(t, decisions["deleted"][0].ID, int64(1))
  244. assert.Equal(t, *decisions["deleted"][0].Origin, "test")
  245. assert.Equal(t, *decisions["deleted"][0].Value, "127.0.0.1")
  246. assert.Equal(t, len(decisions["new"]), 0)
  247. }
  248. func TestStreamDecisionDedup(t *testing.T) {
  249. //Ensure that at stream startup we only get the longest decision
  250. lapi := SetupLAPITest(t)
  251. // Create Valid Alert : 3 decisions for 127.0.0.1, longest has id=3
  252. lapi.InsertAlertFromFile("./tests/alert_sample.json")
  253. // Get Stream, we only get one decision (the longest one)
  254. w := lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  255. decisions, code, err := readDecisionsStreamResp(w)
  256. assert.Equal(t, err, nil)
  257. assert.Equal(t, code, 200)
  258. assert.Equal(t, len(decisions["deleted"]), 0)
  259. assert.Equal(t, len(decisions["new"]), 1)
  260. assert.Equal(t, decisions["new"][0].ID, int64(3))
  261. assert.Equal(t, *decisions["new"][0].Origin, "test")
  262. assert.Equal(t, *decisions["new"][0].Value, "127.0.0.1")
  263. // id=3 decision is deleted, this won't affect `deleted`, because there are decisions on the same ip
  264. w = lapi.RecordResponse("DELETE", "/v1/decisions/3", emptyBody)
  265. assert.Equal(t, 200, w.Code)
  266. w = lapi.RecordResponse("GET", "/v1/decisions/stream", emptyBody)
  267. assert.Equal(t, err, nil)
  268. decisions, code, err = readDecisionsStreamResp(w)
  269. assert.Equal(t, err, nil)
  270. assert.Equal(t, code, 200)
  271. assert.Equal(t, len(decisions["deleted"]), 0)
  272. assert.Equal(t, len(decisions["new"]), 0)
  273. // We delete another decision, yet don't receive it in stream, since there's another decision on same IP
  274. w = lapi.RecordResponse("DELETE", "/v1/decisions/2", emptyBody)
  275. assert.Equal(t, 200, w.Code)
  276. w = lapi.RecordResponse("GET", "/v1/decisions/stream", emptyBody)
  277. decisions, code, err = readDecisionsStreamResp(w)
  278. assert.Equal(t, err, nil)
  279. assert.Equal(t, code, 200)
  280. assert.Equal(t, len(decisions["deleted"]), 0)
  281. assert.Equal(t, len(decisions["new"]), 0)
  282. // We delete the last decision, we receive the delete order
  283. w = lapi.RecordResponse("DELETE", "/v1/decisions/1", emptyBody)
  284. assert.Equal(t, 200, w.Code)
  285. w = lapi.RecordResponse("GET", "/v1/decisions/stream", emptyBody)
  286. decisions, code, err = readDecisionsStreamResp(w)
  287. assert.Equal(t, err, nil)
  288. assert.Equal(t, code, 200)
  289. assert.Equal(t, len(decisions["deleted"]), 1)
  290. assert.Equal(t, decisions["deleted"][0].ID, int64(1))
  291. assert.Equal(t, *decisions["deleted"][0].Origin, "test")
  292. assert.Equal(t, *decisions["deleted"][0].Value, "127.0.0.1")
  293. assert.Equal(t, len(decisions["new"]), 0)
  294. }
  295. func TestStreamDecisionFilters(t *testing.T) {
  296. lapi := SetupLAPITest(t)
  297. // Create Valid Alert
  298. lapi.InsertAlertFromFile("./tests/alert_stream_fixture.json")
  299. w := lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true", emptyBody)
  300. decisions, code, err := readDecisionsStreamResp(w)
  301. assert.Equal(t, 200, code)
  302. assert.Equal(t, err, nil)
  303. assert.Equal(t, len(decisions["deleted"]), 0)
  304. assert.Equal(t, len(decisions["new"]), 3)
  305. assert.Equal(t, decisions["new"][0].ID, int64(1))
  306. assert.Equal(t, *decisions["new"][0].Origin, "test1")
  307. assert.Equal(t, *decisions["new"][0].Value, "127.0.0.1")
  308. assert.Equal(t, *decisions["new"][0].Scenario, "crowdsecurity/http_bf")
  309. assert.Equal(t, decisions["new"][1].ID, int64(2))
  310. assert.Equal(t, *decisions["new"][1].Origin, "test2")
  311. assert.Equal(t, *decisions["new"][1].Value, "127.0.0.1")
  312. assert.Equal(t, *decisions["new"][1].Scenario, "crowdsecurity/ssh_bf")
  313. assert.Equal(t, decisions["new"][2].ID, int64(3))
  314. assert.Equal(t, *decisions["new"][2].Origin, "test3")
  315. assert.Equal(t, *decisions["new"][2].Value, "127.0.0.1")
  316. assert.Equal(t, *decisions["new"][2].Scenario, "crowdsecurity/ddos")
  317. // test filter scenarios_not_containing
  318. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true&scenarios_not_containing=http", emptyBody)
  319. decisions, code, err = readDecisionsStreamResp(w)
  320. assert.Equal(t, err, nil)
  321. assert.Equal(t, 200, code)
  322. assert.Equal(t, len(decisions["deleted"]), 0)
  323. assert.Equal(t, len(decisions["new"]), 2)
  324. assert.Equal(t, decisions["new"][0].ID, int64(2))
  325. assert.Equal(t, decisions["new"][1].ID, int64(3))
  326. // test filter scenarios_containing
  327. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true&scenarios_containing=http", emptyBody)
  328. decisions, code, err = readDecisionsStreamResp(w)
  329. assert.Equal(t, err, nil)
  330. assert.Equal(t, 200, code)
  331. assert.Equal(t, len(decisions["deleted"]), 0)
  332. assert.Equal(t, len(decisions["new"]), 1)
  333. assert.Equal(t, decisions["new"][0].ID, int64(1))
  334. // test filters both by scenarios_not_containing and scenarios_containing
  335. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true&scenarios_not_containing=ssh&scenarios_containing=ddos", emptyBody)
  336. decisions, code, err = readDecisionsStreamResp(w)
  337. assert.Equal(t, err, nil)
  338. assert.Equal(t, 200, code)
  339. assert.Equal(t, len(decisions["deleted"]), 0)
  340. assert.Equal(t, len(decisions["new"]), 1)
  341. assert.Equal(t, decisions["new"][0].ID, int64(3))
  342. // test filter by origin
  343. w = lapi.RecordResponse("GET", "/v1/decisions/stream?startup=true&origins=test1,test2", emptyBody)
  344. decisions, code, err = readDecisionsStreamResp(w)
  345. assert.Equal(t, err, nil)
  346. assert.Equal(t, 200, code)
  347. assert.Equal(t, len(decisions["deleted"]), 0)
  348. assert.Equal(t, len(decisions["new"]), 2)
  349. assert.Equal(t, decisions["new"][0].ID, int64(1))
  350. assert.Equal(t, decisions["new"][1].ID, int64(2))
  351. }