synapse 2.178.0__py311-none-any.whl → 2.180.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 (62) hide show
  1. synapse/cortex.py +166 -31
  2. synapse/datamodel.py +47 -1
  3. synapse/exc.py +1 -0
  4. synapse/lib/aha.py +2 -1
  5. synapse/lib/ast.py +110 -76
  6. synapse/lib/base.py +12 -3
  7. synapse/lib/cell.py +150 -11
  8. synapse/lib/coro.py +14 -0
  9. synapse/lib/drive.py +551 -0
  10. synapse/lib/layer.py +1 -1
  11. synapse/lib/lmdbslab.py +2 -0
  12. synapse/lib/modelrev.py +5 -1
  13. synapse/lib/node.py +14 -4
  14. synapse/lib/schemas.py +97 -0
  15. synapse/lib/snap.py +36 -11
  16. synapse/lib/storm.py +9 -5
  17. synapse/lib/stormhttp.py +1 -0
  18. synapse/lib/stormlib/modelext.py +29 -3
  19. synapse/lib/stormlib/stix.py +44 -17
  20. synapse/lib/stormlib/vault.py +2 -2
  21. synapse/lib/stormtypes.py +1 -1
  22. synapse/lib/types.py +9 -0
  23. synapse/lib/version.py +2 -2
  24. synapse/lookup/pe.py +303 -38
  25. synapse/models/auth.py +2 -0
  26. synapse/models/dns.py +24 -1
  27. synapse/models/geopol.py +3 -0
  28. synapse/models/geospace.py +4 -1
  29. synapse/models/inet.py +1 -0
  30. synapse/models/infotech.py +135 -92
  31. synapse/models/person.py +5 -2
  32. synapse/models/telco.py +3 -0
  33. synapse/tests/test_cortex.py +45 -1
  34. synapse/tests/test_lib_aha.py +17 -0
  35. synapse/tests/test_lib_ast.py +231 -0
  36. synapse/tests/test_lib_cell.py +225 -0
  37. synapse/tests/test_lib_coro.py +12 -0
  38. synapse/tests/test_lib_layer.py +22 -0
  39. synapse/tests/test_lib_modelrev.py +7 -0
  40. synapse/tests/test_lib_node.py +12 -1
  41. synapse/tests/test_lib_storm.py +32 -7
  42. synapse/tests/test_lib_stormhttp.py +40 -0
  43. synapse/tests/test_lib_stormlib_modelext.py +55 -3
  44. synapse/tests/test_lib_stormlib_stix.py +15 -0
  45. synapse/tests/test_lib_stormlib_vault.py +11 -1
  46. synapse/tests/test_lib_stormtypes.py +5 -0
  47. synapse/tests/test_lib_types.py +9 -0
  48. synapse/tests/test_model_dns.py +8 -0
  49. synapse/tests/test_model_geopol.py +2 -0
  50. synapse/tests/test_model_geospace.py +3 -1
  51. synapse/tests/test_model_inet.py +10 -1
  52. synapse/tests/test_model_infotech.py +47 -0
  53. synapse/tests/test_model_person.py +2 -0
  54. synapse/tests/test_model_syn.py +11 -0
  55. synapse/tests/test_model_telco.py +2 -1
  56. synapse/tests/test_utils_stormcov.py +1 -1
  57. synapse/tools/changelog.py +28 -0
  58. {synapse-2.178.0.dist-info → synapse-2.180.0.dist-info}/METADATA +1 -1
  59. {synapse-2.178.0.dist-info → synapse-2.180.0.dist-info}/RECORD +62 -61
  60. {synapse-2.178.0.dist-info → synapse-2.180.0.dist-info}/WHEEL +1 -1
  61. {synapse-2.178.0.dist-info → synapse-2.180.0.dist-info}/LICENSE +0 -0
  62. {synapse-2.178.0.dist-info → synapse-2.180.0.dist-info}/top_level.txt +0 -0
@@ -692,6 +692,9 @@ class StormHttpTest(s_test.SynTest):
692
692
  tlscadir = s_common.gendir(dirn, 'cadir')
693
693
  cacertpath = shutil.copyfile(os.path.join(cadir, 'somelocalca.crt'), os.path.join(tlscadir, 'somelocalca.crt'))
694
694
 
695
+ with s_common.genfile(cacertpath) as fd:
696
+ ca_cert = fd.read().decode()
697
+
695
698
  pkey, cert = tdir.genUserCert('someuser', signas='somelocalca')
696
699
  user_pkey = tdir._pkeyToByts(pkey).decode()
697
700
  user_cert = tdir._certToByts(cert).decode()
@@ -835,3 +838,40 @@ class StormHttpTest(s_test.SynTest):
835
838
  ## bad cert
836
839
  sslopts['client_cert'] = 'not-gonna-work'
837
840
  await self.asyncraises(s_exc.BadArg, core.callStorm(q, opts=opts))
841
+
842
+ # Provide a CA certificate directly
843
+ async with self.getTestCore(dirn=dirn) as core:
844
+
845
+ sslctx = core.initSslCtx(certpath, pkeypath)
846
+ sslctx.load_verify_locations(cafile=cacertpath)
847
+
848
+ addr, port = await core.addHttpsPort(0, sslctx=sslctx)
849
+ root = await core.auth.getUserByName('root')
850
+ await root.setPasswd('root')
851
+
852
+ core.addHttpApi('/api/v0/test', s_test.HttpReflector, {'cell': core})
853
+
854
+ sslopts = {}
855
+
856
+ opts = {
857
+ 'vars': {
858
+ 'url': f'https://root:root@localhost:{port}/api/v0/test',
859
+ 'verify': True,
860
+ 'sslopts': sslopts,
861
+ },
862
+ }
863
+
864
+ q = 'return($lib.inet.http.get($url, ssl_verify=$verify, ssl_opts=$sslopts))'
865
+
866
+ size, sha256 = await core.callStorm('return($lib.bytes.put($lib.base64.decode(Zm9v)))')
867
+ opts['vars']['sha256'] = sha256
868
+
869
+ ## no cert provided
870
+ resp = await core.callStorm(q, opts=opts)
871
+ self.eq(-1, resp['code'])
872
+ self.isin('certificate verify failed', resp['reason'])
873
+
874
+ ## provide just the CA Certificate
875
+ sslopts['ca_cert'] = ca_cert
876
+ resp = await core.callStorm(q, opts=opts)
877
+ self.eq(200, resp['code'])
@@ -57,12 +57,24 @@ class StormtypesModelextTest(s_test.SynTest):
57
57
  model_defs = await core.callStorm('return ( $lib.model.ext.getExtModel() )')
58
58
  self.isinstance(model_defs, dict)
59
59
 
60
+ self.len(1, await core.nodes('_visi:int:tick'))
61
+ await core._delAllFormProp('_visi:int', 'tick', {})
62
+ self.len(0, await core.nodes('_visi:int:tick'))
63
+
64
+ self.len(1, await core.nodes('._woot'))
65
+ await core._delAllUnivProp('_woot', {})
66
+ self.len(0, await core.nodes('._woot'))
67
+
68
+ self.len(1, await core.nodes('#lol:score'))
69
+ await core._delAllTagProp('score', {})
70
+ self.len(0, await core.nodes('#lol:score'))
71
+
60
72
  await core.callStorm('_visi:int=10 test:int=1234 | delnode')
61
73
  await core.callStorm('''
62
- $lib.model.ext.delTagProp(score)
63
- $lib.model.ext.delUnivProp(_woot)
74
+ $lib.model.ext.delTagProp(score, force=(true))
75
+ $lib.model.ext.delUnivProp(_woot, force=(true))
64
76
  $lib.model.ext.delFormProp(_visi:int, tick)
65
- $lib.model.ext.delFormProp(test:int, _tick)
77
+ $lib.model.ext.delFormProp(test:int, _tick, force=(true))
66
78
  $lib.model.ext.delForm(_visi:int)
67
79
  $lib.model.ext.delEdge(inet:user, _copies, *)
68
80
  ''')
@@ -276,6 +288,46 @@ class StormtypesModelextTest(s_test.SynTest):
276
288
 
277
289
  self.nn(core.model.edge(('inet:user', '_copies', None)))
278
290
 
291
+ # Property values left behind in layers are cleanly removed
292
+ async with self.getTestCore() as core:
293
+ await core.callStorm('''
294
+ $typeinfo = ({})
295
+ $docinfo = ({"doc": "NEWP"})
296
+ $lib.model.ext.addUnivProp(_woot, (int, ({})), $docinfo)
297
+ $lib.model.ext.addTagProp(score, (int, ({})), $docinfo)
298
+ $lib.model.ext.addFormProp(test:int, _tick, (time, ({})), $docinfo)
299
+ ''')
300
+ fork = await core.callStorm('return ( $lib.view.get().fork().iden ) ')
301
+ nodes = await core.nodes('[test:int=1234 :_tick=2024 ._woot=1 +#hehe:score=10]')
302
+ self.len(1, nodes)
303
+ self.eq(nodes[0].get('._woot'), 1)
304
+
305
+ nodes = await core.nodes('test:int=1234 [:_tick=2023 ._woot=2 +#hehe:score=9]',
306
+ opts={'view': fork})
307
+ self.len(1, nodes)
308
+ self.eq(nodes[0].get('._woot'), 2)
309
+
310
+ self.len(0, await core.nodes('test:int | delnode'))
311
+
312
+ with self.raises(s_exc.CantDelUniv):
313
+ await core.callStorm('$lib.model.ext.delUnivProp(_woot)')
314
+ with self.raises(s_exc.CantDelProp):
315
+ await core.callStorm('$lib.model.ext.delFormProp(test:int, _tick)')
316
+ with self.raises(s_exc.CantDelProp):
317
+ await core.callStorm('$lib.model.ext.delTagProp(score)')
318
+
319
+ await core.callStorm('$lib.model.ext.delUnivProp(_woot, force=(true))')
320
+ await core.callStorm('$lib.model.ext.delFormProp(test:int, _tick, force=(true))')
321
+ await core.callStorm('$lib.model.ext.delTagProp(score, force=(true))')
322
+
323
+ nodes = await core.nodes('[test:int=1234]')
324
+ self.len(1, nodes)
325
+ self.none(nodes[0].get('._woot'))
326
+ self.none(nodes[0].get('_tick'))
327
+ nodes = await core.nodes('test:int=1234', opts={'view': fork})
328
+ self.none(nodes[0].get('._woot'))
329
+ self.none(nodes[0].get('_tick'))
330
+
279
331
  async def test_lib_stormlib_behold_modelext(self):
280
332
  self.skipIfNexusReplay()
281
333
  async with self.getTestCore() as core:
@@ -410,6 +410,21 @@ class StormLibStixTest(s_test.SynTest):
410
410
  self.len(1, [n for n in nodes if n[0][0] == 'it:cmd'])
411
411
  self.stormIsInWarn("STIX bundle ingest has no relationship definition for: ('threat-actor', 'gronks', 'threat-actor')", msgs)
412
412
 
413
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), newp)')
414
+ self.stormIsInErr('config must be a dictionary', msgs)
415
+
416
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"relationships": 5}))')
417
+ self.stormIsInErr('Error processing relationships', msgs)
418
+
419
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"bundle": 3}))')
420
+ self.stormIsInErr('bundle value must be a dictionary', msgs)
421
+
422
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"bundle": {"storm": 3}}))')
423
+ self.stormIsInErr('storm query must be a string', msgs)
424
+
425
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({"objects": 3}), ({}))')
426
+ self.stormIsInErr('data.objects must be array', msgs)
427
+
413
428
  async def test_stix_export_custom(self):
414
429
 
415
430
  async with self.getTestCore() as core:
@@ -159,10 +159,13 @@ class StormlibVaultTest(s_test.SynTest):
159
159
 
160
160
  # Rename vault
161
161
  opts = {'vars': {'giden': giden}}
162
+ self.eq('gvault', await core.callStorm('return($lib.vault.get($giden).name)', opts=opts))
162
163
  q = '$lib.vault.get($giden).name = foobar'
163
164
  await core.callStorm(q, opts=opts)
164
165
  vault = core.getVault(giden)
165
166
  self.eq(vault.get('name'), 'foobar')
167
+ self.nn(await core.callStorm('return($lib.vault.byname(foobar))'))
168
+ await self.asyncraises(s_exc.NoSuchName, core.callStorm('return($lib.vault.byname(gvault))'))
166
169
 
167
170
  # Get secrets without EDIT perms
168
171
  opts = {'vars': {'giden': giden}, 'user': visi1.iden}
@@ -222,10 +225,17 @@ class StormlibVaultTest(s_test.SynTest):
222
225
  q = '$vault = $lib.vault.get($giden) return($vault.setPerm($iden, $lib.auth.easyperm.level.deny))'
223
226
  self.true(await core.callStorm(q, opts=opts))
224
227
 
225
- # List vaults again
226
228
  opts = {'user': visi1.iden}
227
229
  self.eq(0, await core.callStorm('return($lib.len($lib.vault.list()))', opts=opts))
228
230
 
231
+ # Remove permission on global vault
232
+ opts = {'vars': {'iden': visi1.iden, 'giden': giden}}
233
+ q = '$vault = $lib.vault.get($giden) return($vault.setPerm($iden, $lib.null))'
234
+ self.true(await core.callStorm(q, opts=opts))
235
+
236
+ opts = {'user': visi1.iden}
237
+ self.eq(1, await core.callStorm('return($lib.len($lib.vault.list()))', opts=opts))
238
+
229
239
  # Runtime asroot
230
240
 
231
241
  await core.addStormPkg({
@@ -6388,6 +6388,11 @@ words\tword\twrd'''
6388
6388
  self.eq(1, await core.callStorm('return($lib.math.number(1.23).toint())'))
6389
6389
  self.eq(2, await core.callStorm('return($lib.math.number(1.23).toint(rounding=ROUND_UP))'))
6390
6390
 
6391
+ with self.raises(s_exc.BadCast):
6392
+ await core.callStorm('return($lib.math.number((null)))')
6393
+ with self.raises(s_exc.BadCast):
6394
+ await core.callStorm('return($lib.math.number(newp))')
6395
+
6391
6396
  with self.raises(s_exc.StormRuntimeError):
6392
6397
  await core.callStorm('return($lib.math.number(1.23).toint(rounding=NEWP))')
6393
6398
 
@@ -1606,3 +1606,12 @@ class TypesTest(s_t_utils.SynTest):
1606
1606
 
1607
1607
  core.getLayer()._testAddPropArrayIndx(buid, 'test:int', '_hehe', ('newp' * 100,))
1608
1608
  self.len(0, await core.nodes('test:int:_hehe*[~=newp]'))
1609
+
1610
+ async def test_types_typehash(self):
1611
+ async with self.getTestCore() as core:
1612
+ self.true(core.model.form('inet:fqdn').type.typehash is core.model.prop('inet:dns:a:fqdn').type.typehash)
1613
+ self.true(core.model.form('it:prod:softname').type.typehash is core.model.prop('it:network:name').type.typehash)
1614
+ self.true(core.model.form('inet:asn').type.typehash is not core.model.prop('inet:proto:port').type.typehash)
1615
+
1616
+ self.true(s_common.isguid(core.model.form('inet:fqdn').type.typehash))
1617
+ self.true(s_common.isguid(core.model.form('inet:fqdn').typehash))
@@ -108,6 +108,14 @@ class DnsModelTest(s_t_utils.SynTest):
108
108
  self.eq(node.get('name:fqdn'), 'vertex.link')
109
109
  self.eq(node.get('type'), 255)
110
110
 
111
+ nodes = await core.nodes('[inet:dns:request=* :reply:code=NXDOMAIN]')
112
+ self.eq(nodes[0].get('reply:code'), 3)
113
+ self.eq(nodes[0].repr('reply:code'), 'NXDOMAIN')
114
+
115
+ nodes = await core.nodes('[inet:dns:request=* :reply:code=1138]')
116
+ self.eq(nodes[0].get('reply:code'), 1138)
117
+ self.eq(nodes[0].repr('reply:code'), '1138')
118
+
111
119
  nodes = await core.nodes('[inet:dns:query=("tcp://1.2.3.4", 4.3.2.1.in-addr.arpa, 255)]')
112
120
  self.len(1, nodes)
113
121
  node = nodes[0]
@@ -108,6 +108,7 @@ class GeoPolModelTest(s_t_utils.SynTest):
108
108
 
109
109
  nodes = await core.nodes('''
110
110
  [ pol:candidate=*
111
+ :id=" P00009423"
111
112
  :race={pol:race}
112
113
  :contact={[ps:contact=* :name=whippit]}
113
114
  :winner=$lib.true
@@ -116,6 +117,7 @@ class GeoPolModelTest(s_t_utils.SynTest):
116
117
  ]
117
118
  ''')
118
119
  self.eq(1, nodes[0].get('winner'))
120
+ self.eq('P00009423', nodes[0].get('id'))
119
121
  self.len(1, await core.nodes('pol:candidate -> pol:race'))
120
122
  self.len(1, await core.nodes('pol:candidate -> ou:org +:name=vertex'))
121
123
  self.len(1, await core.nodes('pol:candidate -> ps:contact +:name=whippit'))
@@ -2,7 +2,6 @@ import synapse.exc as s_exc
2
2
  import synapse.common as s_common
3
3
 
4
4
  import synapse.tests.utils as s_t_utils
5
- from synapse.tests.utils import alist
6
5
 
7
6
  import synapse.lib.module as s_module
8
7
 
@@ -499,6 +498,7 @@ class GeoTest(s_t_utils.SynTest):
499
498
  :place:name=woot
500
499
  :place={[geo:place=* :name=woot]}
501
500
  :accuracy=10m
501
+ :node=(test:int, 1234)
502
502
  ]
503
503
  ''')
504
504
  self.eq(1655510400000, nodes[0].get('time'))
@@ -507,6 +507,8 @@ class GeoTest(s_t_utils.SynTest):
507
507
  self.eq('woot', nodes[0].get('place:name'))
508
508
  self.eq(10000, nodes[0].get('accuracy'))
509
509
  self.len(1, await core.nodes('geo:telem -> geo:place +:name=woot'))
510
+ self.eq(('test:int', 1234), nodes[0].get('node'))
511
+ self.len(1, await core.nodes('test:int=1234'))
510
512
 
511
513
  async def test_model_geospace_area(self):
512
514
 
@@ -2684,7 +2684,15 @@ class InetModelTest(s_t_utils.SynTest):
2684
2684
  'acct': 'vertex.link/visi',
2685
2685
  }
2686
2686
 
2687
- q = '[inet:search:query=$valu :time=$p.time :text=$p.text :engine=$p.engine :acct=$p.acct :host=$p.host]'
2687
+ q = '''[
2688
+ inet:search:query=$valu
2689
+ :time=$p.time
2690
+ :text=$p.text
2691
+ :engine=$p.engine
2692
+ :acct=$p.acct
2693
+ :host=$p.host
2694
+ :account=*
2695
+ ]'''
2688
2696
  nodes = await core.nodes(q, opts={'vars': {'valu': iden, 'p': props}})
2689
2697
  self.len(1, nodes)
2690
2698
  node = nodes[0]
@@ -2694,6 +2702,7 @@ class InetModelTest(s_t_utils.SynTest):
2694
2702
  self.eq(node.get('engine'), 'roofroof')
2695
2703
  self.eq(node.get('host'), host)
2696
2704
  self.eq(node.get('acct'), ('vertex.link', 'visi'))
2705
+ self.len(1, await core.nodes('inet:search:query :account -> inet:service:account'))
2697
2706
 
2698
2707
  residen = s_common.guid()
2699
2708
  props = {
@@ -407,6 +407,35 @@ class InfotechModelTest(s_t_utils.SynTest):
407
407
  self.eq(nodes[0].get('net6'), ('fe80::', 'fe80::ffff:ffff:ffff:ffff'))
408
408
  self.eq(nodes[0].get('type'), 'virtual.sdn.')
409
409
 
410
+ nodes = await core.nodes('''[
411
+ it:sec:stix:indicator=*
412
+ :id=zoinks
413
+ :name=woot
414
+ :confidence=90
415
+ :revoked=(false)
416
+ :description="my neato indicator"
417
+ :pattern="some rule text"
418
+ :pattern_type=yara
419
+ :created=20240815
420
+ :updated=20240815
421
+ :labels=(hehe, haha)
422
+ :valid_from=20240815
423
+ :valid_until=20240815
424
+ ]''')
425
+ self.len(1, nodes)
426
+ self.eq('zoinks', nodes[0].get('id'))
427
+ self.eq('woot', nodes[0].get('name'))
428
+ self.eq(90, nodes[0].get('confidence'))
429
+ self.eq(False, nodes[0].get('revoked'))
430
+ self.eq('my neato indicator', nodes[0].get('description'))
431
+ self.eq('some rule text', nodes[0].get('pattern'))
432
+ self.eq('yara', nodes[0].get('pattern_type'))
433
+ self.eq(('haha', 'hehe'), nodes[0].get('labels'))
434
+ self.eq(1723680000000, nodes[0].get('created'))
435
+ self.eq(1723680000000, nodes[0].get('updated'))
436
+ self.eq(1723680000000, nodes[0].get('valid_from'))
437
+ self.eq(1723680000000, nodes[0].get('valid_until'))
438
+
410
439
  async def test_infotech_ios(self):
411
440
 
412
441
  async with self.getTestCore() as core:
@@ -1553,6 +1582,24 @@ class InfotechModelTest(s_t_utils.SynTest):
1553
1582
  self.eq(rule, nodes[0].get('rule'))
1554
1583
  self.eq(0x10000200003, nodes[0].get('version'))
1555
1584
 
1585
+ nodes = await core.nodes('''[
1586
+ (it:app:yara:netmatch=* :node=(inet:fqdn, foo.com))
1587
+ (it:app:yara:netmatch=* :node=(inet:ipv4, 1.2.3.4))
1588
+ (it:app:yara:netmatch=* :node=(inet:ipv6, "::ffff"))
1589
+ (it:app:yara:netmatch=* :node=(inet:url, "http://foo.com"))
1590
+ :rule=$rule
1591
+ :version=1.2.3
1592
+ ]''', opts=opts)
1593
+ self.len(4, nodes)
1594
+ for node in nodes:
1595
+ self.nn(node.get('node'))
1596
+ self.nn(node.get('version'))
1597
+
1598
+ self.len(4, await core.nodes('it:app:yara:rule=$rule -> it:app:yara:netmatch', opts=opts))
1599
+
1600
+ with self.raises(s_exc.BadTypeValu):
1601
+ await core.nodes('[it:app:yara:netmatch=* :node=(it:dev:str, foo)]')
1602
+
1556
1603
  async def test_it_app_snort(self):
1557
1604
 
1558
1605
  async with self.getTestCore() as core:
@@ -165,6 +165,7 @@ class PsModelTest(s_t_utils.SynTest):
165
165
  :birth:place:name=$p."birth:place:name"
166
166
  :death:place=$p."death:place" :death:place:loc=$p."death:place:loc"
167
167
  :death:place:name=$p."death:place:name"
168
+ :service:accounts=(*, *)
168
169
  )]'''
169
170
  nodes = await core.nodes(q, opts=opts)
170
171
  self.len(1, nodes)
@@ -208,6 +209,7 @@ class PsModelTest(s_t_utils.SynTest):
208
209
  self.eq(node.get('death:place:name'), 'reston, va, usa, earth, sol, milkyway')
209
210
  self.len(1, await core.nodes('ps:contact :birth:place -> geo:place'))
210
211
  self.len(1, await core.nodes('ps:contact :death:place -> geo:place'))
212
+ self.len(2, await core.nodes('ps:contact :service:accounts -> inet:service:account'))
211
213
 
212
214
  nodes = await core.nodes('''[
213
215
  ps:achievement=*
@@ -340,11 +340,22 @@ class SynModelTest(s_t_utils.SynTest):
340
340
  nodes = await core.nodes(f'syn:trigger={iden}')
341
341
  self.len(1, nodes)
342
342
 
343
+ indx = await core.getNexsIndx()
344
+
343
345
  # set the trigger doc
344
346
  nodes = await core.nodes(f'syn:trigger={iden} [ :doc=hehe ]')
345
347
  self.len(1, nodes)
346
348
  self.eq('hehe', nodes[0].get('doc'))
347
349
 
350
+ self.eq(await core.getNexsIndx(), indx + 1)
351
+
352
+ # set the trigger name
353
+ nodes = await core.nodes(f'syn:trigger={iden} [ :name=trigname ]')
354
+ self.len(1, nodes)
355
+ self.eq('trigname', nodes[0].get('name'))
356
+
357
+ self.eq(await core.getNexsIndx(), indx + 2)
358
+
348
359
  # Trigger reloads and make some more triggers to play with
349
360
  tdef = {'cond': 'prop:set', 'prop': 'inet:ipv4:asn', 'storm': '[inet:user=1] | testcmd'}
350
361
  await core.view.addTrigger(tdef)
@@ -112,7 +112,7 @@ class TelcoModelTest(s_t_utils.SynTest):
112
112
  q = '''[(tel:mob:telem=$valu :time=$p.time :latlong=$p.latlong :place=$p.place :host=$p.host
113
113
  :loc=$p.loc :accuracy=$p.accuracy :cell=$p.cell :imsi=$p.imsi :imei=$p.imei :phone=$p.phone
114
114
  :mac=$p.mac :ipv4=$p.ipv4 :ipv6=$p.ipv6 :wifi=$p.wifi :adid=$p.adid :aaid=$p.aaid :idfa=$p.idfa
115
- :name=$p.name :email=$p.email :acct=$p.acct :app=$p.app :data=$p.data)]'''
115
+ :name=$p.name :email=$p.email :acct=$p.acct :app=$p.app :data=$p.data :account=*)]'''
116
116
  nodes = await core.nodes(q, opts={'vars': {'valu': guid, 'p': props}})
117
117
  self.len(1, nodes)
118
118
  node = nodes[0]
@@ -142,6 +142,7 @@ class TelcoModelTest(s_t_utils.SynTest):
142
142
  self.eq(node.get('acct'), ('vertex.link', 'clown'))
143
143
  self.eq(node.get('app'), softguid)
144
144
  self.eq(node.get('data'), {'some key': 'some valu', 'BEEP': 1})
145
+ self.len(1, await core.nodes('tel:mob:telem :account -> inet:service:account'))
145
146
 
146
147
  async def test_telco_imei(self):
147
148
  async with self.getTestCore() as core:
@@ -78,7 +78,7 @@ class TestUtilsStormcov(s_utils.SynTest):
78
78
  await core.stormlist(s_files.getAssetStr('stormcov/lookup.storm'), opts={'mode': 'lookup'})
79
79
 
80
80
  orig = s_snap.Snap.nodesByPropValu
81
- async def nodesByPropValu(self, full, cmpr, valu):
81
+ async def nodesByPropValu(self, full, cmpr, valu, norm=True):
82
82
  frame = inspect.currentframe()
83
83
  if pivotracer.dynamic_source_filename(None, frame) is not None:
84
84
  assert (2, 2) == pivotracer.line_number_range(frame)
@@ -109,6 +109,8 @@ class ModelDiffer:
109
109
  changes['new_edges'] = {k: _curv.get(k) for k in new_edges}
110
110
 
111
111
  updated_edges = collections.defaultdict(dict)
112
+ deprecated_edges = {}
113
+
112
114
  for edge, curinfo in _curv.items():
113
115
  if edge in new_edges:
114
116
  continue
@@ -116,12 +118,19 @@ class ModelDiffer:
116
118
  if curinfo == oldinfo:
117
119
  continue
118
120
 
121
+ if curinfo.get('deprecated') and not oldinfo.get('deprecated'):
122
+ deprecated_edges[edge] = curinfo
123
+ continue
124
+
119
125
  # TODO - Support changes to the edges?
120
126
  assert False, f'A change was found for the edge: {edge}'
121
127
 
122
128
  if updated_edges:
123
129
  changes['updated_edges'] = dict(updated_edges)
124
130
 
131
+ if deprecated_edges:
132
+ changes['deprecated_edges'] = deprecated_edges
133
+
125
134
  return changes
126
135
 
127
136
  def _compareForms(self, curv, oldv, outp: s_output.OutPut) -> dict:
@@ -723,6 +732,25 @@ def _gen_model_rst(version, model_ref, changes, current_model, outp: s_output.Ou
723
732
  ]
724
733
  rst.addLines(*lines)
725
734
 
735
+ if dep_edges := changes.get('edges').get('deprecated_edges'):
736
+
737
+ rst.addHead('Deprecated Edges', lvl=1)
738
+ for (n1, name, n2), info in dep_edges.items():
739
+ if n1 is not None and n2 is not None:
740
+ mesg = f'''The edge has been deprecated when used with a ``{n1}`` and an ``{n2}`` node. {info.get('doc')}'''
741
+ elif n1 is None and n2 is not None:
742
+ mesg = f'''The edge has been deprecated when used with a ``{n2}`` target node. {info.get('doc')}'''
743
+ elif n1 is not None and n2 is None:
744
+ mesg = f'''The edge has been deprecated when used with a ``{n1}`` node. {info.get('doc')}'''
745
+ else:
746
+ mesg = f'''The edge has been deprecated. {info.get('doc')}'''
747
+
748
+ rst.addLines(
749
+ f'``{name}``',
750
+ *textwrap.wrap(mesg, initial_indent=' ', subsequent_indent=' ', width=width),
751
+ '\n',
752
+ )
753
+
726
754
  return rst
727
755
 
728
756
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synapse
3
- Version: 2.178.0
3
+ Version: 2.180.0
4
4
  Summary: Synapse Intelligence Analysis Framework
5
5
  Author-email: The Vertex Project LLC <root@vertex.link>
6
6
  License: Apache License 2.0