exprlib_test.go 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. package exprhelpers
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "testing"
  7. "time"
  8. "github.com/antonmedv/expr"
  9. "github.com/pkg/errors"
  10. log "github.com/sirupsen/logrus"
  11. "github.com/stretchr/testify/assert"
  12. "github.com/stretchr/testify/require"
  13. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  14. "github.com/crowdsecurity/crowdsec/pkg/cstest"
  15. "github.com/crowdsecurity/crowdsec/pkg/database"
  16. "github.com/crowdsecurity/crowdsec/pkg/models"
  17. "github.com/crowdsecurity/crowdsec/pkg/types"
  18. )
  19. var (
  20. TestFolder = "tests"
  21. )
  22. func getDBClient(t *testing.T) *database.Client {
  23. t.Helper()
  24. dbPath, err := os.CreateTemp("", "*sqlite")
  25. require.NoError(t, err)
  26. testDbClient, err := database.NewClient(&csconfig.DatabaseCfg{
  27. Type: "sqlite",
  28. DbName: "crowdsec",
  29. DbPath: dbPath.Name(),
  30. })
  31. require.NoError(t, err)
  32. return testDbClient
  33. }
  34. func TestVisitor(t *testing.T) {
  35. err := Init(nil)
  36. require.NoError(t, err)
  37. tests := []struct {
  38. name string
  39. filter string
  40. result bool
  41. env map[string]interface{}
  42. err error
  43. }{
  44. {
  45. name: "debug : no variable",
  46. filter: "'crowdsec' startsWith 'crowdse'",
  47. result: true,
  48. err: nil,
  49. env: map[string]interface{}{},
  50. },
  51. {
  52. name: "debug : simple variable",
  53. filter: "'crowdsec' startsWith static_one && 1 == 1",
  54. result: true,
  55. err: nil,
  56. env: map[string]interface{}{"static_one": string("crowdse")},
  57. },
  58. {
  59. name: "debug : simple variable re-used",
  60. filter: "static_one.foo == 'bar' && static_one.foo != 'toto'",
  61. result: true,
  62. err: nil,
  63. env: map[string]interface{}{"static_one": map[string]string{"foo": "bar"}},
  64. },
  65. {
  66. name: "debug : can't compile",
  67. filter: "static_one.foo.toto == 'lol'",
  68. result: false,
  69. err: fmt.Errorf("bad syntax"),
  70. env: map[string]interface{}{"static_one": map[string]string{"foo": "bar"}},
  71. },
  72. {
  73. name: "debug : can't compile #2",
  74. filter: "static_one.f!oo.to/to == 'lol'",
  75. result: false,
  76. err: fmt.Errorf("bad syntax"),
  77. env: map[string]interface{}{"static_one": map[string]string{"foo": "bar"}},
  78. },
  79. {
  80. name: "debug : can't compile #3",
  81. filter: "",
  82. result: false,
  83. err: fmt.Errorf("bad syntax"),
  84. env: map[string]interface{}{"static_one": map[string]string{"foo": "bar"}},
  85. },
  86. }
  87. log.SetLevel(log.DebugLevel)
  88. clog := log.WithFields(log.Fields{
  89. "type": "test",
  90. })
  91. for _, test := range tests {
  92. compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(test.env)))
  93. if err != nil && test.err == nil {
  94. log.Fatalf("compile: %s", err)
  95. }
  96. debugFilter, err := NewDebugger(test.filter, expr.Env(GetExprEnv(test.env)))
  97. if err != nil && test.err == nil {
  98. log.Fatalf("debug: %s", err)
  99. }
  100. if compiledFilter != nil {
  101. result, err := expr.Run(compiledFilter, GetExprEnv(test.env))
  102. if err != nil && test.err == nil {
  103. log.Fatalf("run : %s", err)
  104. }
  105. if isOk := assert.Equal(t, test.result, result); !isOk {
  106. t.Fatalf("test '%s' : NOK", test.filter)
  107. }
  108. }
  109. if debugFilter != nil {
  110. debugFilter.Run(clog, test.result, GetExprEnv(test.env))
  111. }
  112. }
  113. }
  114. func TestDistanceHelper(t *testing.T) {
  115. //one set of coord is empty
  116. ret, err := Distance("0.0", "0.0", "12.1", "12.1")
  117. assert.NoError(t, err)
  118. assert.Equal(t, 0.0, ret)
  119. //those aren't even coords
  120. ret, err = Distance("lol", "42.1", "12.1", "12.1")
  121. assert.NotNil(t, err)
  122. assert.Equal(t, 0.0, ret)
  123. //real ones
  124. ret, err = Distance("51.45", "1.15", "41.54", "12.27")
  125. assert.NoError(t, err)
  126. assert.Equal(t, 1389.1793118293067, ret)
  127. }
  128. func TestRegexpCacheBehavior(t *testing.T) {
  129. err := Init(nil)
  130. require.NoError(t, err)
  131. filename := "test_data_re.txt"
  132. err = FileInit(TestFolder, filename, "regex")
  133. require.NoError(t, err)
  134. //cache with no TTL
  135. err = RegexpCacheInit(filename, types.DataSource{Type: "regex", Size: types.IntPtr(1)})
  136. require.NoError(t, err)
  137. ret := RegexpInFile("crowdsec", filename)
  138. assert.False(t, ret)
  139. assert.Equal(t, 1, dataFileRegexCache[filename].Len(false))
  140. ret = RegexpInFile("Crowdsec", filename)
  141. assert.True(t, ret)
  142. assert.Equal(t, 1, dataFileRegexCache[filename].Len(false))
  143. //cache with TTL
  144. ttl := 500 * time.Millisecond
  145. err = RegexpCacheInit(filename, types.DataSource{Type: "regex", Size: types.IntPtr(2), TTL: &ttl})
  146. require.NoError(t, err)
  147. ret = RegexpInFile("crowdsec", filename)
  148. assert.False(t, ret)
  149. assert.Equal(t, 1, dataFileRegexCache[filename].Len(true))
  150. time.Sleep(1 * time.Second)
  151. assert.Equal(t, 0, dataFileRegexCache[filename].Len(true))
  152. }
  153. func TestRegexpInFile(t *testing.T) {
  154. if err := Init(nil); err != nil {
  155. log.Fatal(err)
  156. }
  157. err := FileInit(TestFolder, "test_data_re.txt", "regex")
  158. if err != nil {
  159. log.Fatal(err)
  160. }
  161. tests := []struct {
  162. name string
  163. filter string
  164. result bool
  165. err error
  166. }{
  167. {
  168. name: "RegexpInFile() test: lower case word in data file",
  169. filter: "RegexpInFile('crowdsec', 'test_data_re.txt')",
  170. result: false,
  171. err: nil,
  172. },
  173. {
  174. name: "RegexpInFile() test: Match exactly",
  175. filter: "RegexpInFile('Crowdsec', 'test_data_re.txt')",
  176. result: true,
  177. err: nil,
  178. },
  179. {
  180. name: "RegexpInFile() test: match with word before",
  181. filter: "RegexpInFile('test Crowdsec', 'test_data_re.txt')",
  182. result: true,
  183. err: nil,
  184. },
  185. {
  186. name: "RegexpInFile() test: match with word before and other case",
  187. filter: "RegexpInFile('test CrowdSec', 'test_data_re.txt')",
  188. result: true,
  189. err: nil,
  190. },
  191. }
  192. for _, test := range tests {
  193. compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
  194. if err != nil {
  195. log.Fatal(err)
  196. }
  197. result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
  198. if err != nil {
  199. log.Fatal(err)
  200. }
  201. if isOk := assert.Equal(t, test.result, result); !isOk {
  202. t.Fatalf("test '%s' : NOK", test.name)
  203. }
  204. }
  205. }
  206. func TestFileInit(t *testing.T) {
  207. if err := Init(nil); err != nil {
  208. log.Fatal(err)
  209. }
  210. tests := []struct {
  211. name string
  212. filename string
  213. types string
  214. result int
  215. err error
  216. }{
  217. {
  218. name: "file with type:string",
  219. filename: "test_data.txt",
  220. types: "string",
  221. result: 3,
  222. },
  223. {
  224. name: "file with type:string and empty lines + commentaries",
  225. filename: "test_empty_line.txt",
  226. types: "string",
  227. result: 3,
  228. },
  229. {
  230. name: "file with type:re",
  231. filename: "test_data_re.txt",
  232. types: "regex",
  233. result: 2,
  234. },
  235. {
  236. name: "file without type",
  237. filename: "test_data_no_type.txt",
  238. types: "",
  239. },
  240. }
  241. for _, test := range tests {
  242. err := FileInit(TestFolder, test.filename, test.types)
  243. if err != nil {
  244. log.Fatal(err)
  245. }
  246. if test.types == "string" {
  247. if _, ok := dataFile[test.filename]; !ok {
  248. t.Fatalf("test '%s' : NOK", test.name)
  249. }
  250. if isOk := assert.Equal(t, test.result, len(dataFile[test.filename])); !isOk {
  251. t.Fatalf("test '%s' : NOK", test.name)
  252. }
  253. } else if test.types == "regex" {
  254. if _, ok := dataFileRegex[test.filename]; !ok {
  255. t.Fatalf("test '%s' : NOK", test.name)
  256. }
  257. if isOk := assert.Equal(t, test.result, len(dataFileRegex[test.filename])); !isOk {
  258. t.Fatalf("test '%s' : NOK", test.name)
  259. }
  260. } else {
  261. if _, ok := dataFileRegex[test.filename]; ok {
  262. t.Fatalf("test '%s' : NOK", test.name)
  263. }
  264. if _, ok := dataFile[test.filename]; ok {
  265. t.Fatalf("test '%s' : NOK", test.name)
  266. }
  267. }
  268. log.Printf("test '%s' : OK", test.name)
  269. }
  270. }
  271. func TestFile(t *testing.T) {
  272. if err := Init(nil); err != nil {
  273. log.Fatal(err)
  274. }
  275. err := FileInit(TestFolder, "test_data.txt", "string")
  276. if err != nil {
  277. log.Fatal(err)
  278. }
  279. tests := []struct {
  280. name string
  281. filter string
  282. result bool
  283. err error
  284. }{
  285. {
  286. name: "File() test: word in file",
  287. filter: "'Crowdsec' in File('test_data.txt')",
  288. result: true,
  289. err: nil,
  290. },
  291. {
  292. name: "File() test: word in file but different case",
  293. filter: "'CrowdSecurity' in File('test_data.txt')",
  294. result: false,
  295. err: nil,
  296. },
  297. {
  298. name: "File() test: word not in file",
  299. filter: "'test' in File('test_data.txt')",
  300. result: false,
  301. err: nil,
  302. },
  303. {
  304. name: "File() test: filepath provided doesn't exist",
  305. filter: "'test' in File('non_existing_data.txt')",
  306. result: false,
  307. err: nil,
  308. },
  309. }
  310. for _, test := range tests {
  311. compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
  312. if err != nil {
  313. log.Fatal(err)
  314. }
  315. result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
  316. if err != nil {
  317. log.Fatal(err)
  318. }
  319. if isOk := assert.Equal(t, test.result, result); !isOk {
  320. t.Fatalf("test '%s' : NOK", test.name)
  321. }
  322. log.Printf("test '%s' : OK", test.name)
  323. }
  324. }
  325. func TestIpInRange(t *testing.T) {
  326. tests := []struct {
  327. name string
  328. env map[string]interface{}
  329. code string
  330. result bool
  331. err string
  332. }{
  333. {
  334. name: "IpInRange() test: basic test",
  335. env: map[string]interface{}{
  336. "ip": "192.168.0.1",
  337. "ipRange": "192.168.0.0/24",
  338. "IpInRange": IpInRange,
  339. },
  340. code: "IpInRange(ip, ipRange)",
  341. result: true,
  342. err: "",
  343. },
  344. {
  345. name: "IpInRange() test: malformed IP",
  346. env: map[string]interface{}{
  347. "ip": "192.168.0",
  348. "ipRange": "192.168.0.0/24",
  349. "IpInRange": IpInRange,
  350. },
  351. code: "IpInRange(ip, ipRange)",
  352. result: false,
  353. err: "",
  354. },
  355. {
  356. name: "IpInRange() test: malformed IP range",
  357. env: map[string]interface{}{
  358. "ip": "192.168.0.0/255",
  359. "ipRange": "192.168.0.0/24",
  360. "IpInRange": IpInRange,
  361. },
  362. code: "IpInRange(ip, ipRange)",
  363. result: false,
  364. err: "",
  365. },
  366. }
  367. for _, test := range tests {
  368. program, err := expr.Compile(test.code, expr.Env(test.env))
  369. require.NoError(t, err)
  370. output, err := expr.Run(program, test.env)
  371. require.NoError(t, err)
  372. require.Equal(t, test.result, output)
  373. log.Printf("test '%s' : OK", test.name)
  374. }
  375. }
  376. func TestIpToRange(t *testing.T) {
  377. tests := []struct {
  378. name string
  379. env map[string]interface{}
  380. code string
  381. result string
  382. err string
  383. }{
  384. {
  385. name: "IpToRange() test: IPv4",
  386. env: map[string]interface{}{
  387. "ip": "192.168.1.1",
  388. "netmask": "16",
  389. "IpToRange": IpToRange,
  390. },
  391. code: "IpToRange(ip, netmask)",
  392. result: "192.168.0.0/16",
  393. err: "",
  394. },
  395. {
  396. name: "IpToRange() test: IPv6",
  397. env: map[string]interface{}{
  398. "ip": "2001:db8::1",
  399. "netmask": "/64",
  400. "IpToRange": IpToRange,
  401. },
  402. code: "IpToRange(ip, netmask)",
  403. result: "2001:db8::/64",
  404. err: "",
  405. },
  406. {
  407. name: "IpToRange() test: malformed netmask",
  408. env: map[string]interface{}{
  409. "ip": "192.168.0.1",
  410. "netmask": "test",
  411. "IpToRange": IpToRange,
  412. },
  413. code: "IpToRange(ip, netmask)",
  414. result: "",
  415. err: "",
  416. },
  417. {
  418. name: "IpToRange() test: malformed IP",
  419. env: map[string]interface{}{
  420. "ip": "a.b.c.d",
  421. "netmask": "24",
  422. "IpToRange": IpToRange,
  423. },
  424. code: "IpToRange(ip, netmask)",
  425. result: "",
  426. err: "",
  427. },
  428. {
  429. name: "IpToRange() test: too high netmask",
  430. env: map[string]interface{}{
  431. "ip": "192.168.1.1",
  432. "netmask": "35",
  433. "IpToRange": IpToRange,
  434. },
  435. code: "IpToRange(ip, netmask)",
  436. result: "",
  437. err: "",
  438. },
  439. }
  440. for _, test := range tests {
  441. program, err := expr.Compile(test.code, expr.Env(test.env))
  442. require.NoError(t, err)
  443. output, err := expr.Run(program, test.env)
  444. require.NoError(t, err)
  445. require.Equal(t, test.result, output)
  446. log.Printf("test '%s' : OK", test.name)
  447. }
  448. }
  449. func TestAtof(t *testing.T) {
  450. testFloat := "1.5"
  451. expectedFloat := 1.5
  452. if Atof(testFloat) != expectedFloat {
  453. t.Fatalf("Atof should return 1.5 as a float")
  454. }
  455. log.Printf("test 'Atof()' : OK")
  456. //bad float
  457. testFloat = "1aaa.5"
  458. expectedFloat = 0.0
  459. if Atof(testFloat) != expectedFloat {
  460. t.Fatalf("Atof should return a negative value (error) as a float got")
  461. }
  462. log.Printf("test 'Atof()' : OK")
  463. }
  464. func TestUpper(t *testing.T) {
  465. testStr := "test"
  466. expectedStr := "TEST"
  467. if Upper(testStr) != expectedStr {
  468. t.Fatalf("Upper() should return test in upper case")
  469. }
  470. log.Printf("test 'Upper()' : OK")
  471. }
  472. func TestTimeNow(t *testing.T) {
  473. ti, err := time.Parse(time.RFC3339, TimeNow())
  474. if err != nil {
  475. t.Fatalf("Error parsing the return value of TimeNow: %s", err)
  476. }
  477. if -1*time.Until(ti) > time.Second {
  478. t.Fatalf("TimeNow func should return time.Now().UTC()")
  479. }
  480. log.Printf("test 'TimeNow()' : OK")
  481. }
  482. func TestParseUri(t *testing.T) {
  483. tests := []struct {
  484. name string
  485. env map[string]interface{}
  486. code string
  487. result map[string][]string
  488. err string
  489. }{
  490. {
  491. name: "ParseUri() test: basic test",
  492. env: map[string]interface{}{
  493. "uri": "/foo?a=1&b=2",
  494. "ParseUri": ParseUri,
  495. },
  496. code: "ParseUri(uri)",
  497. result: map[string][]string{"a": {"1"}, "b": {"2"}},
  498. err: "",
  499. },
  500. {
  501. name: "ParseUri() test: no param",
  502. env: map[string]interface{}{
  503. "uri": "/foo",
  504. "ParseUri": ParseUri,
  505. },
  506. code: "ParseUri(uri)",
  507. result: map[string][]string{},
  508. err: "",
  509. },
  510. {
  511. name: "ParseUri() test: extra question mark",
  512. env: map[string]interface{}{
  513. "uri": "/foo?a=1&b=2?",
  514. "ParseUri": ParseUri,
  515. },
  516. code: "ParseUri(uri)",
  517. result: map[string][]string{"a": {"1"}, "b": {"2?"}},
  518. err: "",
  519. },
  520. {
  521. name: "ParseUri() test: weird params",
  522. env: map[string]interface{}{
  523. "uri": "/foo?&?&&&&?=123",
  524. "ParseUri": ParseUri,
  525. },
  526. code: "ParseUri(uri)",
  527. result: map[string][]string{"?": {"", "123"}},
  528. err: "",
  529. },
  530. {
  531. name: "ParseUri() test: bad encoding",
  532. env: map[string]interface{}{
  533. "uri": "/foo?a=%%F",
  534. "ParseUri": ParseUri,
  535. },
  536. code: "ParseUri(uri)",
  537. result: map[string][]string{},
  538. err: "",
  539. },
  540. }
  541. for _, test := range tests {
  542. program, err := expr.Compile(test.code, expr.Env(test.env))
  543. require.NoError(t, err)
  544. output, err := expr.Run(program, test.env)
  545. require.NoError(t, err)
  546. require.Equal(t, test.result, output)
  547. log.Printf("test '%s' : OK", test.name)
  548. }
  549. }
  550. func TestQueryEscape(t *testing.T) {
  551. tests := []struct {
  552. name string
  553. env map[string]interface{}
  554. code string
  555. result string
  556. err string
  557. }{
  558. {
  559. name: "QueryEscape() test: basic test",
  560. env: map[string]interface{}{
  561. "uri": "/foo?a=1&b=2",
  562. "QueryEscape": QueryEscape,
  563. },
  564. code: "QueryEscape(uri)",
  565. result: "%2Ffoo%3Fa%3D1%26b%3D2",
  566. err: "",
  567. },
  568. {
  569. name: "QueryEscape() test: basic test",
  570. env: map[string]interface{}{
  571. "uri": "/foo?a=1&&b=<>'\"",
  572. "QueryEscape": QueryEscape,
  573. },
  574. code: "QueryEscape(uri)",
  575. result: "%2Ffoo%3Fa%3D1%26%26b%3D%3C%3E%27%22",
  576. err: "",
  577. },
  578. }
  579. for _, test := range tests {
  580. program, err := expr.Compile(test.code, expr.Env(test.env))
  581. require.NoError(t, err)
  582. output, err := expr.Run(program, test.env)
  583. require.NoError(t, err)
  584. require.Equal(t, test.result, output)
  585. log.Printf("test '%s' : OK", test.name)
  586. }
  587. }
  588. func TestPathEscape(t *testing.T) {
  589. tests := []struct {
  590. name string
  591. env map[string]interface{}
  592. code string
  593. result string
  594. err string
  595. }{
  596. {
  597. name: "PathEscape() test: basic test",
  598. env: map[string]interface{}{
  599. "uri": "/foo?a=1&b=2",
  600. "PathEscape": PathEscape,
  601. },
  602. code: "PathEscape(uri)",
  603. result: "%2Ffoo%3Fa=1&b=2",
  604. err: "",
  605. },
  606. {
  607. name: "PathEscape() test: basic test with more special chars",
  608. env: map[string]interface{}{
  609. "uri": "/foo?a=1&&b=<>'\"",
  610. "PathEscape": PathEscape,
  611. },
  612. code: "PathEscape(uri)",
  613. result: "%2Ffoo%3Fa=1&&b=%3C%3E%27%22",
  614. err: "",
  615. },
  616. }
  617. for _, test := range tests {
  618. program, err := expr.Compile(test.code, expr.Env(test.env))
  619. require.NoError(t, err)
  620. output, err := expr.Run(program, test.env)
  621. require.NoError(t, err)
  622. require.Equal(t, test.result, output)
  623. log.Printf("test '%s' : OK", test.name)
  624. }
  625. }
  626. func TestPathUnescape(t *testing.T) {
  627. tests := []struct {
  628. name string
  629. env map[string]interface{}
  630. code string
  631. result string
  632. err string
  633. }{
  634. {
  635. name: "PathUnescape() test: basic test",
  636. env: map[string]interface{}{
  637. "uri": "%2Ffoo%3Fa=1&b=%3C%3E%27%22",
  638. "PathUnescape": PathUnescape,
  639. },
  640. code: "PathUnescape(uri)",
  641. result: "/foo?a=1&b=<>'\"",
  642. err: "",
  643. },
  644. {
  645. name: "PathUnescape() test: basic test with more special chars",
  646. env: map[string]interface{}{
  647. "uri": "/$%7Bjndi",
  648. "PathUnescape": PathUnescape,
  649. },
  650. code: "PathUnescape(uri)",
  651. result: "/${jndi",
  652. err: "",
  653. },
  654. }
  655. for _, test := range tests {
  656. program, err := expr.Compile(test.code, expr.Env(test.env))
  657. require.NoError(t, err)
  658. output, err := expr.Run(program, test.env)
  659. require.NoError(t, err)
  660. require.Equal(t, test.result, output)
  661. log.Printf("test '%s' : OK", test.name)
  662. }
  663. }
  664. func TestQueryUnescape(t *testing.T) {
  665. tests := []struct {
  666. name string
  667. env map[string]interface{}
  668. code string
  669. result string
  670. err string
  671. }{
  672. {
  673. name: "QueryUnescape() test: basic test",
  674. env: map[string]interface{}{
  675. "uri": "%2Ffoo%3Fa=1&b=%3C%3E%27%22",
  676. "QueryUnescape": QueryUnescape,
  677. },
  678. code: "QueryUnescape(uri)",
  679. result: "/foo?a=1&b=<>'\"",
  680. err: "",
  681. },
  682. {
  683. name: "QueryUnescape() test: basic test with more special chars",
  684. env: map[string]interface{}{
  685. "uri": "/$%7Bjndi",
  686. "QueryUnescape": QueryUnescape,
  687. },
  688. code: "QueryUnescape(uri)",
  689. result: "/${jndi",
  690. err: "",
  691. },
  692. }
  693. for _, test := range tests {
  694. program, err := expr.Compile(test.code, expr.Env(test.env))
  695. require.NoError(t, err)
  696. output, err := expr.Run(program, test.env)
  697. require.NoError(t, err)
  698. require.Equal(t, test.result, output)
  699. log.Printf("test '%s' : OK", test.name)
  700. }
  701. }
  702. func TestLower(t *testing.T) {
  703. tests := []struct {
  704. name string
  705. env map[string]interface{}
  706. code string
  707. result string
  708. err string
  709. }{
  710. {
  711. name: "Lower() test: basic test",
  712. env: map[string]interface{}{
  713. "name": "ABCDEFG",
  714. "Lower": Lower,
  715. },
  716. code: "Lower(name)",
  717. result: "abcdefg",
  718. err: "",
  719. },
  720. {
  721. name: "Lower() test: basic test with more special chars",
  722. env: map[string]interface{}{
  723. "name": "AbcDefG!#",
  724. "Lower": Lower,
  725. },
  726. code: "Lower(name)",
  727. result: "abcdefg!#",
  728. err: "",
  729. },
  730. }
  731. for _, test := range tests {
  732. program, err := expr.Compile(test.code, expr.Env(test.env))
  733. require.NoError(t, err)
  734. output, err := expr.Run(program, test.env)
  735. require.NoError(t, err)
  736. require.Equal(t, test.result, output)
  737. log.Printf("test '%s' : OK", test.name)
  738. }
  739. }
  740. func TestGetDecisionsCount(t *testing.T) {
  741. var err error
  742. var start_ip, start_sfx, end_ip, end_sfx int64
  743. var ip_sz int
  744. existingIP := "1.2.3.4"
  745. unknownIP := "1.2.3.5"
  746. ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(existingIP)
  747. if err != nil {
  748. t.Errorf("unable to convert '%s' to int: %s", existingIP, err)
  749. }
  750. // Add sample data to DB
  751. dbClient = getDBClient(t)
  752. decision := dbClient.Ent.Decision.Create().
  753. SetUntil(time.Now().Add(time.Hour)).
  754. SetScenario("crowdsec/test").
  755. SetStartIP(start_ip).
  756. SetStartSuffix(start_sfx).
  757. SetEndIP(end_ip).
  758. SetEndSuffix(end_sfx).
  759. SetIPSize(int64(ip_sz)).
  760. SetType("ban").
  761. SetScope("IP").
  762. SetValue(existingIP).
  763. SetOrigin("CAPI").
  764. SaveX(context.Background())
  765. if decision == nil {
  766. assert.Error(t, errors.Errorf("Failed to create sample decision"))
  767. }
  768. tests := []struct {
  769. name string
  770. env map[string]interface{}
  771. code string
  772. result string
  773. err string
  774. }{
  775. {
  776. name: "GetDecisionsCount() test: existing IP count",
  777. env: map[string]interface{}{
  778. "Alert": &models.Alert{
  779. Source: &models.Source{
  780. Value: &existingIP,
  781. },
  782. Decisions: []*models.Decision{
  783. {
  784. Value: &existingIP,
  785. },
  786. },
  787. },
  788. "GetDecisionsCount": GetDecisionsCount,
  789. "sprintf": fmt.Sprintf,
  790. },
  791. code: "sprintf('%d', GetDecisionsCount(Alert.GetValue()))",
  792. result: "1",
  793. err: "",
  794. },
  795. {
  796. name: "GetDecisionsCount() test: unknown IP count",
  797. env: map[string]interface{}{
  798. "Alert": &models.Alert{
  799. Source: &models.Source{
  800. Value: &unknownIP,
  801. },
  802. Decisions: []*models.Decision{
  803. {
  804. Value: &unknownIP,
  805. },
  806. },
  807. },
  808. "GetDecisionsCount": GetDecisionsCount,
  809. "sprintf": fmt.Sprintf,
  810. },
  811. code: "sprintf('%d', GetDecisionsCount(Alert.GetValue()))",
  812. result: "0",
  813. err: "",
  814. },
  815. }
  816. for _, test := range tests {
  817. program, err := expr.Compile(test.code, expr.Env(GetExprEnv(test.env)))
  818. require.NoError(t, err)
  819. output, err := expr.Run(program, GetExprEnv(test.env))
  820. require.NoError(t, err)
  821. require.Equal(t, test.result, output)
  822. log.Printf("test '%s' : OK", test.name)
  823. }
  824. }
  825. func TestGetDecisionsSinceCount(t *testing.T) {
  826. var err error
  827. var start_ip, start_sfx, end_ip, end_sfx int64
  828. var ip_sz int
  829. existingIP := "1.2.3.4"
  830. unknownIP := "1.2.3.5"
  831. ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(existingIP)
  832. if err != nil {
  833. t.Errorf("unable to convert '%s' to int: %s", existingIP, err)
  834. }
  835. // Add sample data to DB
  836. dbClient = getDBClient(t)
  837. decision := dbClient.Ent.Decision.Create().
  838. SetUntil(time.Now().Add(time.Hour)).
  839. SetScenario("crowdsec/test").
  840. SetStartIP(start_ip).
  841. SetStartSuffix(start_sfx).
  842. SetEndIP(end_ip).
  843. SetEndSuffix(end_sfx).
  844. SetIPSize(int64(ip_sz)).
  845. SetType("ban").
  846. SetScope("IP").
  847. SetValue(existingIP).
  848. SetOrigin("CAPI").
  849. SaveX(context.Background())
  850. if decision == nil {
  851. assert.Error(t, errors.Errorf("Failed to create sample decision"))
  852. }
  853. decision2 := dbClient.Ent.Decision.Create().
  854. SetCreatedAt(time.Now().AddDate(0, 0, -1)).
  855. SetUntil(time.Now().AddDate(0, 0, -1)).
  856. SetScenario("crowdsec/test").
  857. SetStartIP(start_ip).
  858. SetStartSuffix(start_sfx).
  859. SetEndIP(end_ip).
  860. SetEndSuffix(end_sfx).
  861. SetIPSize(int64(ip_sz)).
  862. SetType("ban").
  863. SetScope("IP").
  864. SetValue(existingIP).
  865. SetOrigin("CAPI").
  866. SaveX(context.Background())
  867. if decision2 == nil {
  868. assert.Error(t, errors.Errorf("Failed to create sample decision"))
  869. }
  870. tests := []struct {
  871. name string
  872. env map[string]interface{}
  873. code string
  874. result string
  875. err string
  876. }{
  877. {
  878. name: "GetDecisionsSinceCount() test: existing IP count since more than 1 day",
  879. env: map[string]interface{}{
  880. "Alert": &models.Alert{
  881. Source: &models.Source{
  882. Value: &existingIP,
  883. },
  884. Decisions: []*models.Decision{
  885. {
  886. Value: &existingIP,
  887. },
  888. },
  889. },
  890. "GetDecisionsSinceCount": GetDecisionsSinceCount,
  891. "sprintf": fmt.Sprintf,
  892. },
  893. code: "sprintf('%d', GetDecisionsSinceCount(Alert.GetValue(), '25h'))",
  894. result: "2",
  895. err: "",
  896. },
  897. {
  898. name: "GetDecisionsSinceCount() test: existing IP count since more than 1 hour",
  899. env: map[string]interface{}{
  900. "Alert": &models.Alert{
  901. Source: &models.Source{
  902. Value: &existingIP,
  903. },
  904. Decisions: []*models.Decision{
  905. {
  906. Value: &existingIP,
  907. },
  908. },
  909. },
  910. "GetDecisionsSinceCount": GetDecisionsSinceCount,
  911. "sprintf": fmt.Sprintf,
  912. },
  913. code: "sprintf('%d', GetDecisionsSinceCount(Alert.GetValue(), '1h'))",
  914. result: "1",
  915. err: "",
  916. },
  917. {
  918. name: "GetDecisionsSinceCount() test: unknown IP count",
  919. env: map[string]interface{}{
  920. "Alert": &models.Alert{
  921. Source: &models.Source{
  922. Value: &unknownIP,
  923. },
  924. Decisions: []*models.Decision{
  925. {
  926. Value: &unknownIP,
  927. },
  928. },
  929. },
  930. "GetDecisionsSinceCount": GetDecisionsSinceCount,
  931. "sprintf": fmt.Sprintf,
  932. },
  933. code: "sprintf('%d', GetDecisionsSinceCount(Alert.GetValue(), '1h'))",
  934. result: "0",
  935. err: "",
  936. },
  937. }
  938. for _, test := range tests {
  939. program, err := expr.Compile(test.code, expr.Env(GetExprEnv(test.env)))
  940. require.NoError(t, err)
  941. output, err := expr.Run(program, GetExprEnv(test.env))
  942. require.NoError(t, err)
  943. require.Equal(t, test.result, output)
  944. log.Printf("test '%s' : OK", test.name)
  945. }
  946. }
  947. func TestParseUnixTime(t *testing.T) {
  948. tests := []struct {
  949. name string
  950. value string
  951. expected time.Time
  952. expectedErr string
  953. }{
  954. {
  955. name: "ParseUnix() test: valid value with milli",
  956. value: "1672239773.3590894",
  957. expected: time.Date(2022, 12, 28, 15, 02, 53, 0, time.UTC),
  958. },
  959. {
  960. name: "ParseUnix() test: valid value without milli",
  961. value: "1672239773",
  962. expected: time.Date(2022, 12, 28, 15, 02, 53, 0, time.UTC),
  963. },
  964. {
  965. name: "ParseUnix() test: invalid input",
  966. value: "AbcDefG!#",
  967. expected: time.Time{},
  968. expectedErr: "unable to parse AbcDefG!# as unix timestamp",
  969. },
  970. {
  971. name: "ParseUnix() test: negative value",
  972. value: "-1000",
  973. expected: time.Time{},
  974. expectedErr: "unable to parse -1000 as unix timestamp",
  975. },
  976. }
  977. for _, tc := range tests {
  978. tc := tc
  979. t.Run(tc.name, func(t *testing.T) {
  980. output, err := ParseUnixTime(tc.value)
  981. cstest.RequireErrorContains(t, err, tc.expectedErr)
  982. if tc.expectedErr != "" {
  983. return
  984. }
  985. require.WithinDuration(t, tc.expected, output, time.Second)
  986. })
  987. }
  988. }
  989. func TestIsIp(t *testing.T) {
  990. tests := []struct {
  991. name string
  992. method func(string) bool
  993. value string
  994. expected bool
  995. }{
  996. {
  997. name: "IsIPV4() test: valid IPv4",
  998. method: IsIPV4,
  999. value: "1.2.3.4",
  1000. expected: true,
  1001. },
  1002. {
  1003. name: "IsIPV6() test: valid IPv6",
  1004. method: IsIPV6,
  1005. value: "1.2.3.4",
  1006. expected: false,
  1007. },
  1008. {
  1009. name: "IsIPV6() test: valid IPv6",
  1010. method: IsIPV6,
  1011. value: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
  1012. expected: true,
  1013. },
  1014. {
  1015. name: "IsIPV4() test: valid IPv6",
  1016. method: IsIPV4,
  1017. value: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
  1018. expected: false,
  1019. },
  1020. {
  1021. name: "IsIP() test: invalid IP",
  1022. method: IsIP,
  1023. value: "foo.bar",
  1024. expected: false,
  1025. },
  1026. {
  1027. name: "IsIP() test: valid IPv4",
  1028. method: IsIP,
  1029. value: "1.2.3.4",
  1030. expected: true,
  1031. },
  1032. {
  1033. name: "IsIP() test: valid IPv6",
  1034. method: IsIP,
  1035. value: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
  1036. expected: true,
  1037. },
  1038. {
  1039. name: "IsIPV4() test: invalid IPv4",
  1040. method: IsIPV4,
  1041. value: "foo.bar",
  1042. expected: false,
  1043. },
  1044. {
  1045. name: "IsIPV6() test: invalid IPv6",
  1046. method: IsIPV6,
  1047. value: "foo.bar",
  1048. expected: false,
  1049. },
  1050. }
  1051. for _, tc := range tests {
  1052. tc := tc
  1053. t.Run(tc.name, func(t *testing.T) {
  1054. output := tc.method(tc.value)
  1055. require.Equal(t, tc.expected, output)
  1056. })
  1057. }
  1058. }