synapse 2.192.0__py311-none-any.whl → 2.194.0__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (77) hide show
  1. synapse/common.py +15 -0
  2. synapse/cortex.py +19 -25
  3. synapse/datamodel.py +6 -3
  4. synapse/exc.py +6 -1
  5. synapse/lib/agenda.py +17 -6
  6. synapse/lib/ast.py +242 -97
  7. synapse/lib/auth.py +1 -0
  8. synapse/lib/cell.py +31 -85
  9. synapse/lib/cli.py +20 -11
  10. synapse/lib/parser.py +5 -1
  11. synapse/lib/snap.py +44 -15
  12. synapse/lib/storm.lark +16 -1
  13. synapse/lib/storm.py +40 -21
  14. synapse/lib/storm_format.py +1 -0
  15. synapse/lib/stormctrl.py +88 -6
  16. synapse/lib/stormlib/cache.py +6 -2
  17. synapse/lib/stormlib/json.py +5 -2
  18. synapse/lib/stormlib/scrape.py +1 -1
  19. synapse/lib/stormlib/stix.py +8 -8
  20. synapse/lib/stormtypes.py +32 -5
  21. synapse/lib/version.py +2 -2
  22. synapse/lib/view.py +20 -3
  23. synapse/models/geopol.py +1 -0
  24. synapse/models/geospace.py +1 -0
  25. synapse/models/inet.py +20 -1
  26. synapse/models/infotech.py +24 -6
  27. synapse/models/orgs.py +7 -2
  28. synapse/models/person.py +15 -4
  29. synapse/models/risk.py +19 -2
  30. synapse/models/telco.py +10 -3
  31. synapse/tests/test_axon.py +6 -6
  32. synapse/tests/test_cortex.py +133 -14
  33. synapse/tests/test_exc.py +4 -0
  34. synapse/tests/test_lib_agenda.py +282 -2
  35. synapse/tests/test_lib_aha.py +13 -6
  36. synapse/tests/test_lib_ast.py +301 -10
  37. synapse/tests/test_lib_auth.py +6 -7
  38. synapse/tests/test_lib_cell.py +71 -1
  39. synapse/tests/test_lib_grammar.py +14 -0
  40. synapse/tests/test_lib_layer.py +1 -1
  41. synapse/tests/test_lib_lmdbslab.py +3 -3
  42. synapse/tests/test_lib_storm.py +273 -55
  43. synapse/tests/test_lib_stormctrl.py +65 -0
  44. synapse/tests/test_lib_stormhttp.py +5 -5
  45. synapse/tests/test_lib_stormlib_auth.py +5 -5
  46. synapse/tests/test_lib_stormlib_cache.py +38 -6
  47. synapse/tests/test_lib_stormlib_json.py +20 -0
  48. synapse/tests/test_lib_stormlib_modelext.py +3 -3
  49. synapse/tests/test_lib_stormlib_scrape.py +6 -6
  50. synapse/tests/test_lib_stormlib_spooled.py +1 -1
  51. synapse/tests/test_lib_stormlib_xml.py +5 -5
  52. synapse/tests/test_lib_stormtypes.py +54 -57
  53. synapse/tests/test_lib_view.py +1 -1
  54. synapse/tests/test_model_base.py +1 -2
  55. synapse/tests/test_model_geopol.py +4 -0
  56. synapse/tests/test_model_geospace.py +6 -0
  57. synapse/tests/test_model_inet.py +43 -5
  58. synapse/tests/test_model_infotech.py +10 -1
  59. synapse/tests/test_model_orgs.py +17 -2
  60. synapse/tests/test_model_person.py +23 -1
  61. synapse/tests/test_model_risk.py +13 -0
  62. synapse/tests/test_tools_healthcheck.py +4 -4
  63. synapse/tests/test_tools_storm.py +95 -0
  64. synapse/tests/test_utils.py +17 -18
  65. synapse/tests/test_utils_getrefs.py +1 -1
  66. synapse/tests/utils.py +0 -35
  67. synapse/tools/changelog.py +6 -4
  68. synapse/tools/storm.py +1 -1
  69. synapse/utils/getrefs.py +14 -3
  70. synapse/vendor/cpython/lib/http/__init__.py +0 -0
  71. synapse/vendor/cpython/lib/http/cookies.py +59 -0
  72. synapse/vendor/cpython/lib/test/test_http_cookies.py +49 -0
  73. {synapse-2.192.0.dist-info → synapse-2.194.0.dist-info}/METADATA +6 -6
  74. {synapse-2.192.0.dist-info → synapse-2.194.0.dist-info}/RECORD +77 -73
  75. {synapse-2.192.0.dist-info → synapse-2.194.0.dist-info}/WHEEL +1 -1
  76. {synapse-2.192.0.dist-info → synapse-2.194.0.dist-info}/LICENSE +0 -0
  77. {synapse-2.192.0.dist-info → synapse-2.194.0.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import asyncio
4
4
  import datetime
5
5
  import itertools
6
6
  import urllib.parse as u_parse
7
+ import unittest.mock as mock
7
8
 
8
9
  import synapse.exc as s_exc
9
10
  import synapse.common as s_common
@@ -52,10 +53,6 @@ class StormTest(s_t_utils.SynTest):
52
53
  with self.raises(s_exc.BadTypeValu):
53
54
  await core.nodes('[ ou:org=({"hq": "woot"}) ]')
54
55
 
55
- msgs = await core.stormlist('[ ou:org=({"hq": "woot", "$try": true}) ]')
56
- self.len(0, [m for m in msgs if m[0] == 'node'])
57
- self.stormIsInWarn('Bad value for prop hq: valu is not a guid', msgs)
58
-
59
56
  nodes05 = await core.nodes('[ ou:org=({"name": "vertex", "$props": {"motto": "for the people"}}) ]')
60
57
  self.len(1, nodes05)
61
58
  self.eq('vertex', nodes05[0].get('name'))
@@ -95,6 +92,57 @@ class StormTest(s_t_utils.SynTest):
95
92
  self.len(1, nodes12)
96
93
  self.ne(nodes11[0].ndef, nodes12[0].ndef)
97
94
 
95
+ # GUID ctor has a short-circuit where it tries to find an existing ndef before it does,
96
+ # some property deconfliction, and `<form>=({})` when pushed through guid generation gives
97
+ # back the same guid as `<form>=()`, which if we're not careful could lead to an
98
+ # inconsistent case where you fail to make a node because you don't provide any props,
99
+ # make a node with that matching ndef, and then run that invalid GUID ctor query again,
100
+ # and have it return back a node due to the short circuit. So test that we're consistent here.
101
+ with self.raises(s_exc.BadTypeValu):
102
+ await core.nodes('[ ou:org=({}) ]')
103
+
104
+ self.len(1, await core.nodes('[ ou:org=() ]'))
105
+
106
+ with self.raises(s_exc.BadTypeValu):
107
+ await core.nodes('[ ou:org=({}) ]')
108
+
109
+ msgs = await core.stormlist('[ ou:org=({"$props": {"desc": "lol"}})]')
110
+ self.len(0, [m for m in msgs if m[0] == 'node'])
111
+ self.stormIsInErr('No values provided for form ou:org', msgs)
112
+
113
+ msgs = await core.stormlist('[ou:org=({"name": "burrito corp", "$props": {"phone": "lolnope"}})]')
114
+ self.len(0, [m for m in msgs if m[0] == 'node'])
115
+ self.stormIsInErr('Bad value for prop ou:org:phone: requires a digit string', msgs)
116
+
117
+ with self.raises(s_exc.BadTypeValu):
118
+ await core.nodes('[ ou:org=({"$try": true}) ]')
119
+
120
+ # $try only affects $props
121
+ msgs = await core.stormlist('[ ou:org=({"founded": "lolnope", "$try": true}) ]')
122
+ self.len(0, [m for m in msgs if m[0] == 'node'])
123
+ self.stormIsInErr('Bad value for prop ou:org:founded: Unknown time format for lolnope', msgs)
124
+
125
+ msgs = await core.stormlist('[ou:org=({"name": "burrito corp", "$try": true, "$props": {"phone": "lolnope", "desc": "burritos man"}})]')
126
+ nodes = [m for m in msgs if m[0] == 'node']
127
+ self.len(1, nodes)
128
+ node = nodes[0][1]
129
+ props = node[1]['props']
130
+ self.none(props.get('phone'))
131
+ self.eq(props.get('name'), 'burrito corp')
132
+ self.eq(props.get('desc'), 'burritos man')
133
+ self.stormIsInWarn('Skipping bad value for prop ou:org:phone: requires a digit string', msgs)
134
+
135
+ await self.asyncraises(s_exc.BadTypeValu, core.addNode(core.auth.rootuser, 'ou:org', {'name': 'org name 77', 'phone': 'lolnope'}, props={'desc': 'an org desc'}))
136
+
137
+ await self.asyncraises(s_exc.BadTypeValu, core.addNode(core.auth.rootuser, 'ou:org', {'name': 'org name 77'}, props={'desc': 'an org desc', 'phone': 'lolnope'}))
138
+
139
+ node = await core.addNode(core.auth.rootuser, 'ou:org', {'$try': True, '$props': {'phone': 'invalid'}, 'name': 'org name 77'}, props={'desc': 'an org desc'})
140
+ self.nn(node)
141
+ props = node[1]['props']
142
+ self.none(props.get('phone'))
143
+ self.eq(props.get('name'), 'org name 77')
144
+ self.eq(props.get('desc'), 'an org desc')
145
+
98
146
  async def test_lib_storm_jsonexpr(self):
99
147
  async with self.getTestCore() as core:
100
148
 
@@ -253,7 +301,7 @@ class StormTest(s_t_utils.SynTest):
253
301
  emit bar
254
302
  }
255
303
  function makelist() {
256
- $retn = $lib.list()
304
+ $retn = ()
257
305
  for $item in $generate() { $retn.append($item) }
258
306
  return($retn)
259
307
  }
@@ -266,7 +314,7 @@ class StormTest(s_t_utils.SynTest):
266
314
  emit $node.repr()
267
315
  }
268
316
  function makelist() {
269
- $retn = $lib.list()
317
+ $retn = ()
270
318
  for $item in $generate() { $retn.append($item) }
271
319
  return($retn)
272
320
  }
@@ -338,9 +386,135 @@ class StormTest(s_t_utils.SynTest):
338
386
  prnt = [m[1]['mesg'] for m in msgs if m[0] == 'print']
339
387
  self.eq(prnt, ['inner 0', 'outer 0'])
340
388
 
341
- await self.asyncraises(s_exc.StormRuntimeError, core.nodes('emit foo'))
389
+ # Emit outside an emitter function raises a runtime error with posinfo
390
+ with self.raises(s_exc.StormRuntimeError) as cm:
391
+ await core.nodes('emit foo')
392
+ self.nn(cm.exception.get('highlight'))
342
393
 
343
- # include a quick test for using stop in a node yielder
394
+ with self.raises(s_exc.StormRuntimeError) as cm:
395
+ await core.nodes('[test:str=emit] emit foo')
396
+ self.nn(cm.exception.get('highlight'))
397
+
398
+ # stop cannot cross function boundaries
399
+ q = '''
400
+ function inner(v) {
401
+ if ( $v = 2 ) {
402
+ stop
403
+ }
404
+ return ( $v )
405
+ }
406
+ function outer(n) {
407
+ for $i in $lib.range($n) {
408
+ emit $inner($i)
409
+ }
410
+ }
411
+ $N = (5)
412
+ for $valu in $outer($N) {
413
+ $lib.print(`{$valu}/{$N}`)
414
+ }
415
+ '''
416
+ msgs = await core.stormlist(q)
417
+ self.stormIsInPrint('1/5', msgs)
418
+ self.stormNotInPrint('2/5', msgs)
419
+ self.stormIsInErr('function inner - Generator control statement "stop" used outside of a generator '
420
+ 'function.',
421
+ msgs)
422
+
423
+ # The function exception raised can be caught.
424
+ q = '''
425
+ function inner(v) {
426
+ if ( $v = 2 ) {
427
+ stop
428
+ }
429
+ return ( $v )
430
+ }
431
+ function outer(n) {
432
+ for $i in $lib.range($n) {
433
+ emit $inner($i)
434
+ }
435
+ }
436
+ $N = (5)
437
+ try {
438
+ for $valu in $outer($N) {
439
+ $lib.print(`{$valu}/{$N}`)
440
+ }
441
+ } catch StormRuntimeError as err {
442
+ $lib.print(`caught: {$err.mesg}`)
443
+ }
444
+ '''
445
+ msgs = await core.stormlist(q)
446
+ self.stormIsInPrint('1/5', msgs)
447
+ self.stormNotInPrint('2/5', msgs)
448
+ self.stormIsInPrint('caught: function inner - Generator control statement "stop" used outside of a'
449
+ ' generator function.',
450
+ msgs)
451
+
452
+ # Outside a function, StopStorm is caught and converted into a StormRuntimeError for the message stream.
453
+ # Since this is tearing down the runtime, it cannot be caught.
454
+ q = '''
455
+ $N = (5)
456
+ for $j in $lib.range($N) {
457
+ if ($j = 2) {
458
+ stop
459
+ }
460
+ $lib.print(`{$j}/{$N}`)
461
+ }
462
+ '''
463
+ msgs = await core.stormlist(q)
464
+ self.stormIsInPrint('1/5', msgs)
465
+ self.stormNotInPrint('2/5', msgs)
466
+ self.stormIsInErr('Generator control statement "stop" used outside of a generator function.',
467
+ msgs)
468
+ errname = [m[1][0] for m in msgs if m[0] == 'err'][0]
469
+ self.eq(errname, 'StormRuntimeError')
470
+
471
+ q = '''
472
+ $N = (5)
473
+ try {
474
+ for $j in $lib.range($N) {
475
+ if ($j = 2) {
476
+ stop
477
+ }
478
+ $lib.print(`{$j}/{$N}`)
479
+ }
480
+ } catch StormRuntimeError as err {
481
+ $lib.print(`caught: {$err.mesg}`)
482
+ }
483
+ '''
484
+ msgs = await core.stormlist(q)
485
+ self.stormIsInPrint('1/5', msgs)
486
+ self.stormNotInPrint('2/5', msgs)
487
+ self.stormNotInPrint('caught:', msgs)
488
+ self.stormIsInErr('Generator control statement "stop" used outside of a generator function.',
489
+ msgs)
490
+
491
+ # Mixing a Loop control flow statement in an emitter to stop its processing
492
+ # will be converted into a catchable StormRuntimeError
493
+ q = '''
494
+ function inner(n) {
495
+ emit $n
496
+ $n = ( $n + 1 )
497
+ emit $n
498
+ $n = ( $n + 1 )
499
+ if ( $n >= 2 ) {
500
+ break
501
+ }
502
+ emit $n
503
+ }
504
+ $N = (0)
505
+ try {
506
+ for $valu in $inner($N) {
507
+ $lib.print(`got {$valu}`)
508
+ }
509
+ } catch StormRuntimeError as err {
510
+ $lib.print(`caught: {$err.mesg}`)
511
+ }
512
+ '''
513
+ msgs = await core.stormlist(q)
514
+ self.stormIsInPrint('got 1', msgs)
515
+ self.stormNotInPrint('got 2', msgs)
516
+ self.stormIsInPrint('caught: function inner - Loop control statement "break" used outside of a loop.',
517
+ msgs)
344
518
 
345
519
  async def test_lib_storm_intersect(self):
346
520
  async with self.getTestCore() as core:
@@ -579,7 +753,7 @@ class StormTest(s_t_utils.SynTest):
579
753
  return((0))
580
754
  }
581
755
 
582
- $alerts = $lib.list()
756
+ $alerts = ()
583
757
  { $alerts.append($node.repr()) }
584
758
 
585
759
  $bool = $stuff($alerts)
@@ -646,32 +820,6 @@ class StormTest(s_t_utils.SynTest):
646
820
  self.none(task['info'].get('opts'))
647
821
  self.eq(core.view.iden, task['info'].get('view'))
648
822
 
649
- # test the parallel command
650
- nodes = await core.nodes('parallel --size 4 { [ ou:org=* ] }')
651
- self.len(4, nodes)
652
-
653
- # check that subquery validation happens
654
- with self.raises(s_exc.NoSuchVar):
655
- await core.nodes('parallel --size 4 { [ ou:org=$foo ] }')
656
-
657
- # check that an exception on inbound percolates correctly
658
- with self.raises(s_exc.BadTypeValu):
659
- await core.nodes('[ ou:org=* ou:org=foo ] | parallel { [:name=bar] }')
660
-
661
- # check that an exception in the parallel pipeline percolates correctly
662
- with self.raises(s_exc.BadTypeValu):
663
- await core.nodes('parallel { [ou:org=foo] }')
664
-
665
- nodes = await core.nodes('ou:org | parallel {[ :name=foo ]}')
666
- self.true(all([n.get('name') == 'foo' for n in nodes]))
667
-
668
- # Runtsafety test
669
- q = '[ inet:fqdn=www.vertex.link ] $q=:domain | parallel $q'
670
- await self.asyncraises(s_exc.StormRuntimeError, core.nodes(q))
671
-
672
- nodes = await core.nodes('ou:org | parallel ${ $foo=bar [ :name=$foo ]}')
673
- self.true(all([n.get('name') == 'bar' for n in nodes]))
674
-
675
823
  # test $lib.exit() and the StormExit handlers
676
824
  msgs = [m async for m in core.view.storm('$lib.exit()')]
677
825
  self.eq(msgs[-1][0], 'fini')
@@ -789,10 +937,10 @@ class StormTest(s_t_utils.SynTest):
789
937
  },
790
938
  )
791
939
  }
792
- await core.loadStormPkg(emptypkg)
940
+ core.loadStormPkg(emptypkg)
793
941
  await core.addStormPkg(strverpkg)
794
942
 
795
- await core.loadStormPkg(pkg0)
943
+ core.loadStormPkg(pkg0)
796
944
 
797
945
  await core.nodes('$lib.import(foo.baz)', opts=opts)
798
946
  await core.nodes('$lib.import(foo.baz, reqvers="==0.0.1")', opts=opts)
@@ -934,7 +1082,7 @@ class StormTest(s_t_utils.SynTest):
934
1082
 
935
1083
  opts = {'view': view}
936
1084
  self.len(0, await core.callStorm('''
937
- $list = $lib.list()
1085
+ $list = ()
938
1086
  $layr = $lib.view.get().layers.0
939
1087
  for $item in $layr.getStorNodes() {
940
1088
  $list.append($item)
@@ -947,7 +1095,7 @@ class StormTest(s_t_utils.SynTest):
947
1095
  await core.callStorm('inet:ipv4=11.22.33.44 [ +(blahverb)> { inet:asn=99 } ]', opts=opts)
948
1096
 
949
1097
  sodes = await core.callStorm('''
950
- $list = $lib.list()
1098
+ $list = ()
951
1099
  $layr = $lib.view.get().layers.0
952
1100
  for $item in $layr.getStorNodes() {
953
1101
  $list.append($item)
@@ -956,7 +1104,7 @@ class StormTest(s_t_utils.SynTest):
956
1104
  self.len(2, sodes)
957
1105
 
958
1106
  ipv4 = await core.callStorm('''
959
- $list = $lib.list()
1107
+ $list = ()
960
1108
  $layr = $lib.view.get().layers.0
961
1109
  for ($buid, $sode) in $layr.getStorNodes() {
962
1110
  yield $buid
@@ -1098,7 +1246,7 @@ class StormTest(s_t_utils.SynTest):
1098
1246
  self.len(0, await core.nodes('diff', opts=opts))
1099
1247
 
1100
1248
  self.len(0, await core.callStorm('''
1101
- $list = $lib.list()
1249
+ $list = ()
1102
1250
  for ($buid, $sode) in $lib.view.get().layers.0.getStorNodes() {
1103
1251
  $list.append($buid)
1104
1252
  }
@@ -1379,9 +1527,9 @@ class StormTest(s_t_utils.SynTest):
1379
1527
  '''))
1380
1528
 
1381
1529
  self.eq(('foo', 'bar', 'baz'), await core.callStorm('''
1382
- return($lib.list( // do foo thing
1383
- /* hehe */ foo /* hehe */ , /* hehe */ bar /* hehe */ , /* hehe */ baz /* hehe */
1384
- ))
1530
+ return(([ // do foo thing
1531
+ /* hehe */ "foo" /* hehe */ , /* hehe */ "bar" /* hehe */ , /* hehe */ "baz" /* hehe */
1532
+ ]))
1385
1533
  '''))
1386
1534
 
1387
1535
  # surrogate escapes are allowed
@@ -2594,6 +2742,7 @@ class StormTest(s_t_utils.SynTest):
2594
2742
  class PkgHandler(s_httpapi.Handler):
2595
2743
 
2596
2744
  async def get(self, name):
2745
+ assert self.request.headers.get('X-Synapse-Version') == s_version.verstring
2597
2746
 
2598
2747
  if name == 'notok':
2599
2748
  self.sendRestErr('FooBar', 'baz faz')
@@ -2603,6 +2752,8 @@ class StormTest(s_t_utils.SynTest):
2603
2752
 
2604
2753
  class PkgHandlerRaw(s_httpapi.Handler):
2605
2754
  async def get(self, name):
2755
+ assert self.request.headers.get('X-Synapse-Version') == s_version.verstring
2756
+
2606
2757
  self.set_header('Content-Type', 'application/json')
2607
2758
  return self.write(pkg)
2608
2759
 
@@ -3279,7 +3430,7 @@ class StormTest(s_t_utils.SynTest):
3279
3430
  self.eq(nodes[4].ndef, ('inet:ipv4', 0x01020304))
3280
3431
 
3281
3432
  # Queries can be a heavy list
3282
- q = '$list = $lib.list(${ -> * }, ${ <- * }, ${ -> edge:refs:n2 :n1 -> * }) inet:ipv4=1.2.3.4 | tee --join $list'
3433
+ q = '$list = ([${ -> * }, ${ <- * }, ${ -> edge:refs:n2 :n1 -> * }]) inet:ipv4=1.2.3.4 | tee --join $list'
3283
3434
  nodes = await core.nodes(q)
3284
3435
  self.len(5, nodes)
3285
3436
  self.eq(nodes[0].ndef, ('inet:asn', 0))
@@ -3289,22 +3440,22 @@ class StormTest(s_t_utils.SynTest):
3289
3440
  self.eq(nodes[4].ndef, ('inet:ipv4', 0x01020304))
3290
3441
 
3291
3442
  # A empty list of queries still works as an nop
3292
- q = '$list = $lib.list() | tee $list'
3443
+ q = '$list = () | tee $list'
3293
3444
  msgs = await core.stormlist(q)
3294
3445
  self.len(2, msgs)
3295
3446
  self.eq(('init', 'fini'), [m[0] for m in msgs])
3296
3447
 
3297
- q = 'inet:ipv4=1.2.3.4 $list = $lib.list() | tee --join $list'
3448
+ q = 'inet:ipv4=1.2.3.4 $list = () | tee --join $list'
3298
3449
  msgs = await core.stormlist(q)
3299
3450
  self.len(3, msgs)
3300
3451
  self.eq(('init', 'node', 'fini'), [m[0] for m in msgs])
3301
3452
 
3302
- q = '$list = $lib.list() | tee --parallel $list'
3453
+ q = '$list = () | tee --parallel $list'
3303
3454
  msgs = await core.stormlist(q)
3304
3455
  self.len(2, msgs)
3305
3456
  self.eq(('init', 'fini'), [m[0] for m in msgs])
3306
3457
 
3307
- q = 'inet:ipv4=1.2.3.4 $list = $lib.list() | tee --parallel --join $list'
3458
+ q = 'inet:ipv4=1.2.3.4 $list = () | tee --parallel --join $list'
3308
3459
  msgs = await core.stormlist(q)
3309
3460
  self.len(3, msgs)
3310
3461
  self.eq(('init', 'node', 'fini'), [m[0] for m in msgs])
@@ -3437,6 +3588,73 @@ class StormTest(s_t_utils.SynTest):
3437
3588
  q = '[ inet:fqdn=www.vertex.link ] $q=:domain | tee $q'
3438
3589
  await self.asyncraises(s_exc.StormRuntimeError, core.nodes(q))
3439
3590
 
3591
+ async def test_storm_parallel(self):
3592
+
3593
+ async with self.getTestCore() as core:
3594
+
3595
+ nodes = await core.nodes('parallel --size 4 { [ ou:org=* ] }')
3596
+ self.len(4, nodes)
3597
+
3598
+ # check that subquery validation happens
3599
+ with self.raises(s_exc.NoSuchVar):
3600
+ await core.nodes('parallel --size 4 { [ ou:org=$foo ] }')
3601
+
3602
+ # check that an exception on inbound percolates correctly
3603
+ with self.raises(s_exc.BadTypeValu):
3604
+ await core.nodes('[ ou:org=(foo,) ou:org=foo ] | parallel { [:name=bar] }')
3605
+
3606
+ with self.raises(s_exc.BadTypeValu):
3607
+ await core.nodes('[ ou:org=(foo,) ou:org=foo ] | parallel --size 1 { [:name=bar] }')
3608
+
3609
+ # check that an exception in the parallel pipeline percolates correctly
3610
+ with self.raises(s_exc.BadTypeValu):
3611
+ await core.nodes('parallel { [ou:org=foo] }')
3612
+
3613
+ nodes = await core.nodes('ou:org | parallel {[ :name=foo ]}')
3614
+ self.true(all([n.get('name') == 'foo' for n in nodes]))
3615
+
3616
+ # Runtsafety test
3617
+ q = '[ inet:fqdn=www.vertex.link ] $q=:domain | parallel $q'
3618
+ await self.asyncraises(s_exc.StormRuntimeError, core.nodes(q))
3619
+
3620
+ nodes = await core.nodes('ou:org | parallel ${ $foo=bar [ :name=$foo ]}')
3621
+ self.true(all([n.get('name') == 'bar' for n in nodes]))
3622
+
3623
+ orig = s_storm.ParallelCmd.pipeline
3624
+ tsks = {'cnt': 0}
3625
+
3626
+ async def pipecnt(self, runt, query, inq, outq):
3627
+ tsks['cnt'] += 1
3628
+ await orig(self, runt, query, inq, outq)
3629
+
3630
+ with mock.patch('synapse.lib.storm.ParallelCmd.pipeline', pipecnt):
3631
+
3632
+ nodes = await core.nodes('ou:org parallel --size 4 {[ :name=bar ]}')
3633
+ self.len(5, nodes)
3634
+ self.true(all([n.get('name') == 'bar' for n in nodes]))
3635
+ self.eq(4, tsks['cnt'])
3636
+
3637
+ tsks['cnt'] = 0
3638
+
3639
+ nodes = await core.nodes('ou:org parallel --size 5 {[ :name=bar ]}')
3640
+ self.len(5, nodes)
3641
+ self.true(all([n.get('name') == 'bar' for n in nodes]))
3642
+ self.eq(5, tsks['cnt'])
3643
+
3644
+ tsks['cnt'] = 0
3645
+
3646
+ # --size greater than number of nodes only creates a pipeline for each node
3647
+ nodes = await core.nodes('ou:org parallel --size 10 {[ :name=foo ]}')
3648
+ self.len(5, nodes)
3649
+ self.true(all([n.get('name') == 'foo' for n in nodes]))
3650
+ self.eq(5, tsks['cnt'])
3651
+
3652
+ tsks['cnt'] = 0
3653
+
3654
+ nodes = await core.nodes('parallel --size 4 {[ ou:org=* ]}')
3655
+ self.len(4, nodes)
3656
+ self.eq(4, tsks['cnt'])
3657
+
3440
3658
  async def test_storm_yieldvalu(self):
3441
3659
 
3442
3660
  async with self.getTestCore() as core:
@@ -3491,7 +3709,7 @@ class StormTest(s_t_utils.SynTest):
3491
3709
  fork = await core.callStorm('return( $lib.view.get().fork().iden )')
3492
3710
 
3493
3711
  q = '''
3494
- $nodes = $lib.list()
3712
+ $nodes = ()
3495
3713
  view.exec $view { inet:ipv4=1.2.3.4 $nodes.append($node) } |
3496
3714
  for $n in $nodes {
3497
3715
  yield $n
@@ -3501,7 +3719,7 @@ class StormTest(s_t_utils.SynTest):
3501
3719
  self.stormIsInErr('Node is not from the current view.', msgs)
3502
3720
 
3503
3721
  q = '''
3504
- $nodes = $lib.list()
3722
+ $nodes = ()
3505
3723
  view.exec $view { for $x in ${ inet:ipv4=1.2.3.4 } { $nodes.append($x) } } |
3506
3724
  for $n in $nodes {
3507
3725
  yield $n
@@ -3516,7 +3734,7 @@ class StormTest(s_t_utils.SynTest):
3516
3734
 
3517
3735
  # Nodes lifted from another view and referred to by iden() works
3518
3736
  q = '''
3519
- $nodes = $lib.list()
3737
+ $nodes = ()
3520
3738
  view.exec $view { inet:ipv4=1.2.3.4 $nodes.append($node) } |
3521
3739
  for $n in $nodes {
3522
3740
  yield $n.iden()
@@ -3526,7 +3744,7 @@ class StormTest(s_t_utils.SynTest):
3526
3744
  self.len(1, nodes)
3527
3745
 
3528
3746
  q = '''
3529
- $nodes = $lib.list()
3747
+ $nodes = ()
3530
3748
  view.exec $view { for $x in ${ inet:ipv4=1.2.3.4 } { $nodes.append($x) } } |
3531
3749
  for $n in $nodes {
3532
3750
  yield $n.iden()
@@ -3882,7 +4100,7 @@ class StormTest(s_t_utils.SynTest):
3882
4100
  )},
3883
4101
  ),
3884
4102
  }
3885
- await core.loadStormPkg(pdef)
4103
+ core.loadStormPkg(pdef)
3886
4104
  msgs = await core.stormlist('woot --help')
3887
4105
  helptext = '\n'.join([m[1].get('mesg') for m in msgs if m[0] == 'print'])
3888
4106
  self.isin('Inputs:\n\n hehe:haha\n hoho:lol - We know whats up', helptext)
@@ -4656,7 +4874,7 @@ class StormTest(s_t_utils.SynTest):
4656
4874
  async def test_storm_cmdscope(self):
4657
4875
 
4658
4876
  async with self.getTestCore() as core:
4659
- await core.loadStormPkg({
4877
+ core.loadStormPkg({
4660
4878
  'name': 'testpkg',
4661
4879
  'version': '0.0.1',
4662
4880
  'commands': (
@@ -0,0 +1,65 @@
1
+ import pickle
2
+
3
+ import synapse.lib.stormctrl as s_stormctrl
4
+
5
+ import synapse.tests.utils as s_t_utils
6
+
7
+ class StormctrlTest(s_t_utils.SynTest):
8
+ def test_basic(self):
9
+
10
+ # Classes inherit as expected
11
+ self.isinstance(s_stormctrl.StormReturn(), s_stormctrl.StormCtrlFlow)
12
+ self.isinstance(s_stormctrl.StormExit(), s_stormctrl.StormCtrlFlow)
13
+ self.isinstance(s_stormctrl.StormBreak(), s_stormctrl.StormCtrlFlow)
14
+ self.isinstance(s_stormctrl.StormContinue(), s_stormctrl.StormCtrlFlow)
15
+ self.isinstance(s_stormctrl.StormStop(), s_stormctrl.StormCtrlFlow)
16
+
17
+ # Subtypes are noted as well
18
+ self.isinstance(s_stormctrl.StormBreak(), s_stormctrl.StormLoopCtrl)
19
+ self.isinstance(s_stormctrl.StormContinue(), s_stormctrl.StormLoopCtrl)
20
+ self.isinstance(s_stormctrl.StormStop(), s_stormctrl.StormGenrCtrl)
21
+
22
+ # control flow and exist constructs inherit from the SynErrMixin
23
+ # return does not to keep it thin. it is used often.
24
+ self.isinstance(s_stormctrl.StormExit(), s_stormctrl._SynErrMixin)
25
+ self.isinstance(s_stormctrl.StormBreak(), s_stormctrl._SynErrMixin)
26
+ self.isinstance(s_stormctrl.StormContinue(), s_stormctrl._SynErrMixin)
27
+ self.isinstance(s_stormctrl.StormStop(), s_stormctrl._SynErrMixin)
28
+ self.false(isinstance(s_stormctrl.StormReturn(), s_stormctrl._SynErrMixin))
29
+
30
+ # The base class cannot be used on its own.
31
+ with self.raises(NotImplementedError):
32
+ s_stormctrl.StormCtrlFlow()
33
+
34
+ # The _SynErrMixin classes have several methods that let us treat
35
+ # instance of them like SynErr exceptions.
36
+ e = s_stormctrl.StormExit(mesg='words', foo='bar')
37
+ self.eq(e.get('foo'), 'bar')
38
+ self.eq(e.items(), {'mesg': 'words', 'foo': 'bar'})
39
+ self.eq("StormExit: foo='bar' mesg='words'", str(e))
40
+ e.set('hehe', 1234)
41
+ e.set('foo', 'words')
42
+ self.eq("StormExit: foo='words' hehe=1234 mesg='words'", str(e))
43
+
44
+ e.setdefault('defv', 1)
45
+ self.eq("StormExit: defv=1 foo='words' hehe=1234 mesg='words'", str(e))
46
+
47
+ e.setdefault('defv', 2)
48
+ self.eq("StormExit: defv=1 foo='words' hehe=1234 mesg='words'", str(e))
49
+
50
+ e.update({'foo': 'newwords', 'bar': 'baz'})
51
+ self.eq("StormExit: bar='baz' defv=1 foo='newwords' hehe=1234 mesg='words'", str(e))
52
+
53
+ # But it does not have an errname property
54
+ self.false(hasattr(e, 'errname'))
55
+
56
+ # StormReturn is used to move objects around.
57
+ e = s_stormctrl.StormReturn('weee')
58
+ self.eq(e.item, 'weee')
59
+
60
+ async def test_pickled_stormctrlflow(self):
61
+ e = s_stormctrl.StormExit(mesg='words', foo='bar')
62
+ buf = pickle.dumps(e)
63
+ new_e = pickle.loads(buf)
64
+ self.eq(new_e.get('foo'), 'bar')
65
+ self.eq("StormExit: foo='bar' mesg='words'", str(new_e))
@@ -518,11 +518,11 @@ class StormHttpTest(s_test.SynTest):
518
518
  self.eq(data.get('body'), 'MTIzNA==')
519
519
 
520
520
  q = '''
521
- $fields=$lib.list(
522
- ({"name": "foo", "value": "bar"}),
523
- ({"name": "foo", "value": "bar2"}),
524
- ({"name": "baz", "value": "cool"})
525
- )
521
+ $fields=([
522
+ {"name": "foo", "value": "bar"},
523
+ {"name": "foo", "value": "bar2"},
524
+ {"name": "baz", "value": "cool"}
525
+ ])
526
526
  $resp = $lib.inet.http.post($url, fields=$fields, ssl_verify=$lib.false)
527
527
  return ( $resp.json() )
528
528
  '''
@@ -427,7 +427,7 @@ class StormLibAuthTest(s_test.SynTest):
427
427
 
428
428
  await core.callStorm('$lib.user.json.set(hi, hehe, prop=foo)')
429
429
  items = await core.callStorm('''
430
- $list = $lib.list()
430
+ $list = ()
431
431
  for $item in $lib.user.json.iter() { $list.append($item) }
432
432
  return($list)
433
433
  ''')
@@ -437,7 +437,7 @@ class StormLibAuthTest(s_test.SynTest):
437
437
  ))
438
438
 
439
439
  items = await core.callStorm('''
440
- $list = $lib.list()
440
+ $list = ()
441
441
  for $item in $lib.user.json.iter(path=bye) { $list.append($item) }
442
442
  return($list)
443
443
  ''')
@@ -731,7 +731,7 @@ class StormLibAuthTest(s_test.SynTest):
731
731
  '''))
732
732
 
733
733
  # user roles can be set in bulk
734
- roles = await core.callStorm('''$roles=$lib.list()
734
+ roles = await core.callStorm('''$roles=()
735
735
  $role=$lib.auth.roles.byname(admins) $roles.append($role.iden)
736
736
  $role=$lib.auth.roles.byname(all) $roles.append($role.iden)
737
737
  $lib.auth.users.byname(visi).setRoles($roles)
@@ -791,7 +791,7 @@ class StormLibAuthTest(s_test.SynTest):
791
791
  visi = await core.callStorm('''
792
792
  $rule = $lib.auth.ruleFromText(hehe.haha)
793
793
  $visi = $lib.auth.users.byname(visi)
794
- $visi.setRules($lib.list($rule))
794
+ $visi.setRules(([$rule]))
795
795
  return($visi)
796
796
  ''')
797
797
  self.eq(((True, ('hehe', 'haha')),), visi['rules'])
@@ -834,7 +834,7 @@ class StormLibAuthTest(s_test.SynTest):
834
834
  ninjas = await core.callStorm('''
835
835
  $rule = $lib.auth.ruleFromText(hehe.haha)
836
836
  $ninjas = $lib.auth.roles.byname(ninjas)
837
- $ninjas.setRules($lib.list($rule))
837
+ $ninjas.setRules(([$rule]))
838
838
  return($ninjas)
839
839
  ''')
840
840
  self.eq(((True, ('hehe', 'haha')),), ninjas['rules'])