synapse 2.174.0__py311-none-any.whl → 2.175.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.

synapse/lib/storm.py CHANGED
@@ -14,6 +14,7 @@ import synapse.datamodel as s_datamodel
14
14
  import synapse.lib.ast as s_ast
15
15
  import synapse.lib.base as s_base
16
16
  import synapse.lib.chop as s_chop
17
+ import synapse.lib.coro as s_coro
17
18
  import synapse.lib.node as s_node
18
19
  import synapse.lib.snap as s_snap
19
20
  import synapse.lib.time as s_time
@@ -3491,13 +3492,18 @@ class DiffCmd(Cmd):
3491
3492
  // Lift the nodes with the tag #cno.mal.redtree added in the top layer.
3492
3493
 
3493
3494
  diff --tag cno.mal.redtree
3495
+
3496
+ // Lift nodes by multiple tags (results are uniqued)
3497
+
3498
+ diff --tag cno.mal.redtree rep.vt
3494
3499
  '''
3495
3500
  name = 'diff'
3496
3501
  readonly = True
3497
3502
 
3498
3503
  def getArgParser(self):
3499
3504
  pars = Cmd.getArgParser(self)
3500
- pars.add_argument('--tag', default=None, help='Lift only nodes with the given tag in the top layer.')
3505
+ pars.add_argument('--tag', default=None, nargs='*',
3506
+ help='Lift only nodes with the given tag (or tags) in the top layer.')
3501
3507
  pars.add_argument('--prop', default=None, help='Lift nodes with changes to the given property the top layer.')
3502
3508
  return pars
3503
3509
 
@@ -3516,10 +3522,11 @@ class DiffCmd(Cmd):
3516
3522
 
3517
3523
  if self.opts.tag:
3518
3524
 
3519
- tagname = await s_stormtypes.tostr(self.opts.tag)
3525
+ tagnames = [await s_stormtypes.tostr(tag) for tag in self.opts.tag]
3520
3526
 
3521
3527
  layr = runt.snap.view.layers[0]
3522
- async for _, buid, sode in layr.liftByTag(tagname):
3528
+
3529
+ async for _, buid, sode in layr.liftByTags(tagnames):
3523
3530
  node = await self.runt.snap._joinStorNode(buid, {layr.iden: sode})
3524
3531
  if node is not None:
3525
3532
  yield node, runt.initPath(node)
@@ -3765,73 +3772,76 @@ class MergeCmd(Cmd):
3765
3772
  def _getPropFilter(self):
3766
3773
  if self.opts.include_props:
3767
3774
 
3775
+ _include_props = set(self.opts.include_props)
3776
+
3768
3777
  def propfilter(prop):
3769
- if prop in self.opts.include_props:
3770
- return False
3771
- return True
3778
+ return prop not in _include_props
3772
3779
 
3773
3780
  return propfilter
3774
3781
 
3775
3782
  if self.opts.exclude_props:
3776
3783
 
3784
+ _exclude_props = set(self.opts.exclude_props)
3785
+
3777
3786
  def propfilter(prop):
3778
- if prop in self.opts.exclude_props:
3779
- return True
3780
- return False
3787
+ return prop in _exclude_props
3781
3788
 
3782
3789
  return propfilter
3783
3790
 
3784
3791
  return None
3785
3792
 
3786
- async def _checkNodePerms(self, node, sode, runt):
3793
+ async def _checkNodePerms(self, node, sode, runt, allows):
3787
3794
 
3788
3795
  layr0 = runt.snap.view.layers[0].iden
3789
3796
  layr1 = runt.snap.view.layers[1].iden
3790
3797
 
3791
- if sode.get('valu') is not None:
3798
+ if not allows['forms'] and sode.get('valu') is not None:
3792
3799
  runt.confirm(('node', 'del', node.form.name), gateiden=layr0)
3793
3800
  runt.confirm(('node', 'add', node.form.name), gateiden=layr1)
3794
3801
 
3795
- for name, (valu, stortype) in sode.get('props', {}).items():
3796
- prop = node.form.prop(name)
3797
- runt.confirmPropDel(prop, layriden=layr0)
3798
- runt.confirmPropSet(prop, layriden=layr1)
3802
+ if not allows['props']:
3803
+ for name in sode.get('props', {}).keys():
3804
+ prop = node.form.prop(name)
3805
+ runt.confirmPropDel(prop, layriden=layr0)
3806
+ runt.confirmPropSet(prop, layriden=layr1)
3807
+
3808
+ if not allows['tags']:
3809
+
3810
+ tags = []
3811
+ tagadds = []
3812
+ for tag, valu in sode.get('tags', {}).items():
3813
+ if valu != (None, None):
3814
+ tagadds.append(tag)
3815
+ tagperm = tuple(tag.split('.'))
3816
+ runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3817
+ runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3818
+ else:
3819
+ tags.append((len(tag), tag))
3820
+
3821
+ for _, tag in sorted(tags, reverse=True):
3822
+ look = tag + '.'
3823
+ if any([tagadd.startswith(look) for tagadd in tagadds]):
3824
+ continue
3799
3825
 
3800
- tags = []
3801
- tagadds = []
3802
- for tag, valu in sode.get('tags', {}).items():
3803
- if valu != (None, None):
3804
3826
  tagadds.append(tag)
3805
3827
  tagperm = tuple(tag.split('.'))
3806
3828
  runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3807
3829
  runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3808
- else:
3809
- tags.append((len(tag), tag))
3810
-
3811
- for _, tag in sorted(tags, reverse=True):
3812
- look = tag + '.'
3813
- if any([tagadd.startswith(look) for tagadd in tagadds]):
3814
- continue
3815
-
3816
- tagadds.append(tag)
3817
- tagperm = tuple(tag.split('.'))
3818
- runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3819
- runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3820
3830
 
3821
- for tag, tagdict in sode.get('tagprops', {}).items():
3822
- for prop, (valu, stortype) in tagdict.items():
3831
+ for tag in sode.get('tagprops', {}).keys():
3823
3832
  tagperm = tuple(tag.split('.'))
3824
3833
  runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3825
3834
  runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3826
3835
 
3827
- async for name in runt.snap.view.layers[0].iterNodeDataKeys(node.buid):
3828
- runt.confirm(('node', 'data', 'pop', name), gateiden=layr0)
3829
- runt.confirm(('node', 'data', 'set', name), gateiden=layr1)
3836
+ if not allows['ndata']:
3837
+ async for name in runt.snap.view.layers[0].iterNodeDataKeys(node.buid):
3838
+ runt.confirm(('node', 'data', 'pop', name), gateiden=layr0)
3839
+ runt.confirm(('node', 'data', 'set', name), gateiden=layr1)
3830
3840
 
3831
- async for edge in runt.snap.view.layers[0].iterNodeEdgesN1(node.buid):
3832
- verb = edge[0]
3833
- runt.confirm(('node', 'edge', 'del', verb), gateiden=layr0)
3834
- runt.confirm(('node', 'edge', 'add', verb), gateiden=layr1)
3841
+ if not allows['edges']:
3842
+ async for verb in runt.snap.view.layers[0].iterNodeEdgeVerbsN1(node.buid):
3843
+ runt.confirm(('node', 'edge', 'del', verb), gateiden=layr0)
3844
+ runt.confirm(('node', 'edge', 'add', verb), gateiden=layr1)
3835
3845
 
3836
3846
  async def execStormCmd(self, runt, genr):
3837
3847
 
@@ -3841,12 +3851,31 @@ class MergeCmd(Cmd):
3841
3851
 
3842
3852
  notags = self.opts.no_tags
3843
3853
  onlytags = self.opts.only_tags
3854
+ doapply = self.opts.apply
3844
3855
 
3845
3856
  tagfilter = self._getTagFilter()
3846
3857
  propfilter = self._getPropFilter()
3847
3858
 
3848
- layr0 = runt.snap.view.layers[0].iden
3849
- layr1 = runt.snap.view.layers[1].iden
3859
+ layr0 = runt.snap.view.layers[0]
3860
+ layr1 = runt.snap.view.layers[1]
3861
+
3862
+ doperms = doapply and not (runt.isAdmin(gateiden=layr0.iden) and runt.isAdmin(gateiden=layr1.iden))
3863
+
3864
+ if doperms:
3865
+ allows = {
3866
+ 'forms': runt.user.allowed(('node', 'del'), gateiden=layr0.iden, deepdeny=True) and
3867
+ runt.user.allowed(('node', 'add'), gateiden=layr1.iden, deepdeny=True),
3868
+ 'props': runt.user.allowed(('node', 'prop', 'del'), gateiden=layr0.iden, deepdeny=True) and
3869
+ runt.user.allowed(('node', 'prop', 'set'), gateiden=layr1.iden, deepdeny=True),
3870
+ 'tags': runt.user.allowed(('node', 'tag', 'del'), gateiden=layr0.iden, deepdeny=True) and
3871
+ runt.user.allowed(('node', 'tag', 'add'), gateiden=layr1.iden, deepdeny=True),
3872
+ 'ndata': runt.user.allowed(('node', 'data', 'pop'), gateiden=layr0.iden, deepdeny=True) and
3873
+ runt.user.allowed(('node', 'data', 'set'), gateiden=layr1.iden, deepdeny=True),
3874
+ 'edges': runt.user.allowed(('node', 'edge', 'del'), gateiden=layr0.iden, deepdeny=True) and
3875
+ runt.user.allowed(('node', 'edge', 'add'), gateiden=layr1.iden, deepdeny=True),
3876
+ }
3877
+
3878
+ doperms = not all(allows.values())
3850
3879
 
3851
3880
  if self.opts.diff:
3852
3881
 
@@ -3854,7 +3883,7 @@ class MergeCmd(Cmd):
3854
3883
  yield node, path
3855
3884
 
3856
3885
  async def diffgenr():
3857
- async for buid, sode in runt.snap.view.layers[0].getStorNodes():
3886
+ async for buid, sode in layr0.getStorNodes():
3858
3887
  node = await runt.snap.getNodeByBuid(buid)
3859
3888
  if node is not None:
3860
3889
  yield node, runt.initPath(node)
@@ -3873,33 +3902,19 @@ class MergeCmd(Cmd):
3873
3902
  sodes = await node.getStorNodes()
3874
3903
  sode = sodes[0]
3875
3904
 
3876
- if self.opts.apply:
3905
+ if doapply:
3877
3906
  editor = s_snap.SnapEditor(snap)
3878
3907
 
3879
3908
  subs = []
3880
3909
 
3881
- async def sync():
3882
-
3883
- if not self.opts.apply:
3884
- subs.clear()
3885
- return
3886
-
3887
- addedits = editor.getNodeEdits()
3888
- if addedits:
3889
- await runt.snap.view.parent.storNodeEdits(addedits, meta=meta)
3890
-
3891
- if subs:
3892
- subedits = [(node.buid, node.form.name, subs)]
3893
- await runt.snap.view.storNodeEdits(subedits, meta=meta)
3894
- subs.clear()
3895
-
3896
3910
  # check all node perms first
3897
- if self.opts.apply:
3898
- await self._checkNodePerms(node, sode, runt)
3911
+ if doperms:
3912
+ await self._checkNodePerms(node, sode, runt, allows)
3899
3913
 
3900
3914
  form = node.form.name
3901
3915
  if form == 'syn:tag':
3902
3916
  if notags:
3917
+ await asyncio.sleep(0)
3903
3918
  continue
3904
3919
  else:
3905
3920
  # avoid merging a tag if the node won't exist below us
@@ -3908,6 +3923,7 @@ class MergeCmd(Cmd):
3908
3923
  if undr.get('valu') is not None:
3909
3924
  break
3910
3925
  else:
3926
+ await asyncio.sleep(0)
3911
3927
  continue
3912
3928
 
3913
3929
  protonode = None
@@ -3916,23 +3932,24 @@ class MergeCmd(Cmd):
3916
3932
  valu = sode.get('valu')
3917
3933
  if valu is not None:
3918
3934
 
3919
- if tagfilter and form == 'syn:tag' and tagfilter(valu[0]):
3935
+ if tagfilter is not None and form == 'syn:tag' and tagfilter(valu[0]):
3936
+ await asyncio.sleep(0)
3920
3937
  continue
3921
3938
 
3922
- if not self.opts.apply:
3939
+ if not doapply:
3923
3940
  valurepr = node.form.type.repr(valu[0])
3924
3941
  await runt.printf(f'{nodeiden} {form} = {valurepr}')
3925
3942
  else:
3926
3943
  delnode = True
3927
3944
  protonode = await editor.addNode(form, valu[0])
3928
3945
 
3929
- elif self.opts.apply:
3946
+ elif doapply:
3930
3947
  protonode = await editor.addNode(form, node.ndef[1], norminfo={})
3931
3948
 
3932
3949
  for name, (valu, stortype) in sode.get('props', {}).items():
3933
3950
 
3934
3951
  prop = node.form.prop(name)
3935
- if propfilter:
3952
+ if propfilter is not None:
3936
3953
  if name[0] == '.':
3937
3954
  if propfilter(name):
3938
3955
  continue
@@ -3942,7 +3959,7 @@ class MergeCmd(Cmd):
3942
3959
 
3943
3960
  if prop.info.get('ro'):
3944
3961
  if name == '.created':
3945
- if self.opts.apply:
3962
+ if doapply:
3946
3963
  protonode.props['.created'] = valu
3947
3964
  subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
3948
3965
  continue
@@ -3952,8 +3969,8 @@ class MergeCmd(Cmd):
3952
3969
  props = undr.get('props')
3953
3970
  if props is not None:
3954
3971
  curv = props.get(name)
3955
- if curv is not None and curv[0] != valu:
3956
- isset = True
3972
+ if curv is not None:
3973
+ isset = curv[0] != valu
3957
3974
  break
3958
3975
 
3959
3976
  if isset:
@@ -3963,24 +3980,23 @@ class MergeCmd(Cmd):
3963
3980
  await runt.snap.warn(mesg)
3964
3981
  continue
3965
3982
 
3966
- if not self.opts.apply:
3983
+ if not doapply:
3967
3984
  valurepr = prop.type.repr(valu)
3968
3985
  await runt.printf(f'{nodeiden} {form}:{name} = {valurepr}')
3969
3986
  else:
3970
3987
  await protonode.set(name, valu)
3971
3988
  subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
3972
3989
 
3973
- if self.opts.apply and protonode is None:
3990
+ if doapply and protonode is None:
3974
3991
  protonode = await editor.addNode(form, node.ndef[1], norminfo={})
3975
3992
 
3976
3993
  if not notags:
3977
3994
  for tag, valu in sode.get('tags', {}).items():
3978
3995
 
3979
- if tagfilter and tagfilter(tag):
3996
+ if tagfilter is not None and tagfilter(tag):
3980
3997
  continue
3981
3998
 
3982
- tagperm = tuple(tag.split('.'))
3983
- if not self.opts.apply:
3999
+ if not doapply:
3984
4000
  valurepr = ''
3985
4001
  if valu != (None, None):
3986
4002
  tagrepr = runt.model.type('ival').repr(valu)
@@ -3992,12 +4008,11 @@ class MergeCmd(Cmd):
3992
4008
 
3993
4009
  for tag, tagdict in sode.get('tagprops', {}).items():
3994
4010
 
3995
- if tagfilter and tagfilter(tag):
4011
+ if tagfilter is not None and tagfilter(tag):
3996
4012
  continue
3997
4013
 
3998
4014
  for prop, (valu, stortype) in tagdict.items():
3999
- tagperm = tuple(tag.split('.'))
4000
- if not self.opts.apply:
4015
+ if not doapply:
4001
4016
  valurepr = repr(valu)
4002
4017
  await runt.printf(f'{nodeiden} {form}#{tag}:{prop} = {valurepr}')
4003
4018
  else:
@@ -4006,31 +4021,33 @@ class MergeCmd(Cmd):
4006
4021
 
4007
4022
  if not onlytags or form == 'syn:tag':
4008
4023
 
4009
- layr = runt.snap.view.layers[0]
4010
- async for name, valu in layr.iterNodeData(node.buid):
4011
- if not self.opts.apply:
4024
+ async for name, valu in s_coro.pause(layr0.iterNodeData(node.buid)):
4025
+ if not doapply:
4012
4026
  valurepr = repr(valu)
4013
4027
  await runt.printf(f'{nodeiden} {form} DATA {name} = {valurepr}')
4014
4028
  else:
4015
4029
  await protonode.setData(name, valu)
4016
4030
  subs.append((s_layer.EDIT_NODEDATA_DEL, (name, valu), ()))
4017
- if len(subs) >= 1000:
4018
- await asyncio.sleep(0)
4019
4031
 
4020
- async for edge in layr.iterNodeEdgesN1(node.buid):
4032
+ async for edge in s_coro.pause(layr0.iterNodeEdgesN1(node.buid)):
4021
4033
  name, dest = edge
4022
- if not self.opts.apply:
4034
+ if not doapply:
4023
4035
  await runt.printf(f'{nodeiden} {form} +({name})> {dest}')
4024
4036
  else:
4025
4037
  await protonode.addEdge(name, dest)
4026
4038
  subs.append((s_layer.EDIT_EDGE_DEL, edge, ()))
4027
- if len(subs) >= 1000:
4028
- await asyncio.sleep(0)
4029
4039
 
4030
4040
  if delnode:
4031
4041
  subs.append((s_layer.EDIT_NODE_DEL, valu, ()))
4032
4042
 
4033
- await sync()
4043
+ if doapply:
4044
+ addedits = editor.getNodeEdits()
4045
+ if addedits:
4046
+ await runt.snap.view.parent.storNodeEdits(addedits, meta=meta)
4047
+
4048
+ if subs:
4049
+ subedits = [(node.buid, node.form.name, subs)]
4050
+ await runt.snap.view.storNodeEdits(subedits, meta=meta)
4034
4051
 
4035
4052
  runt.snap.clearCachedNode(node.buid)
4036
4053
  yield await runt.snap.getNodeByBuid(node.buid), path
synapse/lib/types.py CHANGED
@@ -1402,7 +1402,7 @@ class Loc(Type):
1402
1402
 
1403
1403
  class Ndef(Type):
1404
1404
 
1405
- stortype = s_layer.STOR_TYPE_MSGP
1405
+ stortype = s_layer.STOR_TYPE_NDEF
1406
1406
 
1407
1407
  def postTypeInit(self):
1408
1408
  self.setNormFunc(list, self._normPyTuple)
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 174, 0)
226
+ version = (2, 175, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = 'cc04ec913185e0c1ef5a94958d6eb40eb288f38f'
228
+ commit = 'c331c50ef1e7b2234be997c4b791fb8b8bed7cf2'
@@ -75,6 +75,42 @@ class DaemonTest(s_t_utils.SynTest):
75
75
  async with await s_telepath.openurl(f'tcp://127.0.0.1:{port}') as proxy:
76
76
  self.eq(proxy._ahainfo, ahainfo)
77
77
 
78
+ async def test_dmon_errors(self):
79
+
80
+ async with self.getTestCell(s_cell.Cell, conf={'dmon:listen': 'tcp://0.0.0.0:0/', 'auth:anon': 'root'}) as cell:
81
+ host, port = cell.sockaddr
82
+
83
+ async with await s_telepath.openurl(f'tcp://127.0.0.1:{port}') as prox:
84
+
85
+ # Throw an exception when trying to handle mesg outright
86
+ async with await prox.getPoolLink() as link:
87
+ with self.getAsyncLoggerStream('synapse.daemon', 'Dmon.onLinkMesg Handler: mesg=') as stream:
88
+ await link.tx(31337)
89
+ self.true(await stream.wait(timeout=6))
90
+
91
+ # Valid format; do not know what the message is.
92
+ async with await prox.getPoolLink() as link:
93
+ mesg = ('newp', {})
94
+ emsg = "Dmon.onLinkMesg Invalid mesg: mesg=('newp', {})"
95
+ with self.getAsyncLoggerStream('synapse.daemon', emsg) as stream:
96
+ await link.tx(mesg)
97
+ self.true(await stream.wait(timeout=6))
98
+
99
+ # Invalid data casues a link to fail on rx
100
+ async with await prox.getPoolLink() as link:
101
+ with self.getAsyncLoggerStream('synapse.lib.link', 'rx error') as stream:
102
+ byts = b'\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\xa6\xa3D\xd5\xdf%\xac\xa9\x92\xc3'
103
+ await link.send(byts)
104
+ self.true(await stream.wait(timeout=6))
105
+
106
+ # bad t2:init message
107
+ async with await prox.getPoolLink() as link:
108
+ mesg = ('t2:init', {})
109
+ emsg = "Error on t2:init:"
110
+ with self.getAsyncLoggerStream('synapse.daemon', emsg) as stream:
111
+ await link.tx(mesg)
112
+ self.true(await stream.wait(timeout=6))
113
+
78
114
  class SvcApi(s_cell.CellApi, s_stormsvc.StormSvc):
79
115
  _storm_svc_name = 'foo'
80
116
  _storm_svc_pkgs = ( # type: ignore
@@ -713,6 +713,41 @@ class AstTest(s_test.SynTest):
713
713
  self.len(3, await core.nodes('test:str=ndefs :ndefs -> *'))
714
714
  self.len(2, await core.nodes('test:str=ndefs :ndefs -> it:dev:int'))
715
715
 
716
+ await core.nodes('[ risk:technique:masquerade=* :node=(it:dev:int, 1) ]')
717
+ nodes = await core.nodes('it:dev:int=1 <- *')
718
+ self.len(2, nodes)
719
+ forms = [node.ndef[0] for node in nodes]
720
+ self.sorteq(forms, ['test:str', 'risk:technique:masquerade'])
721
+
722
+ await core.nodes('risk:technique:masquerade [ :target=(it:dev:int, 1) ]')
723
+ nodes = await core.nodes('it:dev:int=1 <- *')
724
+ self.len(2, nodes)
725
+ forms = [node.ndef[0] for node in nodes]
726
+ self.sorteq(forms, ['test:str', 'risk:technique:masquerade'])
727
+
728
+ await core.nodes('risk:technique:masquerade [ :target=(it:dev:int, 2) ]')
729
+ nodes = await core.nodes('it:dev:int=1 <- *')
730
+ self.len(2, nodes)
731
+ forms = [node.ndef[0] for node in nodes]
732
+ self.sorteq(forms, ['test:str', 'risk:technique:masquerade'])
733
+
734
+ await core.nodes('risk:technique:masquerade [ -:node ]')
735
+ nodes = await core.nodes('it:dev:int=1 <- *')
736
+ self.len(1, nodes)
737
+ self.eq('test:str', nodes[0].ndef[0])
738
+
739
+ await core.nodes('test:str=ndefs [ :ndefs-=(it:dev:int, 1) ]')
740
+ self.len(0, await core.nodes('it:dev:int=1 <- *'))
741
+ nodes = await core.nodes('it:dev:int=2 <- *')
742
+ self.len(2, nodes)
743
+ forms = [node.ndef[0] for node in nodes]
744
+ self.sorteq(forms, ['test:str', 'risk:technique:masquerade'])
745
+
746
+ await core.nodes('risk:technique:masquerade [ -:target ]')
747
+ await core.nodes('test:str=ndefs [ -:ndefs ]')
748
+ self.len(0, await core.nodes('it:dev:int=1 <- *'))
749
+ self.len(0, await core.nodes('it:dev:int=2 <- *'))
750
+
716
751
  async def test_ast_pivot(self):
717
752
  # a general purpose pivot test. come on in!
718
753
  async with self.getTestCore() as core:
@@ -503,6 +503,13 @@ class CellTest(s_t_utils.SynTest):
503
503
  cell.COMMIT = 'mycommit'
504
504
  cell.VERSION = (1, 2, 3)
505
505
  cell.VERSTRING = '1.2.3'
506
+
507
+ http_info = []
508
+ host, port = await cell.addHttpsPort(0)
509
+ http_info.append({'host': host, 'port': port})
510
+ host, port = await cell.addHttpsPort(0, host='127.0.0.1')
511
+ http_info.append({'host': host, 'port': port})
512
+
506
513
  async with cell.getLocalProxy() as prox:
507
514
  info = await prox.getCellInfo()
508
515
  # Cell information
@@ -527,6 +534,10 @@ class CellTest(s_t_utils.SynTest):
527
534
  self.eq(snfo.get('verstring'), s_version.verstring),
528
535
  self.eq(snfo.get('commit'), s_version.commit)
529
536
 
537
+ netw = cnfo.get('network')
538
+ https = netw.get('https')
539
+ self.eq(https, http_info)
540
+
530
541
  async def test_cell_dyncall(self):
531
542
 
532
543
  with self.getTestDir() as dirn:
@@ -530,3 +530,32 @@ class ModelRevTest(s_tests.SynTest):
530
530
  self.len(2, nodes)
531
531
  titles = [n.ndef[1] for n in nodes]
532
532
  self.sorteq(titles, positions)
533
+
534
+ async def test_modelrev_0_2_26(self):
535
+ async with self.getRegrCore('model-0.2.26') as core:
536
+
537
+ nodes = await core.nodes('it:dev:int=1 <- *')
538
+ self.len(3, nodes)
539
+ forms = [node.ndef[0] for node in nodes]
540
+ self.sorteq(forms, ['risk:vulnerable', 'risk:vulnerable', 'inet:fqdn'])
541
+
542
+ nodes = await core.nodes('it:dev:int=2 <- *')
543
+ self.len(2, nodes)
544
+ forms = [node.ndef[0] for node in nodes]
545
+ self.sorteq(forms, ['inet:fqdn', 'inet:fqdn'])
546
+
547
+ nodes = await core.nodes('it:dev:int=3 <- *')
548
+ self.len(1, nodes)
549
+ self.eq(nodes[0].ndef[0], 'risk:vulnerable')
550
+
551
+ nodes = await core.nodes('it:dev:int=4 <- *')
552
+ self.len(1, nodes)
553
+ self.eq(nodes[0].ndef[0], 'inet:fqdn')
554
+
555
+ nodes = await core.nodes('risk:vulnerable:node=(it:dev:int, 1)')
556
+ self.len(2, nodes)
557
+
558
+ rnodes = await core.nodes('reverse(risk:vulnerable:node=(it:dev:int, 1))')
559
+ self.len(2, rnodes)
560
+
561
+ self.eq([node.ndef[0] for node in nodes], [node.ndef[0] for node in reversed(rnodes)])