synapse 2.180.1__py311-none-any.whl → 2.182.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 (90) hide show
  1. synapse/assets/__init__.py +35 -0
  2. synapse/assets/storm/migrations/model-0.2.28.storm +355 -0
  3. synapse/common.py +2 -1
  4. synapse/cortex.py +49 -35
  5. synapse/cryotank.py +1 -1
  6. synapse/datamodel.py +30 -0
  7. synapse/lib/ast.py +12 -7
  8. synapse/lib/auth.py +17 -0
  9. synapse/lib/cell.py +7 -9
  10. synapse/lib/chop.py +0 -1
  11. synapse/lib/drive.py +8 -8
  12. synapse/lib/layer.py +55 -13
  13. synapse/lib/lmdbslab.py +26 -5
  14. synapse/lib/modelrev.py +28 -1
  15. synapse/lib/modules.py +1 -0
  16. synapse/lib/nexus.py +1 -1
  17. synapse/lib/node.py +5 -0
  18. synapse/lib/parser.py +23 -16
  19. synapse/lib/scrape.py +1 -1
  20. synapse/lib/slabseqn.py +2 -2
  21. synapse/lib/snap.py +129 -0
  22. synapse/lib/storm.lark +16 -2
  23. synapse/lib/storm.py +20 -3
  24. synapse/lib/storm_format.py +1 -0
  25. synapse/lib/stormhttp.py +34 -1
  26. synapse/lib/stormlib/auth.py +5 -3
  27. synapse/lib/stormlib/cortex.py +5 -2
  28. synapse/lib/stormlib/easyperm.py +2 -2
  29. synapse/lib/stormlib/ipv6.py +2 -2
  30. synapse/lib/stormlib/model.py +114 -12
  31. synapse/lib/stormlib/project.py +1 -1
  32. synapse/lib/stormtypes.py +81 -7
  33. synapse/lib/types.py +7 -0
  34. synapse/lib/version.py +2 -2
  35. synapse/lib/view.py +47 -0
  36. synapse/models/inet.py +10 -3
  37. synapse/models/infotech.py +2 -1
  38. synapse/models/language.py +4 -0
  39. synapse/models/math.py +50 -0
  40. synapse/models/orgs.py +8 -0
  41. synapse/models/risk.py +9 -0
  42. synapse/tests/files/stormcov/pragma-nocov.storm +18 -0
  43. synapse/tests/test_assets.py +25 -0
  44. synapse/tests/test_cortex.py +129 -0
  45. synapse/tests/test_datamodel.py +6 -0
  46. synapse/tests/test_lib_cell.py +12 -0
  47. synapse/tests/test_lib_grammar.py +7 -1
  48. synapse/tests/test_lib_layer.py +35 -0
  49. synapse/tests/test_lib_lmdbslab.py +11 -9
  50. synapse/tests/test_lib_modelrev.py +655 -1
  51. synapse/tests/test_lib_slabseqn.py +5 -4
  52. synapse/tests/test_lib_snap.py +4 -0
  53. synapse/tests/test_lib_storm.py +110 -1
  54. synapse/tests/test_lib_stormhttp.py +99 -1
  55. synapse/tests/test_lib_stormlib_auth.py +15 -0
  56. synapse/tests/test_lib_stormlib_cortex.py +21 -4
  57. synapse/tests/test_lib_stormlib_iters.py +8 -5
  58. synapse/tests/test_lib_stormlib_model.py +45 -6
  59. synapse/tests/test_lib_stormtypes.py +158 -2
  60. synapse/tests/test_lib_types.py +6 -0
  61. synapse/tests/test_model_inet.py +10 -0
  62. synapse/tests/test_model_language.py +4 -0
  63. synapse/tests/test_model_math.py +22 -0
  64. synapse/tests/test_model_orgs.py +6 -2
  65. synapse/tests/test_model_risk.py +4 -0
  66. synapse/tests/test_tools_storm.py +1 -1
  67. synapse/tests/test_utils_stormcov.py +5 -0
  68. synapse/tests/utils.py +18 -5
  69. synapse/utils/stormcov/plugin.py +31 -1
  70. synapse/vendor/cpython/LICENSE +279 -0
  71. synapse/vendor/cpython/__init__.py +0 -0
  72. synapse/vendor/cpython/lib/__init__.py +0 -0
  73. synapse/vendor/cpython/lib/email/__init__.py +0 -0
  74. synapse/vendor/cpython/lib/email/_parseaddr.py +560 -0
  75. synapse/vendor/cpython/lib/email/utils.py +505 -0
  76. synapse/vendor/cpython/lib/ipaddress.py +2366 -0
  77. synapse/vendor/cpython/lib/test/__init__.py +0 -0
  78. synapse/vendor/cpython/lib/test/support/__init__.py +114 -0
  79. synapse/vendor/cpython/lib/test/test_email/__init__.py +0 -0
  80. synapse/vendor/cpython/lib/test/test_email/test_email.py +480 -0
  81. synapse/vendor/cpython/lib/test/test_email/test_utils.py +167 -0
  82. synapse/vendor/cpython/lib/test/test_ipaddress.py +2672 -0
  83. synapse/vendor/utils.py +4 -3
  84. {synapse-2.180.1.dist-info → synapse-2.182.0.dist-info}/METADATA +3 -3
  85. {synapse-2.180.1.dist-info → synapse-2.182.0.dist-info}/RECORD +88 -71
  86. {synapse-2.180.1.dist-info → synapse-2.182.0.dist-info}/WHEEL +1 -1
  87. synapse/lib/jupyter.py +0 -505
  88. synapse/tests/test_lib_jupyter.py +0 -224
  89. {synapse-2.180.1.dist-info → synapse-2.182.0.dist-info}/LICENSE +0 -0
  90. {synapse-2.180.1.dist-info → synapse-2.182.0.dist-info}/top_level.txt +0 -0
@@ -27,12 +27,13 @@ class SlabSeqn(s_t_utils.SynTest):
27
27
 
28
28
  self.eq(seqn.nextindx(), 0)
29
29
  items = ('foo', 10, 20)
30
- seqn.save(items)
30
+ await seqn.save(items)
31
31
  retn = tuple(seqn.iter(0))
32
32
  self.eq(retn, ((0, 'foo'), (1, 10), (2, 20)))
33
33
  self.chk_size(seqn)
34
34
 
35
- self.raises(s_exc.NotMsgpackSafe, seqn.save, ({'set'},))
35
+ with self.raises(s_exc.NotMsgpackSafe):
36
+ await seqn.save(({'set'},))
36
37
  retn = tuple(seqn.iter(0))
37
38
  self.eq(retn, ((0, 'foo'), (1, 10), (2, 20)))
38
39
 
@@ -48,7 +49,7 @@ class SlabSeqn(s_t_utils.SynTest):
48
49
  self.chk_size(seqn)
49
50
 
50
51
  self.eq(seqn.nextindx(), 3)
51
- seqn.save(items)
52
+ await seqn.save(items)
52
53
 
53
54
  retn = tuple(seqn.iter(0))
54
55
  self.eq(retn, ((0, 'foo'), (1, 10), (2, 20),
@@ -70,7 +71,7 @@ class SlabSeqn(s_t_utils.SynTest):
70
71
  evnt2 = seqn.getOffsetEvent(9)
71
72
  evnt3 = seqn.getOffsetEvent(8)
72
73
 
73
- seqn.save(items)
74
+ await seqn.save(items)
74
75
  retn = tuple(seqn.iter(0))
75
76
  self.len(9, retn)
76
77
  self.chk_size(seqn)
@@ -438,6 +438,10 @@ class SnapTest(s_t_utils.SynTest):
438
438
 
439
439
  self.len(1, await view0.nodes('[ test:int=10 +#woot:score=40 ]'))
440
440
 
441
+ self.len(1, await view0.nodes('[ test:int=20 +#woot:score=10 ]'))
442
+ self.len(1, await view1.nodes('[ test:int=20 +#foo:score=10 ]'))
443
+ self.len(2, await view1.nodes('#woot:score'))
444
+
441
445
  async def test_cortex_lift_layers_ordering(self):
442
446
 
443
447
  async with self._getTestCoreMultiLayer() as (view0, view1):
@@ -1,3 +1,4 @@
1
+ import copy
1
2
  import json
2
3
  import asyncio
3
4
  import datetime
@@ -23,6 +24,77 @@ import synapse.tools.backup as s_tools_backup
23
24
 
24
25
  class StormTest(s_t_utils.SynTest):
25
26
 
27
+ async def test_lib_storm_guidctor(self):
28
+ async with self.getTestCore() as core:
29
+
30
+ nodes00 = await core.nodes('[ ou:org=({"name": "vertex"}) ]')
31
+ self.len(1, nodes00)
32
+ self.eq('vertex', nodes00[0].get('name'))
33
+
34
+ nodes01 = await core.nodes('[ ou:org=({"name": "vertex"}) :names+="the vertex project"]')
35
+ self.len(1, nodes01)
36
+ self.eq('vertex', nodes01[0].get('name'))
37
+ self.eq(nodes00[0].ndef, nodes01[0].ndef)
38
+
39
+ nodes02 = await core.nodes('[ ou:org=({"name": "the vertex project"}) ]')
40
+ self.len(1, nodes02)
41
+ self.eq('vertex', nodes02[0].get('name'))
42
+ self.eq(nodes01[0].ndef, nodes02[0].ndef)
43
+
44
+ nodes03 = await core.nodes('[ ou:org=({"name": "vertex", "type": "woot"}) :names+="the vertex project" ]')
45
+ self.len(1, nodes03)
46
+ self.ne(nodes02[0].ndef, nodes03[0].ndef)
47
+
48
+ nodes04 = await core.nodes('[ ou:org=({"name": "the vertex project", "type": "woot"}) ]')
49
+ self.len(1, nodes04)
50
+ self.eq(nodes03[0].ndef, nodes04[0].ndef)
51
+
52
+ with self.raises(s_exc.BadTypeValu):
53
+ await core.nodes('[ ou:org=({"hq": "woot"}) ]')
54
+
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
+ nodes05 = await core.nodes('[ ou:org=({"name": "vertex", "$props": {"motto": "for the people"}}) ]')
60
+ self.len(1, nodes05)
61
+ self.eq('vertex', nodes05[0].get('name'))
62
+ self.eq('for the people', nodes05[0].get('motto'))
63
+ self.eq(nodes00[0].ndef, nodes05[0].ndef)
64
+
65
+ nodes06 = await core.nodes('[ ou:org=({"name": "acme", "$props": {"motto": "HURR DURR"}}) ]')
66
+ self.len(1, nodes06)
67
+ self.eq('acme', nodes06[0].get('name'))
68
+ self.eq('hurr durr', nodes06[0].get('motto'))
69
+ self.ne(nodes00[0].ndef, nodes06[0].ndef)
70
+
71
+ goals = [s_common.guid(), s_common.guid()]
72
+ goals.sort()
73
+
74
+ nodes07 = await core.nodes('[ ou:org=({"name": "goal driven", "goals": $goals}) ]', opts={'vars': {'goals': goals}})
75
+ self.len(1, nodes07)
76
+ self.eq(goals, nodes07[0].get('goals'))
77
+
78
+ nodes08 = await core.nodes('[ ou:org=({"name": "goal driven", "goals": $goals}) ]', opts={'vars': {'goals': goals}})
79
+ self.len(1, nodes08)
80
+ self.eq(goals, nodes08[0].get('goals'))
81
+ self.eq(nodes07[0].ndef, nodes08[0].ndef)
82
+
83
+ nodes09 = await core.nodes('[ ou:org=({"name": "vertex"}) :name=foobar :names=() ]')
84
+ nodes10 = await core.nodes('[ ou:org=({"name": "vertex"}) :type=lulz ]')
85
+ self.len(1, nodes09)
86
+ self.len(1, nodes10)
87
+ self.ne(nodes09[0].ndef, nodes10[0].ndef)
88
+
89
+ await core.nodes('[ ou:org=* :type=lulz ]')
90
+ await core.nodes('[ ou:org=* :type=hehe ]')
91
+ nodes11 = await core.nodes('[ ou:org=({"name": "vertex", "$props": {"type": "lulz"}}) ]')
92
+ self.len(1, nodes11)
93
+
94
+ nodes12 = await core.nodes('[ ou:org=({"name": "vertex", "type": "hehe"}) ]')
95
+ self.len(1, nodes12)
96
+ self.ne(nodes11[0].ndef, nodes12[0].ndef)
97
+
26
98
  async def test_lib_storm_jsonexpr(self):
27
99
  async with self.getTestCore() as core:
28
100
 
@@ -1483,6 +1555,43 @@ class StormTest(s_t_utils.SynTest):
1483
1555
  msgs = await core.stormlist('test:ro | merge', opts=altview)
1484
1556
  self.stormIsInWarn("Cannot merge read only property with conflicting value", msgs)
1485
1557
 
1558
+ async def test_storm_merge_stricterr(self):
1559
+
1560
+ conf = {'modules': [('synapse.tests.utils.DeprModule', {})]}
1561
+ async with self.getTestCore(conf=copy.deepcopy(conf)) as core:
1562
+
1563
+ await core.nodes('$lib.model.ext.addFormProp(test:deprprop, _str, (str, ({})), ({}))')
1564
+
1565
+ viewiden = await core.callStorm('return($lib.view.get().fork().iden)')
1566
+ asfork = {'view': viewiden}
1567
+
1568
+ await core.nodes('[ test:deprprop=base ]')
1569
+
1570
+ self.len(1, await core.nodes('test:deprprop=base [ :_str=foo +#test ]', opts=asfork))
1571
+ await core.nodes('[ test:deprprop=fork test:str=other ]', opts=asfork)
1572
+
1573
+ await core.nodes('model.deprecated.lock test:deprprop')
1574
+
1575
+ msgs = await core.stormlist('diff | merge --apply --no-tags', opts=asfork)
1576
+ self.stormIsInWarn('Form test:deprprop is locked due to deprecation for valu=base', msgs)
1577
+ self.stormIsInWarn('Form test:deprprop is locked due to deprecation for valu=fork', msgs)
1578
+ self.stormHasNoErr(msgs)
1579
+
1580
+ msgs = await core.stormlist('diff | merge --apply --only-tags', opts=asfork)
1581
+ self.stormIsInWarn('Form test:deprprop is locked due to deprecation for valu=base', msgs)
1582
+ self.stormHasNoErr(msgs)
1583
+
1584
+ self.eq({
1585
+ 'meta:source': 1,
1586
+ 'syn:tag': 1,
1587
+ 'test:deprprop': 1,
1588
+ 'test:str': 1,
1589
+ }, await core.callStorm('return($lib.view.get().getFormCounts())'))
1590
+
1591
+ nodes = await core.nodes('test:deprprop')
1592
+ self.eq(['base'], [n.ndef[1] for n in nodes])
1593
+ self.eq([], nodes[0].getTags())
1594
+
1486
1595
  async def test_storm_merge_opts(self):
1487
1596
 
1488
1597
  async with self.getTestCore() as core:
@@ -3700,7 +3809,7 @@ class StormTest(s_t_utils.SynTest):
3700
3809
  msgs = await core.stormlist('help view')
3701
3810
  self.stormIsInPrint('Storm api for a View instance', msgs)
3702
3811
  self.stormIsInPrint('view.merge', msgs)
3703
- self.stormNotInPrint('uniq', msgs)
3812
+ self.stormNotInPrint('tee', msgs)
3704
3813
 
3705
3814
  msgs = await core.stormlist('help newp')
3706
3815
  self.stormIsInPrint('No commands found matching "newp"', msgs)
@@ -61,7 +61,36 @@ class StormHttpTest(s_test.SynTest):
61
61
  core.addHttpApi('/api/v0/notjson', HttpNotJson, {'cell': core})
62
62
  core.addHttpApi('/api/v0/badjson', HttpBadJson, {'cell': core})
63
63
  url = f'https://root:root@127.0.0.1:{port}/api/v0/test'
64
- opts = {'vars': {'url': url}}
64
+ status_url = f'https://127.0.0.1:{port}/api/v1/status'
65
+ opts = {'vars': {'url': url, 'port': port, 'status_url': status_url}}
66
+
67
+ # Request URL is exposed
68
+ q = '''
69
+ $resp = $lib.inet.http.get($url, ssl_verify=$lib.false)
70
+ return ( $resp.url )
71
+ '''
72
+ resp = await core.callStorm(q, opts=opts)
73
+ # The password is omitted
74
+ self.eq(resp, f'https://127.0.0.1:{port}/api/v0/test')
75
+
76
+ # Redirects expose the final URL
77
+ q = '''
78
+ $params = ({'redirect': $status_url})
79
+ $resp = $lib.inet.http.get($url, params=$params, ssl_verify=$lib.false)
80
+ return ( $resp.url )
81
+ '''
82
+ resp = await core.callStorm(q, opts=opts)
83
+ self.eq(resp, f'https://127.0.0.1:{port}/api/v1/status')
84
+
85
+ q = '''
86
+ $_url = `https://root:root@127.0.0.1:{($port + (1))}/api/v0/newp`
87
+ $resp = $lib.inet.http.get($_url, ssl_verify=$lib.false)
88
+ if ( $resp.code != (-1) ) { $lib.exit(mesg='Test fail!') }
89
+ return ( $resp.url )
90
+ '''
91
+ resp = await core.callStorm(q, opts=opts)
92
+ # The password is present
93
+ self.eq(resp, f'https://root:root@127.0.0.1:{port + 1}/api/v0/newp')
65
94
 
66
95
  # Header and params as dict
67
96
  q = '''
@@ -106,6 +135,17 @@ class StormHttpTest(s_test.SynTest):
106
135
  resp = await core.callStorm(q, opts=opts)
107
136
  self.eq(resp, 'application/json; charset=UTF-8')
108
137
 
138
+ # Request headers
139
+ q = '''
140
+ $headers = ({"Wow": "OhMy"})
141
+ $resp = $lib.inet.http.get($url, headers=$headers, ssl_verify=$lib.false)
142
+ return ( $resp.request_headers )
143
+ '''
144
+ resp = await core.callStorm(q, opts=opts)
145
+ self.eq(resp.get('Wow'), 'OhMy')
146
+ # Authorization header derived from the basic auth in $url
147
+ self.isin('Authorization', resp)
148
+
109
149
  badurl = f'https://root:root@127.0.0.1:{port}/api/v0/notjson'
110
150
  badopts = {'vars': {'url': badurl}}
111
151
  q = '''
@@ -175,6 +215,64 @@ class StormHttpTest(s_test.SynTest):
175
215
  retn = await core.callStorm('return($lib.inet.http.codereason(123))')
176
216
  self.eq(retn, 'Unknown HTTP status code 123')
177
217
 
218
+ # Request history is preserved across multiple redirects.
219
+ q = '''$api = $lib.cortex.httpapi.add(dyn00)
220
+ $api.methods.get = ${
221
+ $api = $request.api
222
+ if $n {
223
+ $part = `redir0{$n}`
224
+ $redir = `/api/ext/{$part}`
225
+ $headers = ({"Location": $redir})
226
+ $api.vars.n = ($n - (1) )
227
+ $api.path = $part
228
+ $request.reply(301, headers=$headers)
229
+ } else {
230
+ $api.vars.n = $initial_n
231
+ $api.path = dyn00
232
+ $request.reply(200, body=({"end": "youMadeIt"}) )
233
+ }
234
+ }
235
+ $api.authenticated = (false)
236
+ $api.vars.initial_n = (3)
237
+ $api.vars.n = (3)
238
+ return ( ($api.iden) )'''
239
+ iden00 = await core.callStorm(q)
240
+
241
+ q = '''
242
+ $url = `https://127.0.0.1:{$port}/api/ext/dyn00`
243
+ $resp = $lib.inet.http.get($url, ssl_verify=$lib.false)
244
+ return ( $resp )
245
+ '''
246
+ resp = await core.callStorm(q, opts=opts)
247
+ self.eq(resp.get('url'), f'https://127.0.0.1:{port}/api/ext/redir01')
248
+ self.eq([hnfo.get('url') for hnfo in resp.get('history')],
249
+ [f'https://127.0.0.1:{port}/api/ext/dyn00',
250
+ f'https://127.0.0.1:{port}/api/ext/redir03',
251
+ f'https://127.0.0.1:{port}/api/ext/redir02',
252
+ ])
253
+
254
+ # The gtor returns a list of objects
255
+ q = '''
256
+ $url = `https://127.0.0.1:{$port}/api/ext/dyn00`
257
+ $resp = $lib.inet.http.get($url, ssl_verify=$lib.false)
258
+ return ( $resp.history.0 )
259
+ '''
260
+ resp = await core.callStorm(q, opts=opts)
261
+ self.eq(resp.get('url'), f'https://127.0.0.1:{port}/api/ext/dyn00')
262
+
263
+ # The history is not available if there is a fatal error when
264
+ # following redirects.
265
+ q = '''
266
+ $_url = `https://127.0.0.1:{($port + (1))}/api/v0/newp`
267
+ $params = ({'redirect': $_url})
268
+ $resp = $lib.inet.http.get($url, params=$params, ssl_verify=$lib.false)
269
+ if ( $resp.code != (-1) ) { $lib.exit(mesg='Test fail!') }
270
+ return ( $resp.history )
271
+ '''
272
+ resp = await core.callStorm(q, opts=opts)
273
+ self.isinstance(resp, tuple)
274
+ self.len(0, resp)
275
+
178
276
  async def test_storm_http_inject_ca(self):
179
277
 
180
278
  with self.getTestDir() as dirn:
@@ -988,6 +988,21 @@ class StormLibAuthTest(s_test.SynTest):
988
988
  msgs = await core.stormlist(f'$lib.auth.roles.del({iden})', opts=aslowuser)
989
989
  self.stormHasNoWarnErr(msgs)
990
990
 
991
+ # Use arbitrary idens when creating roles.
992
+ iden = '9e0998f68b662ed3776b6ce33a2d21eb'
993
+ with self.raises(s_exc.BadArg):
994
+ await core.callStorm('$lib.auth.roles.add(runners, iden=12345)')
995
+ opts = {'vars': {'iden': iden}}
996
+ rdef = await core.callStorm('$r=$lib.auth.roles.add(runners, iden=$iden) return ( $r )',
997
+ opts=opts)
998
+ self.eq(rdef.get('iden'), iden)
999
+ ret = await core.callStorm('return($lib.auth.roles.get($iden))', opts=opts)
1000
+ self.eq(ret, rdef)
1001
+ with self.raises(s_exc.DupRoleName):
1002
+ await core.callStorm('$lib.auth.roles.add(runners, iden=$iden)', opts=opts)
1003
+ with self.raises(s_exc.DupIden):
1004
+ await core.callStorm('$lib.auth.roles.add(walkers, iden=$iden)', opts=opts)
1005
+
991
1006
  async def test_stormlib_auth_gateadmin(self):
992
1007
 
993
1008
  async with self.getTestCore() as core:
@@ -9,8 +9,6 @@ import synapse.exc as s_exc
9
9
 
10
10
  import synapse.tests.utils as s_test
11
11
 
12
- from pprint import pprint
13
-
14
12
  class CortexLibTest(s_test.SynTest):
15
13
 
16
14
  async def test_libcortex_httpapi_methods(self):
@@ -63,11 +61,16 @@ $request.reply(206, headers=$headers, body=({"no":"body"}))
63
61
  '''
64
62
  testpath00 = await core.callStorm(q)
65
63
 
64
+ iden = s_common.guid()
66
65
  q = '''
67
- $api = $lib.cortex.httpapi.add(nomeths)
66
+ $api = $lib.cortex.httpapi.add(nomeths, iden=$iden)
68
67
  return ( $api.iden )
69
68
  '''
70
- nomeths = await core.callStorm(q)
69
+ nomeths = await core.callStorm(q, opts={'vars': {'iden': iden}})
70
+ self.eq(iden, nomeths)
71
+
72
+ adef = await core.getHttpExtApi(iden)
73
+ self.nn(adef)
71
74
 
72
75
  info = await core.callStorm('return( $lib.cortex.httpapi.get($iden).pack() )',
73
76
  opts={'vars': {'iden': testpath00}})
@@ -89,6 +92,20 @@ $request.reply(206, headers=$headers, body=({"no":"body"}))
89
92
  q = '$api = $lib.cortex.httpapi.get($iden) $api.methods.wildycustomverb = ${}'
90
93
  await core.callStorm(q, opts={'vars': {'iden': testpath00}})
91
94
 
95
+ with self.raises(s_exc.DupIden):
96
+ q = '''
97
+ $api = $lib.cortex.httpapi.add(duplicate, iden=$iden)
98
+ return ( $api.iden )
99
+ '''
100
+ await core.callStorm(q, opts={'vars': {'iden': iden}})
101
+
102
+ with self.raises(s_exc.SchemaViolation):
103
+ q = '''
104
+ $api = $lib.cortex.httpapi.add(duplicate, iden="trollolololol")
105
+ return ( $api.iden )
106
+ '''
107
+ await core.callStorm(q, opts={'vars': {'iden': iden}})
108
+
92
109
  async with self.getHttpSess(auth=('root', 'root'), port=hport) as sess: # type: aiohttp.ClientSession
93
110
  resp = await sess.get(f'https://localhost:{hport}/api/ext/testpath00')
94
111
  self.eq(resp.status, 200)
@@ -109,8 +109,11 @@ class StormLibItersTest(s_test.SynTest):
109
109
  }
110
110
  '''
111
111
  msgs = await core.stormlist(q)
112
- err = "$lib.iters.zip() encountered errors in 3 iterators during iteration: " \
113
- "(foo: bar), " \
114
- "(NoSuchVar: Missing variable: newp), " \
115
- "(NameError: name 'newp' is not defined)"
116
- self.stormIsInErr(err, msgs)
112
+ errs = (
113
+ "$lib.iters.zip() encountered errors in 3 iterators during iteration: ",
114
+ "(foo: bar)",
115
+ "(NoSuchVar: Missing variable: newp)",
116
+ "(NameError: name 'newp' is not defined)",
117
+ )
118
+ for errchunk in errs:
119
+ self.stormIsInErr(errchunk, msgs)
@@ -403,9 +403,48 @@ class StormlibModelTest(s_test.SynTest):
403
403
  q = 'test:str=src $n=$node -> { test:str=deny $lib.model.migration.copyTags($n, $node) }'
404
404
  await self.asyncraises(s_exc.AuthDeny, core.nodes(q, opts=aslow))
405
405
 
406
+ with self.raises(s_exc.NoSuchProp) as exc:
407
+ await core.callStorm('$lib.model.migration.liftByPropValuNoNorm(formname, propname, valu)')
408
+ self.eq(exc.exception.get('mesg'), 'Could not find prop: formname:propname')
409
+
410
+ with self.raises(s_exc.AuthDeny) as exc:
411
+ await core.callStorm('$lib.model.migration.liftByPropValuNoNorm(it:prod:soft, cpe, valu)')
412
+ self.eq(exc.exception.get('mesg'), '$lib.model.migration.liftByPropValuNoNorm() is restricted to model migrations only.')
413
+
414
+ with self.raises(s_exc.BadArg) as exc:
415
+ await core.callStorm('$lib.model.migration.setNodePropValuNoNorm(notanode, propname, valu)')
416
+ self.eq(exc.exception.get('mesg'), '$lib.model.migration.setNodePropValuNoNorm() argument must be a node.')
417
+
418
+ with self.raises(s_exc.AuthDeny) as exc:
419
+ await core.callStorm('test:str $lib.model.migration.setNodePropValuNoNorm($node, propname, valu)')
420
+ self.eq(exc.exception.get('mesg'), '$lib.model.migration.setNodePropValuNoNorm() is restricted to model migrations only.')
421
+
422
+ # copy extended properties
423
+
424
+ await self.asyncraises(s_exc.BadArg, core.nodes('test:str=src $lib.model.migration.copyExtProps($node, newp)'))
425
+ await self.asyncraises(s_exc.BadArg, core.nodes('test:str=dst $lib.model.migration.copyExtProps(newp, $node)'))
426
+
427
+ await core.addFormProp('test:str', '_foo', ('str', {}), {})
428
+
429
+ srciden = s_common.guid()
430
+ dstiden = s_common.guid()
431
+
432
+ opts = {'vars': {'srciden': srciden, 'dstiden': dstiden}}
433
+ await core.callStorm('''
434
+ [ test:str=$srciden :_foo=foobarbaz ]
435
+ $n=$node -> {
436
+ [ test:str=$dstiden ]
437
+ $lib.model.migration.copyExtProps($n, $node)
438
+ }
439
+ ''', opts=opts)
440
+
441
+ nodes = await core.nodes('test:str=$dstiden', opts=opts)
442
+ self.len(1, nodes)
443
+ self.eq(nodes[0].get('_foo'), 'foobarbaz')
444
+
406
445
  async def test_model_migration_s_itSecCpe_2_170_0(self):
407
446
 
408
- async with self.getRegrCore('itSecCpe_2_170_0') as core:
447
+ async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
409
448
  # Migrate it:sec:cpe nodes with a valid CPE2.3, valid CPE2.2
410
449
  q = 'it:sec:cpe +#test.cpe.23valid +#test.cpe.22valid'
411
450
  nodes = await core.nodes(q)
@@ -436,7 +475,7 @@ class StormlibModelTest(s_test.SynTest):
436
475
  self.eq(data['status'], 'success')
437
476
  self.none(data.get('reason'))
438
477
 
439
- async with self.getRegrCore('itSecCpe_2_170_0') as core:
478
+ async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
440
479
  # Migrate it:sec:cpe nodes with a valid CPE2.3, invalid CPE2.2
441
480
  q = '''
442
481
  it:sec:cpe +#test.cpe.23valid +#test.cpe.22invalid
@@ -512,7 +551,7 @@ class StormlibModelTest(s_test.SynTest):
512
551
  self.eq(nodes[0].get('v2_2'), 'cpe:/o:zyxel:nas542_firmware:5.21%28aazf.15%29co')
513
552
  self.eq(nodes[0].get('version'), '5.21(aazf.15)co')
514
553
 
515
- async with self.getRegrCore('itSecCpe_2_170_0') as core:
554
+ async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
516
555
  # Migrate it:sec:cpe nodes with a invalid CPE2.3, valid CPE2.2
517
556
  q = '''
518
557
  it:sec:cpe +#test.cpe.23invalid +#test.cpe.22valid
@@ -573,7 +612,7 @@ class StormlibModelTest(s_test.SynTest):
573
612
  self.eq(data['valu'], 'cpe:2.3:o:zyxel:nas326_firmware:5.21\\(aazf.14\\)c0:*:*:*:*:*:*:*')
574
613
  self.eq(nodes[3].get('version'), '5.21(aazf.14)c0')
575
614
 
576
- async with self.getRegrCore('itSecCpe_2_170_0') as core:
615
+ async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
577
616
  # Migrate it:sec:cpe nodes with a invalid CPE2.3, invalid CPE2.2
578
617
  q = '''
579
618
  it:sec:cpe +#test.cpe.23invalid +#test.cpe.22invalid
@@ -639,7 +678,7 @@ class StormlibModelTest(s_test.SynTest):
639
678
  $lib.model.migration.s.itSecCpe_2_170_0($node)
640
679
  '''
641
680
  msgs = await core.stormlist(q)
642
- self.stormIsInPrint(f'DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): Node already migrated.', msgs)
681
+ self.stormIsInPrint('DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): Node already migrated.', msgs)
643
682
 
644
683
  q = '''
645
684
  it:sec:cpe:version^=8.2p1
@@ -647,7 +686,7 @@ class StormlibModelTest(s_test.SynTest):
647
686
  $lib.model.migration.s.itSecCpe_2_170_0($node, force=$lib.true)
648
687
  '''
649
688
  msgs = await core.stormlist(q)
650
- self.stormIsInPrint(f'DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): No property updates required.', msgs)
689
+ self.stormIsInPrint('DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): No property updates required.', msgs)
651
690
 
652
691
  async with self.getTestCore() as core:
653
692
  with self.raises(s_exc.BadArg):
@@ -1657,6 +1657,25 @@ class StormTypesTest(s_test.SynTest):
1657
1657
  with self.raises(s_exc.StormRuntimeError):
1658
1658
  await core.callStorm('$lib.list().pop()')
1659
1659
 
1660
+ somelist = ["foo", "bar", "baz", "bar"]
1661
+ q = '''
1662
+ $l.rem("bar")
1663
+ $l.rem("newp")
1664
+ return($l)
1665
+ '''
1666
+ opts = {'vars': {'l': somelist.copy()}}
1667
+ out = await core.callStorm(q, opts=opts)
1668
+ self.eq(out, ["foo", "baz", "bar"])
1669
+
1670
+ somelist = ["foo", "bar", "baz", "bar"]
1671
+ q = '''
1672
+ $l.rem("bar", all=$lib.true)
1673
+ return($l)
1674
+ '''
1675
+ opts = {'vars': {'l': somelist.copy()}}
1676
+ out = await core.callStorm(q, opts=opts)
1677
+ self.eq(out, ["foo", "baz"])
1678
+
1660
1679
  async def test_storm_layer_getstornode(self):
1661
1680
 
1662
1681
  async with self.getTestCore() as core:
@@ -5638,6 +5657,143 @@ class StormTypesTest(s_test.SynTest):
5638
5657
  q = 'return($lib.layer.get().getTagPropCount(foo, newp, valu=2))'
5639
5658
  await core.callStorm(q)
5640
5659
 
5660
+ async def test_stormtypes_prop_uniq_values(self):
5661
+ async with self.getTestCore() as core:
5662
+
5663
+ layr1vals = [
5664
+ 'a' * 512 + 'a',
5665
+ 'a' * 512 + 'a',
5666
+ 'a' * 512 + 'c',
5667
+ 'a' * 512 + 'c',
5668
+ 'c' * 512,
5669
+ 'c' * 512,
5670
+ 'c',
5671
+ 'c'
5672
+ ]
5673
+ opts = {'vars': {'vals': layr1vals}}
5674
+ await core.nodes('for $val in $vals {[ it:dev:str=$val .seen=2020 ]}', opts=opts)
5675
+ await core.nodes('for $val in $vals {[ ou:org=* :name=$val .seen=2021]}', opts=opts)
5676
+
5677
+ layr2vals = [
5678
+ 'a' * 512 + 'a',
5679
+ 'a' * 512 + 'a',
5680
+ 'a' * 512 + 'b',
5681
+ 'a' * 512 + 'b',
5682
+ 'b' * 512,
5683
+ 'b' * 512,
5684
+ 'b',
5685
+ 'b'
5686
+ ]
5687
+ forkview = await core.callStorm('return($lib.view.get().fork().iden)')
5688
+ opts = {'view': forkview, 'vars': {'vals': layr2vals}}
5689
+ await core.nodes('for $val in $vals {[ it:dev:str=$val .seen=2020]}', opts=opts)
5690
+ await core.nodes('for $val in $vals {[ ou:org=* :name=$val .seen=2023]}', opts=opts)
5691
+
5692
+ viewq = '''
5693
+ $vals = ([])
5694
+ for $valu in $lib.view.get().getPropValues($prop) {
5695
+ $vals.append($valu)
5696
+ }
5697
+ return($vals)
5698
+ '''
5699
+
5700
+ layrq = '''
5701
+ $vals = ([])
5702
+ for $valu in $lib.layer.get().getPropValues($prop) {
5703
+ $vals.append($valu)
5704
+ }
5705
+ return($vals)
5706
+ '''
5707
+
5708
+ # Values come out in index order which is not necessarily value order
5709
+ opts = {'vars': {'prop': 'it:dev:str'}}
5710
+ uniqvals = list(set(layr1vals))
5711
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5712
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5713
+
5714
+ opts['view'] = forkview
5715
+ uniqvals = list(set(layr2vals) - set(layr1vals))
5716
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5717
+
5718
+ uniqvals = list(set(layr1vals) | set(layr2vals))
5719
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5720
+
5721
+ opts = {'vars': {'prop': 'ou:org:name'}}
5722
+ uniqvals = list(set(layr1vals))
5723
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5724
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5725
+
5726
+ opts['view'] = forkview
5727
+ uniqvals = list(set(layr2vals))
5728
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5729
+
5730
+ uniqvals = list(set(layr1vals) | set(layr2vals))
5731
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5732
+
5733
+ opts = {'vars': {'prop': '.seen'}}
5734
+
5735
+ ival = core.model.type('ival')
5736
+ uniqvals = [ival.norm('2020')[0], ival.norm('2021')[0]]
5737
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5738
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5739
+
5740
+ opts['view'] = forkview
5741
+ uniqvals = [ival.norm('2020')[0], ival.norm('2023')[0]]
5742
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5743
+
5744
+ uniqvals = [ival.norm('2020')[0], ival.norm('2021')[0], ival.norm('2023')[0]]
5745
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5746
+
5747
+ opts['vars']['prop'] = 'ps:contact:name'
5748
+ self.eq([], await core.callStorm(viewq, opts=opts))
5749
+
5750
+ opts['vars']['prop'] = 'newp:newp'
5751
+ with self.raises(s_exc.NoSuchProp):
5752
+ await core.callStorm(layrq, opts=opts)
5753
+
5754
+ with self.raises(s_exc.NoSuchProp):
5755
+ await core.callStorm(viewq, opts=opts)
5756
+
5757
+ arryvals = [
5758
+ ('foo', 'bar'),
5759
+ ('foo', 'bar'),
5760
+ ('foo', 'baz'),
5761
+ ('bar', 'baz'),
5762
+ ('bar', 'foo')
5763
+ ]
5764
+ opts = {'vars': {'vals': arryvals}}
5765
+ await core.nodes('for $val in $vals {[ transport:air:flight=* :stops=$val ]}', opts=opts)
5766
+
5767
+ opts = {'vars': {'prop': 'transport:air:flight:stops'}}
5768
+ uniqvals = list(set(arryvals))
5769
+ self.sorteq(uniqvals, await core.callStorm(viewq, opts=opts))
5770
+ self.sorteq(uniqvals, await core.callStorm(layrq, opts=opts))
5771
+
5772
+ await core.nodes('[ media:news=(bar,) :title=foo ]')
5773
+ await core.nodes('[ media:news=(baz,) :title=bar ]')
5774
+ await core.nodes('[ media:news=(faz,) :title=faz ]')
5775
+
5776
+ forkopts = {'view': forkview}
5777
+ await core.nodes('[ media:news=(baz,) :title=faz ]', opts=forkopts)
5778
+
5779
+ opts = {'vars': {'prop': 'media:news:title'}}
5780
+ self.eq(['bar', 'faz', 'foo'], await core.callStorm(viewq, opts=opts))
5781
+
5782
+ opts = {'view': forkview, 'vars': {'prop': 'media:news:title'}}
5783
+ self.eq(['faz', 'foo'], await core.callStorm(viewq, opts=opts))
5784
+
5785
+ forkview2 = await core.callStorm('return($lib.view.get().fork().iden)', opts=forkopts)
5786
+ forkopts2 = {'view': forkview2}
5787
+
5788
+ await core.nodes('[ ps:contact=(foo,) :name=foo ]', opts=forkopts2)
5789
+ await core.nodes('[ ps:contact=(foo,) :name=bar ]', opts=forkopts)
5790
+ await core.nodes('[ ps:contact=(bar,) :name=bar ]')
5791
+
5792
+ opts = {'view': forkview2, 'vars': {'prop': 'ps:contact:name'}}
5793
+ self.eq(['bar', 'foo'], await core.callStorm(viewq, opts=opts))
5794
+
5795
+ self.eq([], await alist(core.getLayer().iterPropIndxBuids('newp', 'newp', 'newp')))
5796
+
5641
5797
  async def test_lib_stormtypes_cmdopts(self):
5642
5798
  pdef = {
5643
5799
  'name': 'foo',
@@ -6389,9 +6545,9 @@ words\tword\twrd'''
6389
6545
  self.eq(2, await core.callStorm('return($lib.math.number(1.23).toint(rounding=ROUND_UP))'))
6390
6546
 
6391
6547
  with self.raises(s_exc.BadCast):
6392
- await core.callStorm('return($lib.math.number((null)))')
6548
+ await core.callStorm('return($lib.math.number((null)))')
6393
6549
  with self.raises(s_exc.BadCast):
6394
- await core.callStorm('return($lib.math.number(newp))')
6550
+ await core.callStorm('return($lib.math.number(newp))')
6395
6551
 
6396
6552
  with self.raises(s_exc.StormRuntimeError):
6397
6553
  await core.callStorm('return($lib.math.number(1.23).toint(rounding=NEWP))')