exprlib_test.go 23 KB

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