synapse 2.212.0__py311-none-any.whl → 2.214.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 (75) hide show
  1. synapse/cortex.py +37 -6
  2. synapse/daemon.py +6 -6
  3. synapse/exc.py +13 -1
  4. synapse/lib/aha.py +5 -0
  5. synapse/lib/ast.py +2 -6
  6. synapse/lib/boss.py +47 -2
  7. synapse/lib/cell.py +199 -6
  8. synapse/lib/certdir.py +44 -1
  9. synapse/lib/cmd.py +24 -0
  10. synapse/lib/coro.py +8 -2
  11. synapse/lib/drive.py +7 -2
  12. synapse/lib/link.py +11 -3
  13. synapse/lib/schemas.py +1 -1
  14. synapse/lib/scrape.py +3 -1
  15. synapse/lib/snap.py +89 -80
  16. synapse/lib/storm.py +2 -1
  17. synapse/lib/stormlib/imap.py +3 -2
  18. synapse/lib/stormlib/spooled.py +4 -0
  19. synapse/lib/stormtypes.py +18 -0
  20. synapse/lib/task.py +1 -0
  21. synapse/lib/types.py +36 -8
  22. synapse/lib/version.py +2 -2
  23. synapse/models/inet.py +5 -0
  24. synapse/telepath.py +4 -2
  25. synapse/tests/files/testpkg_build_docs/docs/bar.rst +15 -0
  26. synapse/tests/files/testpkg_build_docs/docs/foo.rst +4 -0
  27. synapse/tests/files/testpkg_build_docs/storm/commands/testcmd.storm +0 -0
  28. synapse/tests/files/testpkg_build_docs/storm/modules/apimod.storm +0 -0
  29. synapse/tests/files/testpkg_build_docs/storm/modules/testmod.storm +0 -0
  30. synapse/tests/files/testpkg_build_docs/storm/testcmd.storm +5 -0
  31. synapse/tests/files/testpkg_build_docs/testpkg.yaml +69 -0
  32. synapse/tests/test_cortex.py +20 -1
  33. synapse/tests/test_daemon.py +1 -1
  34. synapse/tests/test_exc.py +6 -0
  35. synapse/tests/test_lib_ast.py +69 -14
  36. synapse/tests/test_lib_boss.py +8 -0
  37. synapse/tests/test_lib_cell.py +119 -8
  38. synapse/tests/test_lib_certdir.py +8 -0
  39. synapse/tests/test_lib_coro.py +5 -0
  40. synapse/tests/test_lib_httpapi.py +10 -2
  41. synapse/tests/test_lib_link.py +1 -1
  42. synapse/tests/test_lib_scrape.py +6 -0
  43. synapse/tests/test_lib_storm.py +123 -1
  44. synapse/tests/test_lib_stormlib_spooled.py +31 -0
  45. synapse/tests/test_lib_stormtypes.py +11 -0
  46. synapse/tests/test_lib_types.py +137 -45
  47. synapse/tests/test_model_crypto.py +8 -0
  48. synapse/tests/test_model_inet.py +7 -0
  49. synapse/tests/test_telepath.py +50 -5
  50. synapse/tests/test_tools_axon.py +304 -0
  51. synapse/tests/test_tools_cortex_layer.py +419 -0
  52. synapse/tests/test_tools_demote.py +114 -0
  53. synapse/tests/test_tools_pkgs_gendocs.py +100 -0
  54. synapse/tests/test_tools_shutdown.py +95 -0
  55. synapse/tests/test_utils.py +22 -1
  56. synapse/tests/utils.py +44 -29
  57. synapse/tools/aha/easycert.py +2 -0
  58. synapse/tools/aha/enroll.py +3 -0
  59. synapse/tools/axon/__init__.py +0 -0
  60. synapse/tools/axon/dump.py +155 -0
  61. synapse/tools/axon/load.py +89 -0
  62. synapse/tools/cortex/__init__.py +0 -0
  63. synapse/tools/cortex/layer/__init__.py +0 -0
  64. synapse/tools/cortex/layer/dump.py +184 -0
  65. synapse/tools/cortex/layer/load.py +129 -0
  66. synapse/tools/demote.py +52 -0
  67. synapse/tools/healthcheck.py +1 -1
  68. synapse/tools/pkgs/gendocs.py +176 -0
  69. synapse/tools/pkgs/pandoc_filter.py +79 -0
  70. synapse/tools/shutdown.py +52 -0
  71. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/METADATA +1 -1
  72. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/RECORD +75 -52
  73. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/WHEEL +0 -0
  74. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/licenses/LICENSE +0 -0
  75. {synapse-2.212.0.dist-info → synapse-2.214.0.dist-info}/top_level.txt +0 -0
@@ -576,6 +576,10 @@ class CertDirTest(s_t_utils.SynTest):
576
576
  key = cdir.getHostKey(hostname)
577
577
  self.basic_assertions(cdir, cert, key, cacert=cacert)
578
578
 
579
+ self.true(cdir.delHostCsr(hostname))
580
+ self.false(os.path.isfile(path))
581
+ self.false(cdir.delHostCsr(hostname))
582
+
579
583
  # Per RFC5280 common-name has a length of 1 to 64 characters
580
584
  # Do not generate CSRs which exceed that name range.
581
585
  with self.raises(s_exc.CryptoErr) as cm:
@@ -610,6 +614,10 @@ class CertDirTest(s_t_utils.SynTest):
610
614
  key = cdir.getUserKey(username)
611
615
  self.basic_assertions(cdir, cert, key, cacert=cacert)
612
616
 
617
+ self.true(cdir.delUserCsr(username))
618
+ self.false(os.path.isfile(path))
619
+ self.false(cdir.delUserCsr(username))
620
+
613
621
  # Per RFC5280 common-name has a length of 1 to 64 characters
614
622
  # Do not generate CSRs which exceed that name range.
615
623
  with self.raises(s_exc.CryptoErr) as cm:
@@ -235,3 +235,8 @@ class CoroTest(s_t_utils.SynTest):
235
235
  results = await s_coro.await_bg_tasks()
236
236
  self.len(1, results)
237
237
  self.isinstance(results[0], ZeroDivisionError)
238
+
239
+ task = s_coro.create_task(sleep(10))
240
+ self.eq([], await s_coro.await_bg_tasks(timeout=0.001))
241
+ task.cancel()
242
+ self.eq([], await s_coro.await_bg_tasks())
@@ -807,8 +807,8 @@ class HttpApiTest(s_tests.SynTest):
807
807
  opts['user'] = newpuser
808
808
  async with sess.get(f'https://localhost:{port}/api/v1/storm', json=data) as resp:
809
809
  self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
810
- data = await resp.json()
811
- self.eq(data, {'status': 'err', 'code': 'NoSuchUser',
810
+ info = await resp.json()
811
+ self.eq(info, {'status': 'err', 'code': 'NoSuchUser',
812
812
  'mesg': f'No user found with iden: {newpuser}'})
813
813
 
814
814
  async def test_http_coreinfo(self):
@@ -1615,6 +1615,14 @@ class HttpApiTest(s_tests.SynTest):
1615
1615
  json={'query': '.created', 'opts': {'user': lowuser.iden, 'view': fork}}) as resp:
1616
1616
  self.eq(resp.status, http.HTTPStatus.FORBIDDEN)
1617
1617
 
1618
+ # ShuttingDown precondition failure
1619
+ core.boss.is_shutdown = True
1620
+ async with sess.get(f'https://localhost:{port}/api/v1/storm', json={'query': '.created'}) as resp:
1621
+ self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
1622
+ info = await resp.json()
1623
+ self.eq(info.get('code'), 'ShuttingDown')
1624
+ core.boss.is_shutdown = False
1625
+
1618
1626
  # check reqvalidstorm with various queries
1619
1627
  tvs = (
1620
1628
  ('test:str=test', {}, 'ok'),
@@ -121,7 +121,7 @@ class LinkTest(s_test.SynTest):
121
121
  self.eq(msg0, ('what', {'k': 1}))
122
122
  evt.set()
123
123
  await asyncio.sleep(0)
124
- with self.getAsyncLoggerStream('synapse.lib.link', 'rx error') as stream:
124
+ with self.getAsyncLoggerStream('synapse.lib.link', 'rx closed unexpectedly') as stream:
125
125
  msg1 = await link.rx()
126
126
  self.true(await stream.wait(6))
127
127
  self.none(msg1)
@@ -203,6 +203,10 @@ A bunch of prefixed urls
203
203
 
204
204
  https://c2server.com/evil/malware/doesnot[care+]aboutstandards{at-all}
205
205
 
206
+ beep “https://unicode.doublequote.org/test.php” boop
207
+
208
+ x ‘https://unicode.singlequote.org/test.php’ z
209
+
206
210
  '''
207
211
 
208
212
  data3 = '''
@@ -725,6 +729,8 @@ class ScrapeTest(s_t_utils.SynTest):
725
729
  nodes.remove(('inet:url', 'https://www.thingspace.com/blog/giggles.html'))
726
730
  nodes.remove(('inet:url', 'https://testme.org/test.php'))
727
731
  nodes.remove(('inet:url', 'https://c2server.com/evil/malware/doesnot[care+]aboutstandards{at-all}'))
732
+ nodes.remove(('inet:url', 'https://unicode.doublequote.org/test.php'))
733
+ nodes.remove(('inet:url', 'https://unicode.singlequote.org/test.php'))
728
734
 
729
735
  nodes = list(s_scrape.scrape(btc_addresses))
730
736
  self.len(11, nodes)
@@ -130,7 +130,6 @@ class StormTest(s_t_utils.SynTest):
130
130
  self.none(props.get('phone'))
131
131
  self.eq(props.get('name'), 'burrito corp')
132
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
133
 
135
134
  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
135
 
@@ -173,6 +172,127 @@ class StormTest(s_t_utils.SynTest):
173
172
  nodes = await core.nodes('[ it:exec:proc=(nulltime,) ]')
174
173
  self.len(1, nodes)
175
174
 
175
+ # Recursive gutors
176
+ nodes = await core.nodes('''[
177
+ inet:service:message=({
178
+ 'id': 'foomesg',
179
+ 'channel': {
180
+ 'id': 'foochannel',
181
+ 'platform': {
182
+ 'name': 'fooplatform',
183
+ 'url': 'http://foo.com'
184
+ }
185
+ }
186
+ })
187
+ ]''')
188
+ self.len(1, nodes)
189
+ node = nodes[0]
190
+ self.eq(node.ndef[0], 'inet:service:message')
191
+ self.eq(node.get('id'), 'foomesg')
192
+ self.nn(node.get('channel'))
193
+
194
+ nodes = await core.nodes('inet:service:message -> inet:service:channel')
195
+ self.len(1, nodes)
196
+ node = nodes[0]
197
+ self.eq(node.get('id'), 'foochannel')
198
+ self.nn(node.get('platform'))
199
+
200
+ nodes = await core.nodes('inet:service:message -> inet:service:channel -> inet:service:platform')
201
+ self.len(1, nodes)
202
+ node = nodes[0]
203
+ self.eq(node.get('name'), 'fooplatform')
204
+ self.eq(node.get('url'), 'http://foo.com')
205
+
206
+ nodes = await core.nodes('''
207
+ inet:service:message=({
208
+ 'id': 'foomesg',
209
+ 'channel': {
210
+ 'id': 'foochannel',
211
+ 'platform': {
212
+ 'name': 'fooplatform',
213
+ 'url': 'http://foo.com'
214
+ }
215
+ }
216
+ })
217
+ ''')
218
+ self.len(1, nodes)
219
+ node = nodes[0]
220
+ self.eq(node.ndef[0], 'inet:service:message')
221
+ self.eq(node.get('id'), 'foomesg')
222
+
223
+ nodes = await core.nodes('''[
224
+ inet:service:message=({
225
+ 'id': 'barmesg',
226
+ 'channel': {
227
+ 'id': 'barchannel',
228
+ 'platform': {
229
+ 'name': 'barplatform',
230
+ 'url': 'http://bar.com'
231
+ }
232
+ },
233
+ '$props': {
234
+ 'platform': {
235
+ 'name': 'barplatform',
236
+ 'url': 'http://bar.com'
237
+ }
238
+ }
239
+ })
240
+ ]''')
241
+ self.len(1, nodes)
242
+ node = nodes[0]
243
+ self.eq(node.ndef[0], 'inet:service:message')
244
+ self.eq(node.get('id'), 'barmesg')
245
+ self.nn(node.get('channel'))
246
+
247
+ platguid = node.get('platform')
248
+ self.nn(platguid)
249
+ nodes = await core.nodes('inet:service:message:id=barmesg -> inet:service:channel -> inet:service:platform')
250
+ self.len(1, nodes)
251
+ self.eq(platguid, nodes[0].ndef[1])
252
+
253
+ # No node lifted if no matching node for inner gutor
254
+ self.len(0, await core.nodes('''
255
+ inet:service:message=({
256
+ 'id': 'foomesg',
257
+ 'channel': {
258
+ 'id': 'foochannel',
259
+ 'platform': {
260
+ 'name': 'newp',
261
+ 'url': 'http://foo.com'
262
+ }
263
+ }
264
+ })
265
+ '''))
266
+
267
+ # BadTypeValu comes through from inner gutor
268
+ with self.raises(s_exc.BadTypeValu) as cm:
269
+ await core.nodes('''
270
+ inet:service:message=({
271
+ 'id': 'foomesg',
272
+ 'channel': {
273
+ 'id': 'foochannel',
274
+ 'platform': {
275
+ 'name': 'newp',
276
+ 'url': 'newp'
277
+ }
278
+ }
279
+ })
280
+ ''')
281
+
282
+ self.eq(cm.exception.get('form'), 'inet:service:platform')
283
+ self.eq(cm.exception.get('prop'), 'url')
284
+ self.eq(cm.exception.get('mesg'), 'Bad value for prop inet:service:platform:url: Invalid/Missing protocol')
285
+
286
+ # Ensure inner nodes are not created unless the entire gutor is valid.
287
+ self.len(0, await core.nodes('''[
288
+ inet:service:account?=({
289
+ "id": "bar",
290
+ "platform": {"name": "barplat"},
291
+ "url": "newp"})
292
+ ]'''))
293
+
294
+ self.len(0, await core.nodes('inet:service:platform:name=barplat'))
295
+
176
296
  async def test_lib_storm_jsonexpr(self):
177
297
  async with self.getTestCore() as core:
178
298
 
@@ -1736,6 +1856,8 @@ class StormTest(s_t_utils.SynTest):
1736
1856
  self.stormHasNoErr(await core.stormlist('merge --diff', opts=altview))
1737
1857
 
1738
1858
  oldn = await core.nodes('[ ou:name=readonly ]', opts=altview)
1859
+ # need to pause a moment so the created times differ
1860
+ await asyncio.sleep(0.01)
1739
1861
  newn = await core.nodes('[ ou:name=readonly ]')
1740
1862
  self.ne(oldn[0].props['.created'], newn[0].props['.created'])
1741
1863
 
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import synapse.exc as s_exc
2
3
  import synapse.lib.stormtypes as s_stormtypes
3
4
 
@@ -124,6 +125,17 @@ class StormlibSpooledTest(s_test.SynTest):
124
125
  msgs = await core.stormlist(q, opts={'vars': {'items': [True, 'neato', False, 9001]}})
125
126
  self.stormIsInPrint("The set is {'neato'}", msgs)
126
127
 
128
+ q = '''
129
+ $set = $lib.spooled.set()
130
+ for $v in $lib.range($n) {
131
+ $set.add($v)
132
+ }
133
+ if $set { return ( (true) ) }
134
+ else { return ( (false) ) }
135
+ '''
136
+ self.false(await core.callStorm(q, opts={'vars': {'n': 0}}))
137
+ self.true(await core.callStorm(q, opts={'vars': {'n': 1}}))
138
+
127
139
  # force a fallback
128
140
  q = '''
129
141
  $set = $lib.spooled.set()
@@ -133,6 +145,20 @@ class StormlibSpooledTest(s_test.SynTest):
133
145
  valu = await core.callStorm(q)
134
146
  self.eq(1500, valu)
135
147
 
148
+ subq = '''{
149
+ $sset = $lib.spooled.set()
150
+ $sset.adds($lib.range(1500))
151
+ return($sset)
152
+ }'''
153
+
154
+ q = '''
155
+ $subset = $lib.storm.eval($text)
156
+ $subset.add(2345)
157
+ return($subset.size())
158
+ '''
159
+ valu = await core.callStorm(q, opts={'vars': {'text': subq}})
160
+ self.eq(1501, valu)
161
+
136
162
  # sad paths
137
163
  # too complex
138
164
  q = '''
@@ -188,3 +214,8 @@ class StormlibSpooledTest(s_test.SynTest):
188
214
  return($set)
189
215
  '''
190
216
  await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
217
+
218
+ # make sure the various spools got trashed
219
+ path = os.path.join(core.dirn, 'tmp')
220
+ files = [f for f in os.listdir(os.path.join(path))]
221
+ self.len(0, files)
@@ -1902,6 +1902,17 @@ class StormTypesTest(s_test.SynTest):
1902
1902
  self.len(1, nodes)
1903
1903
  self.eq(nodes[0].ndef, ('test:str', 'asdf'))
1904
1904
 
1905
+ q = '''
1906
+ $set = $lib.set()
1907
+ for $v in $lib.range($n) {
1908
+ $set.add($v)
1909
+ }
1910
+ if $set { return ( (true) ) }
1911
+ else { return ( (false) ) }
1912
+ '''
1913
+ self.false(await core.callStorm(q, opts={'vars': {'n': 0}}))
1914
+ self.true(await core.callStorm(q, opts={'vars': {'n': 1}}))
1915
+
1905
1916
  # test that some of the more complex objects we've got uniq down properly
1906
1917
  # Bool
1907
1918
  q = '''
@@ -338,42 +338,80 @@ class TypesTest(s_t_utils.SynTest):
338
338
  t = core.model.type('test:hexa')
339
339
  # Test norming to index values
340
340
  testvectors = [
341
- ('0C', b'\x0c'),
342
- ('0X010001', b'\x01\x00\x01'),
343
- ('0FfF', b'\x0f\xff'),
344
- ('f12A3e', b'\xf1\x2a\x3e'),
345
- (b'\x01\x00\x01', b'\x01\x00\x01'),
341
+ (0xc, '0c'),
342
+ (-0xc, 'f4'),
343
+ ('c', '0c'),
344
+ ('0c', '0c'),
345
+ ('-0c', (s_exc.BadTypeValu, 'Non-hexadecimal digit found')),
346
+ ('0x0c', '0c'),
347
+ ('-0x0c', (s_exc.BadTypeValu, 'Non-hexadecimal digit found')),
348
+ (b'\x0c', '0c'),
349
+
350
+ (0x10001, '010001'),
351
+ ('10001', '010001'),
352
+ ('0x10001', '010001'),
353
+ ('010001', '010001'),
354
+ ('0x010001', '010001'),
355
+ (b'\x01\x00\x01', '010001'),
356
+
357
+ (0xFfF, '0fff'),
358
+ ('FfF', '0fff'),
359
+ ('0FfF', '0fff'),
360
+ ('0x0FfF', '0fff'),
361
+ (b'\x0F\xfF', '0fff'),
362
+
346
363
  (b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~',
347
- b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~'),
348
- (65537, s_exc.BadTypeValu),
364
+ 'd41d8cd98f00b204e9800998ecf8427e'),
365
+
366
+ ('01\udcfe0101', (s_exc.BadTypeValu, 'string argument should contain only ASCII characters')),
349
367
  ]
350
368
 
351
- for v, b in testvectors:
352
- if isinstance(b, bytes):
353
- r, subs = t.norm(v)
354
- self.isinstance(r, str)
369
+ for valu, expected in testvectors:
370
+ if isinstance(expected, str):
371
+ norm, subs = t.norm(valu)
372
+ self.isinstance(norm, str)
355
373
  self.eq(subs, {})
374
+ self.eq(norm, expected)
356
375
  else:
357
- self.raises(b, t.norm, v)
376
+ etype, mesg = expected
377
+ with self.raises(etype) as exc:
378
+ t.norm(valu)
379
+ self.eq(exc.exception.get('mesg'), mesg, f'{valu=}')
358
380
 
359
- # width = 4
381
+ # size = 4
360
382
  testvectors4 = [
361
- ('d41d', b'\xd4\x1d'),
362
- (b'\x10\x01', b'\x10\x01'),
363
- ('01', s_exc.BadTypeValu),
364
- ('010101', s_exc.BadTypeValu),
365
- (b'\x10\x01\xff', s_exc.BadTypeValu),
366
- (b'\xff', s_exc.BadTypeValu),
367
- ('01\udcfe0101', s_exc.BadTypeValu),
383
+ (0xc, (s_exc.BadTypeValu, 'Invalid width.')),
384
+ (-0xc, (s_exc.BadTypeValu, 'Invalid width.')),
385
+ ('0c', (s_exc.BadTypeValu, 'Invalid width.')),
386
+ ('0x0c', (s_exc.BadTypeValu, 'Invalid width.')),
387
+ (b'\x0c', (s_exc.BadTypeValu, 'Invalid width.')),
388
+
389
+ (0xd41d, 'd41d'),
390
+ ('d41d', 'd41d'),
391
+ ('0xd41d', 'd41d'),
392
+ (b'\xd4\x1d', 'd41d'),
393
+
394
+ (0x10001, (s_exc.BadTypeValu, 'Invalid width.')),
395
+ ('10001', (s_exc.BadTypeValu, 'Invalid width.')),
396
+ ('0x10001', (s_exc.BadTypeValu, 'Invalid width.')),
397
+ ('010001', (s_exc.BadTypeValu, 'Invalid width.')),
398
+ ('0x010001', (s_exc.BadTypeValu, 'Invalid width.')),
399
+ (b'\x01\x00\x01', (s_exc.BadTypeValu, 'Invalid width.')),
400
+
401
+ ('01\udcfe0101', (s_exc.BadTypeValu, 'string argument should contain only ASCII characters')),
368
402
  ]
369
403
  t = core.model.type('test:hex4')
370
- for v, b in testvectors4:
371
- if isinstance(b, bytes):
372
- r, subs = t.norm(v)
373
- self.isinstance(r, str)
404
+ for valu, expected in testvectors4:
405
+ if isinstance(expected, str):
406
+ norm, subs = t.norm(valu)
407
+ self.isinstance(norm, str)
374
408
  self.eq(subs, {})
409
+ self.eq(norm, expected)
375
410
  else:
376
- self.raises(b, t.norm, v)
411
+ etype, mesg = expected
412
+ with self.raises(etype) as exc:
413
+ t.norm(valu)
414
+ self.eq(exc.exception.get('mesg'), mesg, f'{valu=}')
377
415
 
378
416
  # size = 8, zeropad = True
379
417
  testvectors = [
@@ -384,29 +422,45 @@ class TypesTest(s_t_utils.SynTest):
384
422
  ('0X12345678', '12345678'),
385
423
  ('56:78', '00005678'),
386
424
  ('12:34:56:78', '12345678'),
387
- ('::', s_exc.BadTypeValu),
388
- ('0x::', s_exc.BadTypeValu),
389
- ('0x1234qwer', s_exc.BadTypeValu),
390
- ('0x123456789a', s_exc.BadTypeValu),
391
- ('::', s_exc.BadTypeValu),
425
+ (-1, 'ffffffff'),
426
+ (-0xff, 'ffffff01'),
427
+ (1234, '000004d2'),
428
+ (0x12345678, '12345678'),
429
+ (0x123456789a, (s_exc.BadTypeValu, 'Invalid width.')),
430
+ ('::', (s_exc.BadTypeValu, 'No string left after stripping.')),
431
+ ('0x::', (s_exc.BadTypeValu, 'No string left after stripping.')),
432
+ ('0x1234qwer', (s_exc.BadTypeValu, 'Non-hexadecimal digit found')),
433
+ ('0x123456789a', (s_exc.BadTypeValu, 'Invalid width.')),
392
434
  (b'\x12', '00000012'),
393
435
  (b'\x12\x34', '00001234'),
394
436
  (b'\x12\x34\x56', '00123456'),
395
437
  (b'\x12\x34\x56\x78', '12345678'),
396
- (b'\x12\x34\x56\x78\x9a', s_exc.BadTypeValu),
438
+ (b'\x12\x34\x56\x78\x9a', (s_exc.BadTypeValu, 'Invalid width.')),
397
439
  ]
398
440
  t = core.model.type('test:hexpad')
399
- for v, b in testvectors:
400
- if isinstance(b, (str, bytes)):
401
- r, subs = t.norm(v)
402
- self.isinstance(r, str)
441
+ for valu, expected in testvectors:
442
+ if isinstance(expected, str):
443
+ norm, subs = t.norm(valu)
444
+ self.isinstance(norm, str)
403
445
  self.eq(subs, {})
404
- self.eq(r, b)
446
+ self.eq(norm, expected)
405
447
  else:
406
- self.raises(b, t.norm, v)
448
+ etype, mesg = expected
449
+ with self.raises(etype) as exc:
450
+ t.norm(valu)
451
+ self.eq(exc.exception.get('mesg'), mesg, f'{valu=}')
407
452
 
408
453
  # zeropad = 20
409
454
  testvectors = [
455
+ (-1, 'ffffffffffffffffffff'),
456
+ (-0xff, 'ffffffffffffffffff01'),
457
+ (0x12, '00000000000000000012'),
458
+ (0x123, '00000000000000000123'),
459
+ (0x1234, '00000000000000001234'),
460
+ (0x123456, '00000000000000123456'),
461
+ (0x12345678, '00000000000012345678'),
462
+ (0x123456789abcdef123456789abcdef, '123456789abcdef123456789abcdef'),
463
+ (-0x123456789abcdef123456789abcdef, 'edcba9876543210edcba9876543211'),
410
464
  ('0x12', '00000000000000000012'),
411
465
  ('0x123', '00000000000000000123'),
412
466
  ('0x1234', '00000000000000001234'),
@@ -421,14 +475,17 @@ class TypesTest(s_t_utils.SynTest):
421
475
  (b'\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef', '123456789abcdef123456789abcdef'),
422
476
  ]
423
477
  t = core.model.type('test:zeropad')
424
- for v, b in testvectors:
425
- if isinstance(b, (str, bytes)):
426
- r, subs = t.norm(v)
427
- self.isinstance(r, str)
478
+ for valu, expected in testvectors:
479
+ if isinstance(expected, str):
480
+ norm, subs = t.norm(valu)
481
+ self.isinstance(norm, str)
428
482
  self.eq(subs, {})
429
- self.eq(r, b)
483
+ self.eq(norm, expected)
430
484
  else:
431
- self.raises(b, t.norm, v)
485
+ etype, mesg = expected
486
+ with self.raises(etype) as exc:
487
+ t.norm(valu)
488
+ self.eq(exc.exception.get('mesg'), mesg, f'{valu=}')
432
489
 
433
490
  # Do some node creation and lifting
434
491
  nodes = await core.nodes('[test:hexa="01:00 01"]')
@@ -436,8 +493,19 @@ class TypesTest(s_t_utils.SynTest):
436
493
  node = nodes[0]
437
494
  self.eq(node.ndef, ('test:hexa', '010001'))
438
495
  self.len(1, await core.nodes('test:hexa=010001'))
496
+ self.len(1, await core.nodes('test:hexa=(0x10001)'))
439
497
  self.len(1, await core.nodes('test:hexa=$byts', opts={'vars': {'byts': b'\x01\x00\x01'}}))
440
498
 
499
+ nodes = await core.nodes('[test:hexa=(-10)]')
500
+ self.len(1, nodes)
501
+ self.eq(nodes[0].repr(), 'f6')
502
+
503
+ self.len(1, await core.nodes('test:hexa=(-10)'))
504
+
505
+ with self.raises(s_exc.BadTypeValu) as exc:
506
+ await core.callStorm('test:hexa^=(-10)')
507
+ self.eq(exc.exception.get('mesg'), 'Hex prefix lift values must be str, not int.')
508
+
441
509
  # Do some fancy prefix searches for test:hexa
442
510
  valus = ['deadb33f',
443
511
  'deadb33fb33f',
@@ -467,9 +535,25 @@ class TypesTest(s_t_utils.SynTest):
467
535
  self.len(1, await core.nodes('[test:hexa=0xf00fb33b00000000]'))
468
536
  self.len(1, await core.nodes('test:hexa=0xf00fb33b00000000'))
469
537
  self.len(1, await core.nodes('test:hexa^=0xf00fb33b'))
538
+ self.len(1, await core.nodes('test:hexa^=0xf00fb33'))
539
+
540
+ with self.raises(s_exc.BadTypeValu):
541
+ await core.nodes('test:hexa^=(0xf00fb33b)')
542
+
543
+ with self.raises(s_exc.BadTypeValu):
544
+ await core.nodes('test:hexa^=(0xf00fb33)')
470
545
 
471
546
  # Check creating and lifting zeropadded hex types
472
- self.len(3, await core.nodes('[test:zeropad=11 test:zeropad=0x22 test:zeropad=111]'))
547
+ q = '''
548
+ [
549
+ test:zeropad=11
550
+ test:zeropad=0x22
551
+ test:zeropad=111
552
+ test:zeropad=(0x33)
553
+ test:zeropad=(0x444)
554
+ ]
555
+ '''
556
+ self.len(5, await core.nodes(q))
473
557
  self.len(1, await core.nodes('test:zeropad=0x11'))
474
558
  self.len(1, await core.nodes('test:zeropad=0x111'))
475
559
  self.len(1, await core.nodes('test:zeropad=000000000011'))
@@ -479,6 +563,14 @@ class TypesTest(s_t_utils.SynTest):
479
563
  self.len(1, await core.nodes('test:zeropad=000000000022'))
480
564
  self.len(1, await core.nodes('test:zeropad=00000000000000000022')) # len=20
481
565
  self.len(0, await core.nodes('test:zeropad=0000000000000000000022')) # len=22
566
+ self.len(1, await core.nodes('test:zeropad=(0x33)'))
567
+ self.len(1, await core.nodes('test:zeropad=000000000033'))
568
+ self.len(1, await core.nodes('test:zeropad=00000000000000000033')) # len=20
569
+ self.len(0, await core.nodes('test:zeropad=0000000000000000000033')) # len=22
570
+ self.len(1, await core.nodes('test:zeropad=(0x444)'))
571
+ self.len(1, await core.nodes('test:zeropad=000000000444'))
572
+ self.len(1, await core.nodes('test:zeropad=00000000000000000444')) # len=20
573
+ self.len(0, await core.nodes('test:zeropad=0000000000000000000444')) # len=22
482
574
 
483
575
  def test_int(self):
484
576
 
@@ -1228,7 +1320,7 @@ class TypesTest(s_t_utils.SynTest):
1228
1320
  nodes = await core.nodes('test:str:tick=201401*')
1229
1321
  self.eq({node.ndef[1] for node in nodes}, {'a'})
1230
1322
 
1231
- nodes = await core.nodes('test:str:tick*range=("-4200 days", now)')
1323
+ nodes = await core.nodes('test:str:tick*range=("-6000 days", now)')
1232
1324
  self.eq({node.ndef[1] for node in nodes}, {'a', 'b', 'c', 'd'})
1233
1325
 
1234
1326
  opts = {'vars': {'tick': tick, 'tock': tock}}
@@ -545,6 +545,14 @@ class CryptoModelTest(s_t_utils.SynTest):
545
545
  self.eq(nodes[0].get('identities:ipv4s'), (0x01020304, 0x05050505))
546
546
  self.eq(nodes[0].get('identities:ipv6s'), ('ff::11', 'ff::aa'))
547
547
 
548
+ nodes = await core.nodes('[ crypto:x509:cert=* :serial=(1234) ]')
549
+ self.len(1, nodes)
550
+ self.eq(nodes[0].get('serial'), '00000000000000000000000000000000000004d2')
551
+
552
+ nodes = await core.nodes('[ crypto:x509:cert=* :serial=(-1234) ]')
553
+ self.len(1, nodes)
554
+ self.eq(nodes[0].get('serial'), 'fffffffffffffffffffffffffffffffffffffb2e')
555
+
548
556
  nodes = await core.nodes('''
549
557
  [
550
558
  crypto:x509:crl=$crl
@@ -3002,17 +3002,20 @@ class InetModelTest(s_t_utils.SynTest):
3002
3002
  :url="https://v.vtx.lk/slack"
3003
3003
  :name="Synapse users slack"
3004
3004
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3005
+ :app={[ inet:service:app=({"id": "app00"}) ]}
3005
3006
  ]
3006
3007
  '''
3007
3008
  nodes = await core.nodes(q)
3008
3009
  self.len(1, nodes)
3009
3010
  self.nn(nodes[0].get('tenant'))
3011
+ self.nn(nodes[0].get('app'))
3010
3012
  self.eq(nodes[0].ndef, ('inet:service:instance', s_common.guid(('vertex', 'slack'))))
3011
3013
  self.eq(nodes[0].get('id'), 'T2XK1223Y')
3012
3014
  self.eq(nodes[0].get('platform'), platform.ndef[1])
3013
3015
  self.eq(nodes[0].get('url'), 'https://v.vtx.lk/slack')
3014
3016
  self.eq(nodes[0].get('name'), 'synapse users slack')
3015
3017
  platinst = nodes[0]
3018
+ app00 = nodes[0].get('app')
3016
3019
 
3017
3020
  q = '''
3018
3021
  [
@@ -3023,6 +3026,7 @@ class InetModelTest(s_t_utils.SynTest):
3023
3026
  :email=blackout@vertex.link
3024
3027
  :profile={ gen.ps.contact.email vertex.employee blackout@vertex.link }
3025
3028
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3029
+ :app={[ inet:service:app=({"id": "a001"}) ]}
3026
3030
  )
3027
3031
 
3028
3032
  (inet:service:account=(visi, account, vertex, slack)
@@ -3037,6 +3041,7 @@ class InetModelTest(s_t_utils.SynTest):
3037
3041
  self.len(2, accounts)
3038
3042
 
3039
3043
  self.nn(accounts[0].get('tenant'))
3044
+ self.nn(accounts[0].get('app'))
3040
3045
 
3041
3046
  profiles = await core.nodes('ps:contact')
3042
3047
  self.len(2, profiles)
@@ -3175,6 +3180,7 @@ class InetModelTest(s_t_utils.SynTest):
3175
3180
  :platform=$platiden
3176
3181
  :instance=$instiden
3177
3182
  :topic=' My Topic '
3183
+ :app={ inet:service:app:id=app00 }
3178
3184
  ]
3179
3185
  '''
3180
3186
  opts = {'vars': {
@@ -3185,6 +3191,7 @@ class InetModelTest(s_t_utils.SynTest):
3185
3191
  nodes = await core.nodes(q, opts=opts)
3186
3192
  self.len(1, nodes)
3187
3193
  self.eq(nodes[0].ndef, ('inet:service:channel', s_common.guid(('general', 'channel', 'vertex', 'slack'))))
3194
+ self.eq(nodes[0].get('app'), app00)
3188
3195
  self.eq(nodes[0].get('name'), 'general')
3189
3196
  self.eq(nodes[0].get('topic'), 'my topic')
3190
3197
  self.eq(nodes[0].get('period'), (1420070400000, 9223372036854775807))