exprlib_test.go 33 KB

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