synapse 2.155.0__py311-none-any.whl → 2.156.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 (64) hide show
  1. synapse/cmds/cortex.py +2 -14
  2. synapse/common.py +1 -28
  3. synapse/cortex.py +10 -510
  4. synapse/lib/ast.py +60 -1
  5. synapse/lib/cell.py +33 -8
  6. synapse/lib/certdir.py +11 -0
  7. synapse/lib/cmdr.py +0 -5
  8. synapse/lib/gis.py +2 -2
  9. synapse/lib/httpapi.py +1 -43
  10. synapse/lib/layer.py +64 -201
  11. synapse/lib/lmdbslab.py +11 -0
  12. synapse/lib/node.py +1 -3
  13. synapse/lib/parser.py +10 -0
  14. synapse/lib/snap.py +121 -21
  15. synapse/lib/storm.lark +23 -6
  16. synapse/lib/storm.py +15 -338
  17. synapse/lib/storm_format.py +5 -0
  18. synapse/lib/stormlib/gen.py +1 -2
  19. synapse/lib/stormlib/gis.py +41 -0
  20. synapse/lib/stormlib/stats.py +21 -2
  21. synapse/lib/stormlib/storm.py +16 -1
  22. synapse/lib/stormtypes.py +225 -12
  23. synapse/lib/version.py +2 -2
  24. synapse/lib/view.py +96 -21
  25. synapse/models/inet.py +60 -30
  26. synapse/models/infotech.py +56 -1
  27. synapse/models/orgs.py +3 -0
  28. synapse/models/risk.py +15 -0
  29. synapse/models/syn.py +0 -38
  30. synapse/tests/test_cmds_cortex.py +1 -1
  31. synapse/tests/test_cortex.py +32 -336
  32. synapse/tests/test_lib_agenda.py +19 -54
  33. synapse/tests/test_lib_aha.py +97 -0
  34. synapse/tests/test_lib_ast.py +402 -0
  35. synapse/tests/test_lib_grammar.py +30 -10
  36. synapse/tests/test_lib_httpapi.py +0 -46
  37. synapse/tests/test_lib_layer.py +19 -234
  38. synapse/tests/test_lib_lmdbslab.py +22 -0
  39. synapse/tests/test_lib_snap.py +9 -0
  40. synapse/tests/test_lib_storm.py +16 -309
  41. synapse/tests/test_lib_stormlib_gis.py +21 -0
  42. synapse/tests/test_lib_stormlib_stats.py +107 -20
  43. synapse/tests/test_lib_stormlib_storm.py +25 -0
  44. synapse/tests/test_lib_stormtypes.py +231 -8
  45. synapse/tests/test_lib_view.py +6 -13
  46. synapse/tests/test_model_base.py +1 -1
  47. synapse/tests/test_model_inet.py +15 -0
  48. synapse/tests/test_model_infotech.py +60 -0
  49. synapse/tests/test_model_orgs.py +10 -0
  50. synapse/tests/test_model_person.py +0 -3
  51. synapse/tests/test_model_risk.py +20 -0
  52. synapse/tests/test_model_syn.py +20 -34
  53. synapse/tests/test_tools_csvtool.py +2 -1
  54. synapse/tests/test_tools_feed.py +4 -30
  55. synapse/tools/csvtool.py +2 -1
  56. {synapse-2.155.0.dist-info → synapse-2.156.0.dist-info}/METADATA +3 -3
  57. {synapse-2.155.0.dist-info → synapse-2.156.0.dist-info}/RECORD +60 -62
  58. {synapse-2.155.0.dist-info → synapse-2.156.0.dist-info}/WHEEL +1 -1
  59. synapse/cmds/cron.py +0 -726
  60. synapse/cmds/trigger.py +0 -319
  61. synapse/tests/test_cmds_cron.py +0 -453
  62. synapse/tests/test_cmds_trigger.py +0 -176
  63. {synapse-2.155.0.dist-info → synapse-2.156.0.dist-info}/LICENSE +0 -0
  64. {synapse-2.155.0.dist-info → synapse-2.156.0.dist-info}/top_level.txt +0 -0
@@ -496,51 +496,6 @@ class AgendaTest(s_t_utils.SynTest):
496
496
 
497
497
  self.len(0, [appt for (iden, appt) in appts if iden == badguid2])
498
498
 
499
- async def test_cron_perms(self):
500
-
501
- async with self.getTestCore() as core:
502
-
503
- visi = await core.auth.addUser('visi')
504
- newb = await core.auth.addUser('newb')
505
- async with core.getLocalProxy(user='visi') as proxy:
506
-
507
- cdef = {'storm': 'inet:ipv4', 'reqs': {'hour': 2}}
508
- with self.raises(s_exc.AuthDeny):
509
- await proxy.addCronJob(cdef)
510
-
511
- await visi.addRule((True, ('cron', 'add')))
512
- cron0 = await proxy.addCronJob(cdef)
513
- cron0_iden = cron0.get('iden')
514
-
515
- cdef = {'storm': 'inet:ipv6', 'reqs': {'hour': 2}}
516
- cron1 = await proxy.addCronJob(cdef)
517
- cron1_iden = cron1.get('iden')
518
-
519
- await proxy.delCronJob(cron0_iden)
520
-
521
- cdef = {'storm': '[test:str=foo]', 'reqs': {'now': True},
522
- 'incunit': 'month',
523
- 'incvals': 1}
524
- await self.asyncraises(s_exc.BadConfValu, proxy.addCronJob(cdef))
525
-
526
- async with core.getLocalProxy(user='newb') as proxy:
527
-
528
- with self.raises(s_exc.AuthDeny):
529
- await proxy.delCronJob(cron1_iden)
530
-
531
- self.eq(await proxy.listCronJobs(), ())
532
- await newb.addRule((True, ('cron', 'get')))
533
- self.len(1, await proxy.listCronJobs())
534
-
535
- with self.raises(s_exc.AuthDeny):
536
- await proxy.disableCronJob(cron1_iden)
537
-
538
- await newb.addRule((True, ('cron', 'set')))
539
- self.none(await proxy.disableCronJob(cron1_iden))
540
-
541
- await newb.addRule((True, ('cron', 'del')))
542
- await proxy.delCronJob(cron1_iden)
543
-
544
499
  async def test_agenda_stop(self):
545
500
 
546
501
  async with self.getTestCore() as core:
@@ -727,7 +682,8 @@ class AgendaTest(s_t_utils.SynTest):
727
682
 
728
683
  async with self.getTestCore() as core:
729
684
 
730
- derp = await core.auth.addUser('derp')
685
+ lowuser = await core.addUser('lowuser')
686
+ lowuser = lowuser.get('iden')
731
687
 
732
688
  msgs = await core.stormlist('cron.add --hourly 32 { $lib.print(woot) }')
733
689
  self.stormHasNoWarnErr(msgs)
@@ -735,14 +691,23 @@ class AgendaTest(s_t_utils.SynTest):
735
691
  cdef = await core.callStorm('for $cron in $lib.cron.list() { return($cron) }')
736
692
  self.eq(cdef['creator'], core.auth.rootuser.iden)
737
693
 
738
- opts = {'vars': {'derp': derp.iden}}
739
- cdef = await core.callStorm('for $cron in $lib.cron.list() { return($cron.set(creator, $derp)) }', opts=opts)
740
-
741
- self.eq(cdef['creator'], derp.iden)
742
-
743
- async with core.getLocalProxy(user='derp') as proxy:
744
- with self.raises(s_exc.AuthDeny):
745
- await proxy.editCronJob(cdef.get('iden'), 'creator', derp.iden)
694
+ opts = {'vars': {'lowuser': lowuser}}
695
+ cdef = await core.callStorm('for $cron in $lib.cron.list() { return($cron.set(creator, $lowuser)) }',
696
+ opts=opts)
697
+ self.eq(cdef['creator'], lowuser)
698
+
699
+ opts = {'user': lowuser, 'vars': {'iden': cdef.get('iden'), 'lowuser': lowuser}}
700
+ q = '$cron = $lib.cron.get($iden) return ( $cron.set(creator, $lowuser) )'
701
+ msgs = await core.stormlist(q, opts=opts)
702
+ # XXX FIXME - This is an odd message since the new creator does not implicitly have
703
+ # access to the cronjob that is running as them.
704
+ self.stormIsInErr('Provided iden does not match any valid authorized cron job.', msgs)
705
+
706
+ await core.addUserRule(lowuser, (True, ('cron', 'get')))
707
+ opts = {'user': lowuser, 'vars': {'iden': cdef.get('iden'), 'lowuser': lowuser}}
708
+ q = '$cron = $lib.cron.get($iden) return ( $cron.set(creator, $lowuser) )'
709
+ msgs = await core.stormlist(q, opts=opts)
710
+ self.stormIsInErr('must have permission cron.set.creator', msgs)
746
711
 
747
712
  async def test_agenda_fatal_run(self):
748
713
 
@@ -1115,3 +1115,100 @@ class AhaTest(s_test.SynTest):
1115
1115
  online = svcinfo.get('online')
1116
1116
  self.nn(online)
1117
1117
  self.true(ready)
1118
+
1119
+ async def test_aha_reprovision(self):
1120
+ with self.withNexusReplay() as stack:
1121
+ with self.getTestDir() as dirn:
1122
+ aha00dirn = s_common.gendir(dirn, 'aha00')
1123
+ aha01dirn = s_common.gendir(dirn, 'aha01')
1124
+ svc0dirn = s_common.gendir(dirn, 'svc00')
1125
+ svc1dirn = s_common.gendir(dirn, 'svc01')
1126
+ async with await s_base.Base.anit() as cm:
1127
+ aconf = {
1128
+ 'aha:name': 'aha',
1129
+ 'aha:network': 'loop.vertex.link',
1130
+ 'provision:listen': 'ssl://aha.loop.vertex.link:0'
1131
+ }
1132
+ name = aconf.get('aha:name')
1133
+ netw = aconf.get('aha:network')
1134
+ dnsname = f'{name}.{netw}'
1135
+
1136
+ aha = await s_aha.AhaCell.anit(aha00dirn, conf=aconf)
1137
+ await cm.enter_context(aha)
1138
+
1139
+ addr, port = aha.provdmon.addr
1140
+ # update the config to reflect the dynamically bound port
1141
+ aha.conf['provision:listen'] = f'ssl://{dnsname}:{port}'
1142
+
1143
+ # do this config ex-post-facto due to port binding...
1144
+ host, ahaport = await aha.dmon.listen(f'ssl://0.0.0.0:0?hostname={dnsname}&ca={netw}')
1145
+ aha.conf['aha:urls'] = (f'ssl://{dnsname}:{ahaport}',)
1146
+
1147
+ onetime = await aha.addAhaSvcProv('00.svc', provinfo=None)
1148
+ sconf = {'aha:provision': onetime}
1149
+ s_common.yamlsave(sconf, svc0dirn, 'cell.yaml')
1150
+ svc0 = await s_cell.Cell.anit(svc0dirn, conf=sconf)
1151
+ await cm.enter_context(svc0)
1152
+
1153
+ onetime = await aha.addAhaSvcProv('01.svc', provinfo={'mirror': 'svc'})
1154
+ sconf = {'aha:provision': onetime}
1155
+ s_common.yamlsave(sconf, svc1dirn, 'cell.yaml')
1156
+ svc1 = await s_cell.Cell.anit(svc1dirn, conf=sconf)
1157
+ await cm.enter_context(svc1)
1158
+
1159
+ # Ensure that services have connected
1160
+ await asyncio.wait_for(svc1.nexsroot._mirready.wait(), timeout=6)
1161
+ await svc1.sync()
1162
+
1163
+ # Get Aha services
1164
+ snfo = await aha.getAhaSvc('01.svc.loop.vertex.link')
1165
+ svcinfo = snfo.get('svcinfo')
1166
+ ready = svcinfo.get('ready')
1167
+ self.true(ready)
1168
+
1169
+ await aha.fini()
1170
+
1171
+ # Now re-deploy the AHA Service and re-provision the two cells
1172
+ # with the same AHA configuration
1173
+ async with await s_base.Base.anit() as cm:
1174
+ aconf = {
1175
+ 'aha:name': 'aha',
1176
+ 'aha:network': 'loop.vertex.link',
1177
+ 'provision:listen': 'ssl://aha.loop.vertex.link:0'
1178
+ }
1179
+ name = aconf.get('aha:name')
1180
+ netw = aconf.get('aha:network')
1181
+ dnsname = f'{name}.{netw}'
1182
+
1183
+ aha = await s_aha.AhaCell.anit(aha01dirn, conf=aconf)
1184
+ await cm.enter_context(aha)
1185
+
1186
+ addr, port = aha.provdmon.addr
1187
+ # update the config to reflect the dynamically bound port
1188
+ aha.conf['provision:listen'] = f'ssl://{dnsname}:{port}'
1189
+
1190
+ # do this config ex-post-facto due to port binding...
1191
+ host, ahaport = await aha.dmon.listen(f'ssl://0.0.0.0:0?hostname={dnsname}&ca={netw}')
1192
+ aha.conf['aha:urls'] = (f'ssl://{dnsname}:{ahaport}',)
1193
+
1194
+ onetime = await aha.addAhaSvcProv('00.svc', provinfo=None)
1195
+ sconf = {'aha:provision': onetime}
1196
+ s_common.yamlsave(sconf, svc0dirn, 'cell.yaml')
1197
+ svc0 = await s_cell.Cell.anit(svc0dirn, conf=sconf)
1198
+ await cm.enter_context(svc0)
1199
+
1200
+ onetime = await aha.addAhaSvcProv('01.svc', provinfo={'mirror': 'svc'})
1201
+ sconf = {'aha:provision': onetime}
1202
+ s_common.yamlsave(sconf, svc1dirn, 'cell.yaml')
1203
+ svc1 = await s_cell.Cell.anit(svc1dirn, conf=sconf)
1204
+ await cm.enter_context(svc1)
1205
+
1206
+ # Ensure that services have connected
1207
+ await asyncio.wait_for(svc1.nexsroot._mirready.wait(), timeout=6)
1208
+ await svc1.sync()
1209
+
1210
+ # Get Aha services
1211
+ snfo = await aha.getAhaSvc('01.svc.loop.vertex.link')
1212
+ svcinfo = snfo.get('svcinfo')
1213
+ ready = svcinfo.get('ready')
1214
+ self.true(ready)
@@ -656,6 +656,109 @@ class AstTest(s_test.SynTest):
656
656
  self.len(0, await core.nodes('[ inet:ipv4=1.2.3.4 ] :foo -> *'))
657
657
  self.len(0, await core.nodes('[ inet:ipv4=1.2.3.4 ] :asn -> inet:asn'))
658
658
 
659
+ async def test_ast_edge_walknjoin(self):
660
+
661
+ async with self.getTestCore() as core:
662
+
663
+ await core.nodes('[test:str=foo :hehe=bar +(foobar)> { [ test:str=baz ] }]')
664
+
665
+ nodes = await core.nodes('test:str=foo --+> *')
666
+ self.len(2, nodes)
667
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
668
+ self.eq(('test:str', 'baz'), nodes[1].ndef)
669
+
670
+ nodes = await core.nodes('test:str=baz <+-- *')
671
+ self.len(2, nodes)
672
+ self.eq(('test:str', 'baz'), nodes[0].ndef)
673
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
674
+
675
+ nodes = await core.nodes('test:str=foo -(foobar)+> *')
676
+ self.len(2, nodes)
677
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
678
+ self.eq(('test:str', 'baz'), nodes[1].ndef)
679
+
680
+ nodes = await core.nodes('test:str=baz <+(foobar)- *')
681
+ self.len(2, nodes)
682
+ self.eq(('test:str', 'baz'), nodes[0].ndef)
683
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
684
+
685
+ await core.nodes('test:str=foo [ +(coffeeone)> { [ test:str=arabica ] } ]')
686
+ await core.nodes('test:str=foo [ +(coffeetwo)> { [ test:str=robusta ] } ]')
687
+ await core.nodes('[ test:int=28 +(coffeethree)> { test:str=arabica } ]')
688
+
689
+ nodes = await core.nodes('test:str=foo -((coffeeone, coffeetwo))+> *')
690
+ self.len(3, nodes)
691
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
692
+ self.eq(('test:str', 'arabica'), nodes[1].ndef)
693
+ self.eq(('test:str', 'robusta'), nodes[2].ndef)
694
+
695
+ await core.nodes('[test:str=neato :hehe=haha +(stuff)> { [inet:ipv4=1.2.3.0/24] }]')
696
+ await core.nodes('[test:str=burrito :hehe=stuff <(stuff)+ { test:str=baz }]')
697
+ await core.nodes('test:str=neato [ <(other)+ { test:str=foo } ]')
698
+
699
+ nodes = await core.nodes('$edge=stuff test:str=neato -($edge)+> *')
700
+ self.len(257, nodes)
701
+ self.eq(('test:str', 'neato'), nodes[0].ndef)
702
+ for n in nodes[1:]:
703
+ self.eq('inet:ipv4', n.ndef[0])
704
+
705
+ nodes = await core.nodes('test:str=neato | tee { --+> * } { <+(other)- * }')
706
+ self.len(259, nodes)
707
+ self.eq(('test:str', 'neato'), nodes[0].ndef)
708
+ self.eq(('test:str', 'foo'), nodes[-1].ndef)
709
+ self.eq(('test:str', 'neato'), nodes[-2].ndef)
710
+
711
+ for n in nodes[1:257]:
712
+ self.eq('inet:ipv4', n.ndef[0])
713
+
714
+ await core.nodes('test:str=foo [ +(wat)> {[test:int=12]}]')
715
+
716
+ nodes = await core.nodes('test:str=foo -(other)+> test:str')
717
+ self.len(2, nodes)
718
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
719
+ self.eq(('test:str', 'neato'), nodes[1].ndef)
720
+
721
+ with self.raises(s_exc.BadSyntax):
722
+ await core.nodes('test:str=neato --+> test:str')
723
+
724
+ with self.raises(s_exc.BadSyntax):
725
+ await core.nodes('test:str <+-- test:str')
726
+
727
+ nodes = await core.nodes('test:str=foo -(*)+> test:str')
728
+ self.len(5, nodes)
729
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
730
+ ndefs = [n.ndef for n in nodes[1:]]
731
+ self.isin(('test:str', 'arabica'), ndefs)
732
+ self.isin(('test:str', 'robusta'), ndefs)
733
+ self.isin(('test:str', 'baz'), ndefs)
734
+ self.isin(('test:str', 'neato'), ndefs)
735
+ self.notin(('test:int', 12), ndefs)
736
+
737
+ nodes = await core.nodes('test:str=foo -(*)+> *')
738
+ self.len(6, nodes)
739
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
740
+ ndefs = [n.ndef for n in nodes[1:]]
741
+ self.isin(('test:int', 12), ndefs)
742
+
743
+ nodes = await core.nodes('test:str=arabica <+(*)- test:str')
744
+ self.len(2, nodes)
745
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
746
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
747
+
748
+ nodes = await core.nodes('test:str=arabica <+(*)- *')
749
+ self.len(3, nodes)
750
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
751
+ ndefs = [n.ndef for n in nodes[1:]]
752
+ self.isin(('test:str', 'foo'), ndefs)
753
+ self.isin(('test:int', 28), ndefs)
754
+
755
+ await core.nodes('test:str=arabica [ <(place)+ { [ test:str=coffeebar] } ]')
756
+ nodes = await core.nodes('test:str=arabica <+((place, coffeeone))- *')
757
+ self.len(3, nodes)
758
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
759
+ self.eq(('test:str', 'coffeebar'), nodes[1].ndef)
760
+ self.eq(('test:str', 'foo'), nodes[2].ndef)
761
+
659
762
  async def test_ast_lift_filt_array(self):
660
763
 
661
764
  async with self.getTestCore() as core:
@@ -1738,6 +1841,243 @@ class AstTest(s_test.SynTest):
1738
1841
  evnt = firs[0]
1739
1842
  self.eq(evnt[1].get('data'), {'total': 3})
1740
1843
 
1844
+ async def test_ast_emptyblock(self):
1845
+
1846
+ async with self.getTestCore() as core:
1847
+ q = '''
1848
+ empty {
1849
+ $lib.print("a fancy but empty block")
1850
+ }
1851
+ '''
1852
+ msgs = await core.stormlist(q)
1853
+ self.stormIsInPrint('a fancy but empty block', msgs)
1854
+
1855
+ q = '''
1856
+ empty {
1857
+ [test:str=neato]
1858
+ }
1859
+ [ :hehe=stuff ]
1860
+ '''
1861
+ msgs = await core.stormlist(q)
1862
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1863
+ self.len(1, nodes)
1864
+ props = nodes[0][1]['props']
1865
+ self.eq('stuff', props.get('hehe'))
1866
+
1867
+ q = '''
1868
+ empty {
1869
+ $lib.print("some empty block")
1870
+ }
1871
+ [test:str=synapse]
1872
+ '''
1873
+ msgs = await core.stormlist(q)
1874
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1875
+ self.len(1, nodes)
1876
+ self.stormIsInPrint('some empty block', msgs)
1877
+
1878
+ q = '''
1879
+ for $i in $lib.range(10) {
1880
+ if ($i > 5) {
1881
+ [test:int=$i]
1882
+ }
1883
+ } | empty { $lib.print(`count is {$i}`) }
1884
+ '''
1885
+ msgs = await core.stormlist(q)
1886
+ self.stormNotInPrint('count is', msgs)
1887
+
1888
+ q = '''
1889
+ for $i in $lib.range(10) {
1890
+ $lib.print(`count is {$i}`)
1891
+ } | empty { $lib.print(`pipeline is empty`) }
1892
+ '''
1893
+ msgs = await core.stormlist(q)
1894
+ self.stormIsInPrint('count is', msgs)
1895
+ self.stormIsInPrint('pipeline is empty', msgs)
1896
+
1897
+ q = '''
1898
+ [test:str=burrito]
1899
+ empty {
1900
+ [test:str=awesome]
1901
+ }
1902
+ '''
1903
+ msgs = await core.stormlist(q)
1904
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1905
+ self.len(1, nodes)
1906
+ self.eq(('test:str', 'burrito'), nodes[0][0])
1907
+
1908
+ q = '''
1909
+ $lib.print("OH YEA")
1910
+ empty {
1911
+ [test:str=possum]
1912
+ }
1913
+ '''
1914
+ msgs = await core.stormlist(q)
1915
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1916
+ self.len(1, nodes)
1917
+ self.eq(('test:str', 'possum'), nodes[0][0])
1918
+ self.stormIsInPrint('OH YEA', msgs)
1919
+
1920
+ q = '''
1921
+ empty {
1922
+ [test:str=foo]
1923
+ }
1924
+
1925
+ empty {
1926
+ [test:bstr=bar]
1927
+ }
1928
+ '''
1929
+ msgs = await core.stormlist(q)
1930
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1931
+ self.len(1, nodes)
1932
+ self.eq(('test:str', 'foo'), nodes[0][0])
1933
+
1934
+ q = '''
1935
+ empty {
1936
+ $lib.print('call me')
1937
+ }
1938
+
1939
+ $lib.print('ishmael')
1940
+
1941
+ empty {
1942
+ $lib.print('some years ago')
1943
+ }
1944
+
1945
+ [test:str="moby dick"]
1946
+
1947
+ empty {
1948
+ $lib.print('never mind')
1949
+ }
1950
+
1951
+ empty {
1952
+ $lib.print('how long')
1953
+ }
1954
+
1955
+ [ :hehe=haha ]
1956
+ '''
1957
+ msgs = await core.stormlist(q)
1958
+ self.stormIsInPrint('call me', msgs)
1959
+ self.stormIsInPrint('ishmael', msgs)
1960
+ self.stormIsInPrint('some years ago', msgs)
1961
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1962
+ self.len(1, nodes)
1963
+ self.eq(('test:str', 'moby dick'), nodes[0][0])
1964
+ self.eq('haha', nodes[0][1]['props']['hehe'])
1965
+ self.stormNotInPrint('never mind', msgs)
1966
+ self.stormNotInPrint('how long', msgs)
1967
+
1968
+ q = '''
1969
+ function foo(x) {
1970
+ empty {
1971
+ $lib.print($x)
1972
+ }
1973
+
1974
+ return()
1975
+ }
1976
+
1977
+ [test:str=biz :hehe=baz]
1978
+ $foo(:hehe)
1979
+ '''
1980
+ msgs = await core.stormlist(q)
1981
+ self.stormIsInPrint("baz", msgs)
1982
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1983
+ self.len(1, nodes)
1984
+ self.eq(('test:str', 'biz'), nodes[0][0])
1985
+
1986
+ q = '''
1987
+ [test:str=coffee :hehe=pourover] $beep=:hehe | spin | empty { $lib.print("blorp") }
1988
+ '''
1989
+ msgs = await core.stormlist(q)
1990
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1991
+ self.len(0, nodes)
1992
+ self.stormIsInPrint('blorp', msgs)
1993
+
1994
+ q = '''
1995
+ [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) }
1996
+ '''
1997
+ msgs = await core.stormlist(q)
1998
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1999
+ self.len(0, nodes)
2000
+ self.stormIsInErr('Empty block query must be runtsafe', msgs)
2001
+
2002
+ q = '''
2003
+ function foo() {
2004
+ for $x in $lib.range(10) {
2005
+ emit $x
2006
+ }
2007
+ }
2008
+
2009
+ for $data in $foo() {
2010
+ if ($data > 10000) {
2011
+ [test:int=$data]
2012
+ }
2013
+ }
2014
+
2015
+ empty {
2016
+ [test:int=1000]
2017
+ }
2018
+ '''
2019
+ msgs = await core.stormlist(q)
2020
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2021
+ self.len(1, nodes)
2022
+ self.eq(('test:int', 1000), nodes[0][0])
2023
+
2024
+ q = '''
2025
+ empty {
2026
+ [test:int=12345]
2027
+ }
2028
+ '''
2029
+ idens = [nodes[0][1]['iden'],]
2030
+ msgs = await core.stormlist(q, opts={'idens': idens})
2031
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2032
+ self.len(1, nodes)
2033
+ self.eq(('test:int', 1000), nodes[0][0])
2034
+
2035
+ q = '''
2036
+ function foo() {
2037
+ empty {
2038
+ $lib.print('foobarbaz')
2039
+ }
2040
+ [test:int=12]
2041
+ }
2042
+
2043
+ yield $foo()
2044
+ empty {
2045
+ $lib.print('neato')
2046
+ }
2047
+ '''
2048
+ msgs = await core.stormlist(q)
2049
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2050
+ self.len(1, nodes)
2051
+ self.eq(('test:int', 12), nodes[0][0])
2052
+ self.stormIsInPrint('foobarbaz', msgs)
2053
+ self.stormNotInPrint('neato', msgs)
2054
+
2055
+ q = '''
2056
+ function foo() {
2057
+ for $x in $lib.range(2) {
2058
+ emit $x
2059
+ empty {
2060
+ $lib.print(`count is {$x}`)
2061
+ }
2062
+ }
2063
+ }
2064
+ for $x in $foo() {
2065
+ [test:int=$x]
2066
+ }
2067
+ '''
2068
+ msgs = await core.stormlist(q)
2069
+ order = [m[0] for m in msgs]
2070
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2071
+ ndefs = [n[0] for n in nodes]
2072
+
2073
+ self.len(2, nodes)
2074
+ self.eq(order, ['init', 'node:edits', 'node', 'print', 'node:edits', 'node', 'print', 'fini'])
2075
+ self.isin(('test:int', 0), ndefs)
2076
+ self.isin(('test:int', 1), ndefs)
2077
+
2078
+ self.stormIsInPrint('count is 0', msgs)
2079
+ self.stormIsInPrint('count is 1', msgs)
2080
+
1741
2081
  async def test_ast_cmdargs(self):
1742
2082
 
1743
2083
  async with self.getTestCore() as core:
@@ -2778,8 +3118,11 @@ class AstTest(s_test.SynTest):
2778
3118
  self.len(2, await core.nodes('test:str +#taga*:score'))
2779
3119
  self.len(1, await core.nodes('test:str +#tagaa:score=5'))
2780
3120
  self.len(1, await core.nodes('test:str +#tagaa:score<(2+4)'))
3121
+ self.len(0, await core.nodes('test:str +#tagaa:score<-5'))
2781
3122
  self.len(1, await core.nodes('test:str +#tagaa:score*range=(4,6)'))
3123
+ self.len(0, await core.nodes('test:str +#taga*:score <- *'))
2782
3124
  self.len(1, await core.nodes('test:str +#taga*:score <(*)- *'))
3125
+ self.len(3, await core.nodes('test:str +#taga*:score <+(*)- *'))
2783
3126
  self.len(2, await core.nodes('$tag=taga* test:str +#$tag:score'))
2784
3127
  self.len(1, await core.nodes('$tag=tagaa test:str +#$tag:score=5'))
2785
3128
  self.len(1, await core.nodes('$tag=tagaa test:str +#$tag:score*range=(4,6)'))
@@ -2850,3 +3193,62 @@ class AstTest(s_test.SynTest):
2850
3193
  nodes = await core.nodes('test:type10 $foobar=:int2 +(:intprop = $foobar)')
2851
3194
  self.len(1, nodes)
2852
3195
  self.eq(nodes[0].ndef, ('test:type10', 'one'))
3196
+
3197
+ async def test_ast_propvalue(self):
3198
+ async with self.getTestCore() as core:
3199
+
3200
+ # Create node with data prop, assign data prop to var, update var
3201
+ q = '[ it:exec:query=(test1,) :opts=({"foo": "bar"}) ] $opts=:opts $opts.bar = "baz"'
3202
+ nodes = await core.nodes(q)
3203
+ self.len(1, nodes)
3204
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar'})
3205
+
3206
+ q = '[ it:exec:query=(test1,) :opts=({"foo": "bar"}) ] $opts=:opts $opts.bar = "baz" [ :opts=$opts ]'
3207
+ nodes = await core.nodes(q)
3208
+ self.len(1, nodes)
3209
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar', 'bar': 'baz'})
3210
+
3211
+ q = '''
3212
+ '''
3213
+ msgs = await core.stormlist('[ it:exec:query=(test2,) :opts=({"foo": "bar"}) ]')
3214
+ self.stormHasNoWarnErr(msgs)
3215
+
3216
+ # Lift node with data prop, assign data prop to var, update var
3217
+ q = 'it:exec:query=(test2,) $opts=:opts $opts.bar = "baz"'
3218
+ nodes = await core.nodes(q)
3219
+ self.len(1, nodes)
3220
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar'})
3221
+
3222
+ q = 'it:exec:query=(test2,) $opts=:opts $opts.bar = "baz" [ :opts=$opts ]'
3223
+ nodes = await core.nodes(q)
3224
+ self.len(1, nodes)
3225
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar', 'bar': 'baz'})
3226
+
3227
+ # Create node for the lift below
3228
+ q = '''
3229
+ [ it:app:snort:hit=*
3230
+ :flow={[ inet:flow=* :raw=({"foo": "bar"}) ]}
3231
+ ]
3232
+ '''
3233
+ nodes = await core.nodes(q)
3234
+ self.len(1, nodes)
3235
+
3236
+ # Lift node, get prop via implicit pivot, assign data prop to var, update var
3237
+ q = f'it:app:snort:hit $raw = :flow::raw $raw.baz="box" | spin | inet:flow'
3238
+ nodes = await core.nodes(q)
3239
+ self.len(1, nodes)
3240
+ self.eq(nodes[0].props.get('raw'), {'foo': 'bar'})
3241
+
3242
+ q = f'it:app:snort:hit $raw = :flow::raw $raw.baz="box" | spin | inet:flow [ :raw=$raw ]'
3243
+ nodes = await core.nodes(q)
3244
+ self.len(1, nodes)
3245
+ self.eq(nodes[0].props.get('raw'), {'foo': 'bar', 'baz': 'box'})
3246
+
3247
+ async def test_ast_subq_runtsafety(self):
3248
+
3249
+ async with self.getTestCore() as core:
3250
+ msgs = await core.stormlist('$foo={[test:str=foo] return($node.value())} $lib.print($foo)')
3251
+ self.stormIsInPrint('foo', msgs)
3252
+
3253
+ msgs = await core.stormlist('$lib.print({[test:str=foo] return($node.value())})')
3254
+ self.stormIsInPrint('foo', msgs)