exprlib_test.go 26 KB

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