synapse 2.165.0__py311-none-any.whl → 2.167.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/axon.py +4 -10
  2. synapse/cmds/cortex.py +1 -6
  3. synapse/common.py +6 -0
  4. synapse/cortex.py +104 -57
  5. synapse/datamodel.py +32 -0
  6. synapse/exc.py +1 -0
  7. synapse/lib/agenda.py +81 -51
  8. synapse/lib/aha.py +2 -0
  9. synapse/lib/ast.py +21 -23
  10. synapse/lib/base.py +11 -10
  11. synapse/lib/cell.py +24 -24
  12. synapse/lib/hive.py +11 -0
  13. synapse/lib/httpapi.py +1 -0
  14. synapse/lib/nexus.py +3 -2
  15. synapse/lib/node.py +4 -2
  16. synapse/lib/schemas.py +3 -1
  17. synapse/lib/snap.py +50 -0
  18. synapse/lib/storm.py +19 -17
  19. synapse/lib/stormlib/aha.py +370 -17
  20. synapse/lib/stormlib/auth.py +11 -4
  21. synapse/lib/stormlib/cache.py +202 -0
  22. synapse/lib/stormlib/cortex.py +69 -7
  23. synapse/lib/stormlib/macro.py +11 -18
  24. synapse/lib/stormlib/spooled.py +109 -0
  25. synapse/lib/stormlib/stix.py +1 -1
  26. synapse/lib/stormtypes.py +61 -17
  27. synapse/lib/trigger.py +10 -12
  28. synapse/lib/types.py +3 -1
  29. synapse/lib/version.py +2 -2
  30. synapse/lib/view.py +16 -3
  31. synapse/models/base.py +8 -0
  32. synapse/models/files.py +3 -0
  33. synapse/models/inet.py +74 -2
  34. synapse/models/orgs.py +52 -8
  35. synapse/models/person.py +30 -11
  36. synapse/models/risk.py +44 -3
  37. synapse/telepath.py +115 -32
  38. synapse/tests/files/stormpkg/dotstorm/dotstorm.yaml +3 -0
  39. synapse/tests/test_cortex.py +79 -8
  40. synapse/tests/test_datamodel.py +22 -0
  41. synapse/tests/test_lib_agenda.py +8 -1
  42. synapse/tests/test_lib_aha.py +19 -6
  43. synapse/tests/test_lib_cell.py +6 -2
  44. synapse/tests/test_lib_grammar.py +62 -64
  45. synapse/tests/test_lib_httpapi.py +1 -1
  46. synapse/tests/test_lib_rstorm.py +4 -4
  47. synapse/tests/test_lib_storm.py +98 -7
  48. synapse/tests/test_lib_stormlib_aha.py +196 -0
  49. synapse/tests/test_lib_stormlib_cache.py +272 -0
  50. synapse/tests/test_lib_stormlib_compression.py +12 -12
  51. synapse/tests/test_lib_stormlib_cortex.py +71 -0
  52. synapse/tests/test_lib_stormlib_macro.py +94 -0
  53. synapse/tests/test_lib_stormlib_spooled.py +190 -0
  54. synapse/tests/test_lib_stormtypes.py +71 -37
  55. synapse/tests/test_lib_view.py +50 -3
  56. synapse/tests/test_model_files.py +3 -0
  57. synapse/tests/test_model_inet.py +67 -0
  58. synapse/tests/test_model_risk.py +6 -0
  59. synapse/tests/test_telepath.py +30 -7
  60. synapse/tests/test_tools_genpkg.py +26 -0
  61. synapse/tests/test_tools_hiveload.py +1 -0
  62. synapse/tests/test_tools_hivesave.py +1 -0
  63. synapse/tests/test_tools_modrole.py +81 -0
  64. synapse/tests/test_tools_moduser.py +105 -0
  65. synapse/tests/utils.py +22 -3
  66. synapse/tools/autodoc.py +1 -1
  67. synapse/tools/hive/load.py +3 -0
  68. synapse/tools/hive/save.py +3 -0
  69. synapse/tools/modrole.py +59 -7
  70. synapse/tools/moduser.py +78 -10
  71. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/METADATA +3 -3
  72. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/RECORD +75 -72
  73. synapse/lib/provenance.py +0 -111
  74. synapse/tests/test_lib_provenance.py +0 -37
  75. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/LICENSE +0 -0
  76. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/WHEEL +0 -0
  77. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/top_level.txt +0 -0
@@ -53,6 +53,27 @@ class MacroTest(s_test.SynTest):
53
53
  with self.raises(s_exc.AuthDeny):
54
54
  await core.nodes('$lib.macro.del(hehe)', opts=asvisi)
55
55
 
56
+ with self.raises(s_exc.BadArg):
57
+ await core.nodes('$lib.macro.set("", ${ inet:ipv4 })')
58
+
59
+ with self.raises(s_exc.BadArg):
60
+ await core.nodes('$lib.macro.get("")')
61
+
62
+ with self.raises(s_exc.BadArg):
63
+ await core.nodes('$lib.macro.del("")')
64
+
65
+ with self.raises(s_exc.BadArg):
66
+ await core.delStormMacro('', user=None)
67
+
68
+ with self.raises(s_exc.BadArg):
69
+ await core.nodes('$lib.macro.grant("", users, hehe, 3)')
70
+
71
+ with self.raises(s_exc.SchemaViolation):
72
+ await core.nodes('$lib.macro.mod("hehe", ({"name": ""}))')
73
+
74
+ with self.raises(s_exc.BadArg):
75
+ await core.nodes('$lib.macro.mod("", ({"name": "foobar"}))')
76
+
56
77
  with self.raises(s_exc.AuthDeny):
57
78
  await core.nodes('$lib.macro.set(hehe, ${ inet:ipv6 })', opts=asvisi)
58
79
 
@@ -118,6 +139,14 @@ class MacroTest(s_test.SynTest):
118
139
  msgs = await core.stormlist('macro.del print', opts={'readonly': True})
119
140
  self.stormIsInErr('not marked readonly safe', msgs)
120
141
 
142
+ msgs = await core.stormlist('macro.set blorpblorp "+#foo"', opts=asvisi)
143
+ self.stormHasNoWarnErr(msgs)
144
+
145
+ await core.auth.delUser(visi.iden)
146
+ msgs = await core.stormlist('macro.list')
147
+ self.stormIsInPrint("User not found", msgs)
148
+ self.stormIsInPrint(visi.iden, msgs)
149
+
121
150
  async def test_stormlib_macro_vars(self):
122
151
 
123
152
  async with self.getTestCore() as core:
@@ -319,3 +348,68 @@ class MacroTest(s_test.SynTest):
319
348
  await visi.addRule((True, ('storm', 'macro', 'admin')))
320
349
  msgs = await core.stormlist('macro.del asdf', opts={'user': visi.iden})
321
350
  self.stormHasNoWarnErr(msgs)
351
+
352
+ async def test_stormlib_behold_macro(self):
353
+ self.skipIfNexusReplay()
354
+ async with self.getTestCore() as core:
355
+ host, port = await core.addHttpsPort(0, host='127.0.0.1')
356
+
357
+ visi = await core.auth.addUser('visi')
358
+ await visi.setPasswd('secret')
359
+ await visi.setAdmin(True)
360
+
361
+ async with self.getHttpSess() as sess:
362
+ async with sess.post(f'https://localhost:{port}/api/v1/login', json={'user': 'visi', 'passwd': 'secret'}) as resp:
363
+ retn = await resp.json()
364
+ self.eq('ok', retn.get('status'))
365
+ self.eq('visi', retn['result']['name'])
366
+
367
+ async with sess.ws_connect(f'wss://localhost:{port}/api/v1/behold') as sock:
368
+ await sock.send_json({'type': 'call:init'})
369
+ mesg = await sock.receive_json()
370
+ self.eq(mesg['type'], 'init')
371
+
372
+ await core.callStorm('''
373
+ $lib.macro.set('foobar', ${ file:bytes | [+#neato] })
374
+ $lib.macro.set('foobar', ${ inet:ipv4 | [+#burrito] })
375
+ $lib.macro.mod('foobar', ({'name': 'bizbaz'}))
376
+ $lib.macro.grant('bizbaz', users, $visi, 3)
377
+ $lib.macro.del('bizbaz')
378
+ ''', opts={'vars': {'visi': visi.iden}})
379
+
380
+ addmesg = await sock.receive_json()
381
+ self.eq('storm:macro:add', addmesg['data']['event'])
382
+ macro = addmesg['data']['info']['macro']
383
+ self.eq(macro['name'], 'foobar')
384
+ self.eq(macro['storm'], 'file:bytes | [+#neato]')
385
+ self.ne(visi.iden, macro['user'])
386
+ self.ne(visi.iden, macro['creator'])
387
+ self.nn(macro['iden'])
388
+
389
+ setmesg = await sock.receive_json()
390
+ self.eq('storm:macro:mod', setmesg['data']['event'])
391
+ event = setmesg['data']['info']
392
+ self.nn(event['macro'])
393
+ self.eq(event['info']['storm'], 'inet:ipv4 | [+#burrito]')
394
+ self.nn(event['info']['updated'])
395
+
396
+ modmesg = await sock.receive_json()
397
+ self.eq('storm:macro:mod', modmesg['data']['event'])
398
+ event = modmesg['data']['info']
399
+ self.nn(event['macro'])
400
+ self.eq(event['info']['name'], 'bizbaz')
401
+ self.nn(event['info']['updated'])
402
+
403
+ grantmesg = await sock.receive_json()
404
+ self.eq('storm:macro:set:perm', grantmesg['data']['event'])
405
+ event = grantmesg['data']['info']
406
+ self.nn(event['macro'])
407
+ self.eq(event['info']['level'], 3)
408
+ self.eq(event['info']['scope'], 'users')
409
+ self.eq(event['info']['iden'], visi.iden)
410
+
411
+ delmesg = await sock.receive_json()
412
+ self.eq('storm:macro:del', delmesg['data']['event'])
413
+ event = delmesg['data']['info']
414
+ self.nn(event['iden'])
415
+ self.eq(event['name'], 'bizbaz')
@@ -0,0 +1,190 @@
1
+ import synapse.exc as s_exc
2
+ import synapse.lib.stormtypes as s_stormtypes
3
+
4
+ import synapse.tests.utils as s_test
5
+
6
+ class StormlibSpooledTest(s_test.SynTest):
7
+ async def test_lib_spooled_set(self):
8
+ async with self.getTestCore() as core:
9
+ await core.nodes('[inet:ipv4=1.2.3.4 :asn=20]')
10
+ await core.nodes('[inet:ipv4=5.6.7.8 :asn=30]')
11
+
12
+ q = '''
13
+ $set = $lib.spooled.set()
14
+ $set.add(1, 2, 3, 4)
15
+ return($set)
16
+ '''
17
+ valu = await core.callStorm(q)
18
+ self.eq({'1', '2', '3', '4'}, valu)
19
+
20
+ q = '''
21
+ $set = $lib.spooled.set()
22
+ $set.adds($lib.list(1, 2, 3, 4))
23
+ return($set)
24
+ '''
25
+ valu = await core.callStorm(q)
26
+ self.eq({'1', '2', '3', '4'}, valu)
27
+
28
+ q = '''
29
+ $set = $lib.spooled.set()
30
+ inet:ipv4 $set.add(:asn)
31
+ $set.rems((:asn,:asn))
32
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$set.list() ]
33
+ '''
34
+ nodes = await core.nodes(q)
35
+ self.len(1, nodes)
36
+ self.eq(nodes[0].get('data'), ())
37
+
38
+ q = '''
39
+ $set = $lib.spooled.set()
40
+ $set.add($foo)
41
+ $set.add($bar)
42
+ $set.add($biz)
43
+ return(($set.has($foo), $set.has(lolnop)))
44
+ '''
45
+ valu = await core.callStorm(q, opts={'vars': {'foo': b'foo', 'bar': b'bar', 'biz': b'biz'}})
46
+ self.eq(True, valu[0])
47
+ self.eq(False, valu[1])
48
+
49
+ q = '''
50
+ $set = $lib.spooled.set()
51
+ $set.adds(('foo', 'bar', 'baz', 'biz', 'biz', 'biz', 'beep', 'boop'))
52
+ return($set.size())
53
+ '''
54
+ valu = await core.callStorm(q)
55
+ self.eq(6, valu)
56
+
57
+ q = '''
58
+ $set = $lib.spooled.set()
59
+ $set.adds(('foo', 'bar', 'baz', 'biz', 'biz', 'biz', 'beep', 'boop'))
60
+
61
+ $set.rems(('baz', 'beep', 'bar'))
62
+ return($set)
63
+
64
+ '''
65
+ valu = await core.callStorm(q)
66
+ self.eq({'foo', 'boop', 'biz'}, valu)
67
+
68
+ q = '''
69
+ $set = $lib.spooled.set(1, 2, 3, 4 ,5)
70
+ $set.add(1, 2, 3, 4)
71
+ return($set.list())
72
+ '''
73
+ valu = await core.callStorm(q)
74
+ self.isinstance(valu, tuple)
75
+ self.len(5, valu)
76
+ self.isin('1', valu)
77
+ self.isin('2', valu)
78
+ self.isin('3', valu)
79
+ self.isin('4', valu)
80
+ self.isin('5', valu)
81
+
82
+ q = '''
83
+ $set = $lib.spooled.set()
84
+ $set.add($lib.true)
85
+ $set.add($lib.false)
86
+ $set.add($lib.true)
87
+ $set.add($lib.false)
88
+ $set.add('more stuff')
89
+
90
+ $dict = ({
91
+ "foo": "bar",
92
+ "biz": "baz",
93
+ })
94
+ $set.adds($dict)
95
+ return($set)
96
+ '''
97
+ valu = await core.callStorm(q)
98
+ self.len(5, valu)
99
+ self.isin(False, valu)
100
+ self.isin(True, valu)
101
+ self.isin('more stuff', valu)
102
+ self.isin(('biz', 'baz'), valu)
103
+ self.isin(('foo', 'bar'), valu)
104
+
105
+ q = '''
106
+ $set = $lib.spooled.set()
107
+ $set.adds($items)
108
+ for $x in $set {
109
+ $lib.print(`{$x} exists in the set`)
110
+ }
111
+ return()
112
+ '''
113
+ msgs = await core.stormlist(q, opts={'vars': {'items': [True, 'neato', False, 9001]}})
114
+ self.len(7, msgs)
115
+ self.stormIsInPrint('false exists in the set', msgs)
116
+ self.stormIsInPrint('true exists in the set', msgs)
117
+ self.stormIsInPrint('neato exists in the set', msgs)
118
+ self.stormIsInPrint('9001 exists in the set', msgs)
119
+
120
+ q = '''
121
+ $set = $lib.spooled.set(neato, neato, neato, neato)
122
+ $lib.print(`The set is {$set}`)
123
+ '''
124
+ msgs = await core.stormlist(q, opts={'vars': {'items': [True, 'neato', False, 9001]}})
125
+ self.stormIsInPrint("The set is {'neato'}", msgs)
126
+
127
+ # force a fallback
128
+ q = '''
129
+ $set = $lib.spooled.set()
130
+ $set.adds($lib.range(1500))
131
+ return($set.size())
132
+ '''
133
+ valu = await core.callStorm(q)
134
+ self.eq(1500, valu)
135
+
136
+ # sad paths
137
+ # too complex
138
+ q = '''
139
+ $set = $lib.spooled.set()
140
+ $set.add($stormnode)
141
+ return($set)
142
+ '''
143
+ stormnode = s_stormtypes.Node(nodes[0])
144
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
145
+
146
+ # mutable failures
147
+ q = '''
148
+ $set = $lib.spooled.set()
149
+ $set.add(({'neato': 'burrito'}))
150
+ return($set)
151
+ '''
152
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q))
153
+
154
+ q = '''
155
+ $set = $lib.spooled.set()
156
+ $dict = ({'neato': 'burrito'})
157
+ $set.adds(($dict, $dict))
158
+ return($set)
159
+ '''
160
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q))
161
+
162
+ q = '''
163
+ $set = $lib.spooled.set()
164
+ $form = $lib.model.form('inet:ipv4')
165
+ $set.adds(($stormnode, $form, $form))
166
+ return($set)
167
+ '''
168
+ # it'll blow up on the first
169
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
170
+
171
+ q = '''
172
+ $dict = ({'foo': 'bar'})
173
+ $set = $lib.spooled.set($dict)
174
+ return($set)
175
+ '''
176
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q))
177
+
178
+ # type not msgpack-able
179
+ q = '''
180
+ $set = $lib.spooled.set()
181
+ $set.add($lib.model.form("inet:ipv4"))
182
+ return($set)
183
+ '''
184
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q))
185
+
186
+ q = '''
187
+ $set = $lib.spooled.set($stormnode)
188
+ return($set)
189
+ '''
190
+ await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
@@ -20,7 +20,6 @@ import synapse.lib.storm as s_storm
20
20
  import synapse.lib.hashset as s_hashset
21
21
  import synapse.lib.httpapi as s_httpapi
22
22
  import synapse.lib.modelrev as s_modelrev
23
- import synapse.lib.provenance as s_provenance
24
23
  import synapse.lib.stormtypes as s_stormtypes
25
24
 
26
25
  import synapse.tests.utils as s_test
@@ -1344,34 +1343,34 @@ class StormTypesTest(s_test.SynTest):
1344
1343
  n2 = s_common.guid()
1345
1344
  n3 = s_common.guid()
1346
1345
 
1347
- nodes = await core.nodes('[graph:node="*" :data=$data]', opts={'vars': {'data': ghstr}})
1346
+ nodes = await core.nodes('[tel:mob:telem="*" :data=$data]', opts={'vars': {'data': ghstr}})
1348
1347
  self.len(1, nodes)
1349
1348
  node1 = nodes[0]
1350
1349
 
1351
- nodes = await core.nodes('[graph:node="*" :data=$data]', opts={'vars': {'data': mstr}})
1350
+ nodes = await core.nodes('[tel:mob:telem="*" :data=$data]', opts={'vars': {'data': mstr}})
1352
1351
  self.len(1, nodes)
1353
1352
  node2 = nodes[0]
1354
- q = '''graph:node=$n1 $gzthing = :data
1353
+ q = '''tel:mob:telem=$n1 $gzthing = :data
1355
1354
  $foo = $lib.base64.decode($gzthing).gunzip()
1356
1355
  $lib.print($foo)
1357
- [( graph:node=$n2 :data=$foo.decode() )]'''
1356
+ [( tel:mob:telem=$n2 :data=$foo.decode() )]'''
1358
1357
 
1359
1358
  msgs = await core.stormlist(q, opts={'vars': {'n1': node1.ndef[1], 'n2': n2}})
1360
1359
  self.stormHasNoWarnErr(msgs)
1361
1360
  self.stormIsInPrint('ohhai', msgs)
1362
1361
 
1363
1362
  # make sure we gunzip correctly
1364
- nodes = await core.nodes('graph:node=$valu', opts={'vars': {'valu': n2}})
1363
+ nodes = await core.nodes('tel:mob:telem=$valu', opts={'vars': {'valu': n2}})
1365
1364
  self.len(1, nodes)
1366
1365
  self.eq(hstr, nodes[0].get('data'))
1367
1366
 
1368
- text = f'''graph:node=$n2 $bar = :data
1369
- [( graph:node=$n3 :data=$lib.base64.encode($bar.encode().gzip()) )]'''
1367
+ text = f'''tel:mob:telem=$n2 $bar = :data
1368
+ [( tel:mob:telem=$n3 :data=$lib.base64.encode($bar.encode().gzip()) )]'''
1370
1369
  msgs = await core.stormlist(text, opts={'vars': {'n2': node2.ndef[1], 'n3': n3}})
1371
1370
  self.stormHasNoWarnErr(msgs)
1372
1371
 
1373
1372
  # make sure we gzip correctly
1374
- nodes = await core.nodes('graph:node=$valu', opts={'vars': {'valu': n3}})
1373
+ nodes = await core.nodes('tel:mob:telem=$valu', opts={'vars': {'valu': n3}})
1375
1374
  self.len(1, nodes)
1376
1375
  self.eq(mstr.encode(), gzip.decompress(base64.urlsafe_b64decode(nodes[0].props['data'])))
1377
1376
 
@@ -1384,30 +1383,30 @@ class StormTypesTest(s_test.SynTest):
1384
1383
  n2 = s_common.guid()
1385
1384
  n3 = s_common.guid()
1386
1385
 
1387
- nodes = await core.nodes('[graph:node="*" :data=$data]', opts={'vars': {'data': ghstr}})
1386
+ nodes = await core.nodes('[tel:mob:telem="*" :data=$data]', opts={'vars': {'data': ghstr}})
1388
1387
  self.len(1, nodes)
1389
1388
  node1 = nodes[0]
1390
- nodes = await core.nodes('[graph:node="*" :data=$data]', opts={'vars': {'data': mstr}})
1389
+ nodes = await core.nodes('[tel:mob:telem="*" :data=$data]', opts={'vars': {'data': mstr}})
1391
1390
  self.len(1, nodes)
1392
1391
  node2 = nodes[0]
1393
1392
 
1394
- q = '''graph:node=$valu $bzthing = :data $foo = $lib.base64.decode($bzthing).bunzip()
1393
+ q = '''tel:mob:telem=$valu $bzthing = :data $foo = $lib.base64.decode($bzthing).bunzip()
1395
1394
  $lib.print($foo)
1396
- [( graph:node=$n2 :data=$foo.decode() )] -graph:node=$valu'''
1395
+ [( tel:mob:telem=$n2 :data=$foo.decode() )] -tel:mob:telem=$valu'''
1397
1396
  msgs = await core.stormlist(q, opts={'vars': {'valu': node1.ndef[1], 'n2': n2}})
1398
1397
  self.stormHasNoWarnErr(msgs)
1399
1398
  self.stormIsInPrint('ohhai', msgs)
1400
1399
 
1401
1400
  # make sure we bunzip correctly
1402
1401
  opts = {'vars': {'iden': n2}}
1403
- nodes = await core.nodes('graph:node=$iden', opts=opts)
1402
+ nodes = await core.nodes('tel:mob:telem=$iden', opts=opts)
1404
1403
  self.len(1, nodes)
1405
1404
  node = nodes[0]
1406
1405
  self.eq(node.get('data'), hstr)
1407
1406
 
1408
1407
  # bzip
1409
- q = '''graph:node=$valu $bar = :data
1410
- [( graph:node=$n3 :data=$lib.base64.encode($bar.encode().bzip()) )] -graph:node=$valu'''
1408
+ q = '''tel:mob:telem=$valu $bar = :data
1409
+ [( tel:mob:telem=$n3 :data=$lib.base64.encode($bar.encode().bzip()) )] -tel:mob:telem=$valu'''
1411
1410
  nodes = await core.nodes(q, opts={'vars': {'valu': node2.ndef[1], 'n3': n3}})
1412
1411
  self.len(1, nodes)
1413
1412
  node = nodes[0]
@@ -1421,13 +1420,13 @@ class StormTypesTest(s_test.SynTest):
1421
1420
  valu = s_common.guid()
1422
1421
  n2 = s_common.guid()
1423
1422
 
1424
- nodes = await core.nodes('[graph:node=$valu :data=$data]', opts={'vars': {'valu': valu, 'data': ghstr}})
1423
+ nodes = await core.nodes('[tel:mob:telem=$valu :data=$data]', opts={'vars': {'valu': valu, 'data': ghstr}})
1425
1424
  self.len(1, nodes)
1426
1425
  node1 = nodes[0]
1427
1426
  self.eq(node1.get('data'), ghstr)
1428
1427
 
1429
- q = '''graph:node=$valu $jzthing=:data $foo=$jzthing.encode().json() [(graph:node=$n2 :data=$foo)]
1430
- -graph:node=$valu'''
1428
+ q = '''tel:mob:telem=$valu $jzthing=:data $foo=$jzthing.encode().json() [(tel:mob:telem=$n2 :data=$foo)]
1429
+ -tel:mob:telem=$valu'''
1431
1430
  nodes = await core.nodes(q, opts={'vars': {'valu': valu, 'n2': n2}})
1432
1431
  self.len(1, nodes)
1433
1432
  node2 = nodes[0]
@@ -1721,7 +1720,7 @@ class StormTypesTest(s_test.SynTest):
1721
1720
  q = '''
1722
1721
  $set = $lib.set()
1723
1722
  inet:ipv4 $set.add(:asn)
1724
- [ graph:node="*" ] +graph:node [ :data=$set.list() ]
1723
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$set.list() ]
1725
1724
  '''
1726
1725
  nodes = await core.nodes(q)
1727
1726
  self.len(1, nodes)
@@ -1730,7 +1729,7 @@ class StormTypesTest(s_test.SynTest):
1730
1729
  q = '''
1731
1730
  $set = $lib.set()
1732
1731
  inet:ipv4 $set.adds((:asn,:asn))
1733
- [ graph:node="*" ] +graph:node [ :data=$set.list() ]
1732
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$set.list() ]
1734
1733
  '''
1735
1734
  nodes = await core.nodes(q)
1736
1735
  self.len(1, nodes)
@@ -1740,7 +1739,7 @@ class StormTypesTest(s_test.SynTest):
1740
1739
  $set = $lib.set()
1741
1740
  inet:ipv4 $set.adds((:asn,:asn))
1742
1741
  { +:asn=20 $set.rem(:asn) }
1743
- [ graph:node="*" ] +graph:node [ :data=$set.list() ]
1742
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$set.list() ]
1744
1743
  '''
1745
1744
  nodes = await core.nodes(q)
1746
1745
  self.len(1, nodes)
@@ -1750,7 +1749,7 @@ class StormTypesTest(s_test.SynTest):
1750
1749
  $set = $lib.set()
1751
1750
  inet:ipv4 $set.add(:asn)
1752
1751
  $set.rems((:asn,:asn))
1753
- [ graph:node="*" ] +graph:node [ :data=$set.list() ]
1752
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$set.list() ]
1754
1753
  '''
1755
1754
  nodes = await core.nodes(q)
1756
1755
  self.len(1, nodes)
@@ -2152,7 +2151,7 @@ class StormTypesTest(s_test.SynTest):
2152
2151
  q = '''
2153
2152
  inet:fqdn=vertex.link -> inet:dns:a -> inet:ipv4
2154
2153
  $idens = $path.idens()
2155
- [ graph:node="*" ] +graph:node [ :data=$idens ]
2154
+ [ tel:mob:telem="*" ] +tel:mob:telem [ :data=$idens ]
2156
2155
  '''
2157
2156
 
2158
2157
  idens = (
@@ -2700,6 +2699,17 @@ class StormTypesTest(s_test.SynTest):
2700
2699
  await core.addUserRule(user, (True, ('storm', 'lib', 'telepath', 'open', 'cell')))
2701
2700
  self.len(2, await core.callStorm('return ( $lib.telepath.open($url).ipv4s() )', opts=opts))
2702
2701
 
2702
+ # SynErr exceptions are allowed through. They can be caught by storm.
2703
+ with self.raises(s_exc.BadUrl):
2704
+ await core.callStorm('$prox=$lib.telepath.open("weeeeeeeeeeeeee")')
2705
+
2706
+ # Python exceptions are caught and raised as StormRuntimeError exceptions.
2707
+ with self.raises(s_exc.StormRuntimeError) as cm:
2708
+ await core.callStorm('$prox=$lib.telepath.open("tcp://0.0.0.0:60000")')
2709
+ emsg = cm.exception.get('mesg')
2710
+ self.isin('Failed to connect to Telepath service: "tcp://0.0.0.0:60000" error:', emsg)
2711
+ self.isin('Connect call failed', emsg)
2712
+
2703
2713
  async def test_storm_lib_queue(self):
2704
2714
 
2705
2715
  async with self.getTestCore() as core:
@@ -4475,7 +4485,7 @@ class StormTypesTest(s_test.SynTest):
4475
4485
 
4476
4486
  async with self.getTestCore() as core:
4477
4487
 
4478
- cdef = await core.callStorm('return($lib.cron.add(query="{[graph:node=*]}", hourly=30).pack())')
4488
+ cdef = await core.callStorm('return($lib.cron.add(query="{[tel:mob:telem=*]}", hourly=30).pack())')
4479
4489
  self.eq('', cdef.get('doc'))
4480
4490
  self.eq('', cdef.get('name'))
4481
4491
 
@@ -4491,7 +4501,7 @@ class StormTypesTest(s_test.SynTest):
4491
4501
  with self.raises(s_exc.BadArg):
4492
4502
  await core.callStorm('return($lib.cron.get($iden).set(hehe, haha))', opts=opts)
4493
4503
 
4494
- mesgs = await core.stormlist('cron.add --hour +1 {[graph:node=*]} --name myname --doc mydoc')
4504
+ mesgs = await core.stormlist('cron.add --hour +1 {[tel:mob:telem=*]} --name myname --doc mydoc')
4495
4505
  for mesg in mesgs:
4496
4506
  if mesg[0] == 'print':
4497
4507
  iden0 = mesg[1]['mesg'].split(' ')[-1]
@@ -4512,7 +4522,6 @@ class StormTypesTest(s_test.SynTest):
4512
4522
 
4513
4523
  MONO_DELT = 1543827303.0
4514
4524
  unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp()
4515
- s_provenance.reset()
4516
4525
 
4517
4526
  def timetime():
4518
4527
  return unixtime
@@ -4597,7 +4606,7 @@ class StormTypesTest(s_test.SynTest):
4597
4606
  nextlayroffs = await layr.getEditOffs() + 1
4598
4607
 
4599
4608
  # Start simple: add a cron job that creates a node every minute
4600
- q = "cron.add --minute +1 {[graph:node='*' :type=m1]}"
4609
+ q = "cron.add --minute +1 {[meta:note='*' :type=m1]}"
4601
4610
  mesgs = await core.stormlist(q)
4602
4611
  self.stormIsInPrint('Created cron job', mesgs)
4603
4612
  for mesg in mesgs:
@@ -4639,24 +4648,24 @@ class StormTypesTest(s_test.SynTest):
4639
4648
 
4640
4649
  # Make sure it ran
4641
4650
  await layr.waitEditOffs(nextlayroffs, timeout=5)
4642
- self.eq(1, await prox.count('graph:node:type=m1'))
4651
+ self.eq(1, await prox.count('meta:note:type=m1'))
4643
4652
 
4644
- q = "cron.mod $guid { [graph:node='*' :type=m2] }"
4653
+ q = "cron.mod $guid { [meta:note='*' :type=m2] }"
4645
4654
  mesgs = await core.stormlist(q, opts={'vars': {'guid': guid[:6]}})
4646
4655
  self.stormIsInPrint(f'Modified cron job: {guid}', mesgs)
4647
4656
 
4648
- q = "cron.mod xxx { [graph:node='*' :type=m2] }"
4657
+ q = "cron.mod xxx { [meta:note='*' :type=m2] }"
4649
4658
  mesgs = await core.stormlist(q)
4650
4659
  self.stormIsInErr('does not match', mesgs)
4651
4660
 
4652
4661
  # Make sure the old one didn't run and the new query ran
4653
4662
  unixtime += 60
4654
4663
  await asyncio.sleep(0)
4655
- self.eq(1, await prox.count('graph:node:type=m1'))
4664
+ self.eq(1, await prox.count('meta:note:type=m1'))
4656
4665
  # UNG WTF
4657
4666
  await asyncio.sleep(0)
4658
4667
  await asyncio.sleep(0)
4659
- self.eq(1, await prox.count('graph:node:type=m2'))
4668
+ self.eq(1, await prox.count('meta:note:type=m2'))
4660
4669
 
4661
4670
  # Delete the job
4662
4671
  q = f"cron.del {guid}"
@@ -4669,8 +4678,8 @@ class StormTypesTest(s_test.SynTest):
4669
4678
 
4670
4679
  # Make sure deleted job didn't run
4671
4680
  unixtime += 60
4672
- self.eq(1, await prox.count('graph:node:type=m1'))
4673
- self.eq(1, await prox.count('graph:node:type=m2'))
4681
+ self.eq(1, await prox.count('meta:note:type=m1'))
4682
+ self.eq(1, await prox.count('meta:note:type=m2'))
4674
4683
 
4675
4684
  # Test fixed minute, i.e. every hour at 17 past
4676
4685
  unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10,
@@ -4846,6 +4855,7 @@ class StormTypesTest(s_test.SynTest):
4846
4855
 
4847
4856
  self.stormIsInPrint('last result: finished successfully with 0 nodes', mesgs)
4848
4857
  self.stormIsInPrint('entries: <None>', mesgs)
4858
+ self.stormIsInPrint('pool: false', mesgs)
4849
4859
 
4850
4860
  # Test 'stat' command
4851
4861
  mesgs = await core.stormlist('cron.stat xxx')
@@ -4919,7 +4929,7 @@ class StormTypesTest(s_test.SynTest):
4919
4929
  self.stormIsInErr('data.iden must match pattern', msgs)
4920
4930
 
4921
4931
  opts = {'vars': {'iden': 'cd263bd133a5dafa1e1c5e9a01d9d486'}}
4922
- q = "cron.add --iden $iden --day +1 --minute 14 {[test:guid=$lib.guid()]}"
4932
+ q = "cron.add --pool --iden $iden --day +1 --minute 14 {[test:guid=$lib.guid()]}"
4923
4933
  msgs = await core.stormlist(q, opts=opts)
4924
4934
  self.stormIsInPrint('Created cron job: cd263bd133a5dafa1e1c5e9a01d9d486', msgs)
4925
4935
 
@@ -6465,6 +6475,10 @@ words\tword\twrd'''
6465
6475
  self.eq(merge['creator'], core.auth.rootuser.iden)
6466
6476
  self.none(merge.get('updated'))
6467
6477
 
6478
+ merging = 'return($lib.view.get().getMergingViews()) '
6479
+
6480
+ self.eq([fork00], await core.callStorm(merging))
6481
+
6468
6482
  with self.raises(s_exc.AuthDeny):
6469
6483
  core.getView(fork00).reqValidVoter(root.iden)
6470
6484
 
@@ -6540,8 +6554,13 @@ words\tword\twrd'''
6540
6554
  self.none(core.getView(fork00))
6541
6555
  nodes = await core.nodes('inet:fqdn')
6542
6556
  self.len(2, nodes)
6557
+
6558
+ # previously successful merges
6543
6559
  self.len(1, await core.callStorm('$list = ([]) for $merge in $lib.view.get().getMerges() { $list.append($merge) } fini { return($list) }'))
6544
6560
 
6561
+ # current open merge requests
6562
+ self.eq([], await core.callStorm(merging))
6563
+
6545
6564
  # test out the delMergeRequest logic / cleanup
6546
6565
  forkdef = await core.getView().fork()
6547
6566
 
@@ -6549,6 +6568,7 @@ words\tword\twrd'''
6549
6568
 
6550
6569
  opts = {'view': fork.iden}
6551
6570
  self.nn(await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts))
6571
+ self.eq([fork.iden], await core.callStorm(merging))
6552
6572
 
6553
6573
  # confirm that you may not re-parent to a view with a merge request
6554
6574
  layr = await core.addLayer()
@@ -6564,6 +6584,8 @@ words\tword\twrd'''
6564
6584
  self.nn(await core.callStorm('return($lib.view.get().delMergeRequest())', opts=opts))
6565
6585
  self.len(0, [vote async for vote in fork.getMergeVotes()])
6566
6586
 
6587
+ self.eq([], await core.callStorm(merging))
6588
+
6567
6589
  # test coverage for beholder progress events...
6568
6590
  forkdef = await core.getView().fork()
6569
6591
  fork = core.getView(forkdef.get('iden'))
@@ -6572,10 +6594,12 @@ words\tword\twrd'''
6572
6594
  await core.stormlist('[ inet:ipv4=1.2.3.0/20 ]', opts=opts)
6573
6595
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6574
6596
 
6597
+ self.eq([fork.iden], await core.callStorm(merging))
6598
+
6575
6599
  nevents = 8
6576
6600
  if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
6577
6601
  # view:merge:vote:set fires twice
6578
- nevents = nevents + 1
6602
+ nevents += 1
6579
6603
  waiter = core.waiter(nevents, 'cell:beholder')
6580
6604
 
6581
6605
  opts = {'view': fork.iden, 'user': visi.iden}
@@ -6598,6 +6622,8 @@ words\tword\twrd'''
6598
6622
  self.eq(msgs[-1][1]['info']['merge']['creator'], core.auth.rootuser.iden)
6599
6623
  self.eq(msgs[-1][1]['info']['votes'][0]['user'], visi.iden)
6600
6624
 
6625
+ self.eq([], await core.callStorm(merging))
6626
+
6601
6627
  # test coverage for bad state for merge request
6602
6628
  fork00 = await core.getView().fork()
6603
6629
  fork01 = await core.getView(fork00['iden']).fork()
@@ -6606,8 +6632,11 @@ words\tword\twrd'''
6606
6632
  with self.raises(s_exc.BadState):
6607
6633
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6608
6634
 
6635
+ self.eq([], await core.callStorm(merging))
6636
+
6609
6637
  await core.delView(fork01['iden'])
6610
6638
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6639
+ self.eq([fork00['iden']], await core.callStorm(merging))
6611
6640
 
6612
6641
  core.getView().layers[0].readonly = True
6613
6642
  with self.raises(s_exc.BadState):
@@ -6626,6 +6655,8 @@ words\tword\twrd'''
6626
6655
  await core.stormlist('[ inet:ipv4=5.5.5.5 ]', opts=opts)
6627
6656
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6628
6657
 
6658
+ self.eq(set([fork.iden, fork00['iden']]), set(await core.callStorm(merging)))
6659
+
6629
6660
  # hamstring the runViewMerge method on the new view
6630
6661
  async def fake():
6631
6662
  return
@@ -6660,3 +6691,6 @@ words\tword\twrd'''
6660
6691
 
6661
6692
  msgs = await core.stormlist('$lib.view.get().set(quorum, $lib.null)')
6662
6693
  self.stormHasNoWarnErr(msgs)
6694
+
6695
+ with self.raises(s_exc.BadState):
6696
+ await core.callStorm(merging)