synapse 2.218.1__py311-none-any.whl → 2.220.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 (37) hide show
  1. synapse/cortex.py +113 -14
  2. synapse/daemon.py +2 -1
  3. synapse/data/__init__.py +4 -0
  4. synapse/data/lark/__init__.py +0 -0
  5. synapse/data/lark/imap.lark +8 -0
  6. synapse/exc.py +2 -0
  7. synapse/lib/ast.py +86 -84
  8. synapse/lib/json.py +6 -5
  9. synapse/lib/layer.py +27 -0
  10. synapse/lib/link.py +49 -50
  11. synapse/lib/parser.py +3 -5
  12. synapse/lib/schemas.py +25 -0
  13. synapse/lib/storm.py +1 -0
  14. synapse/lib/stormlib/imap.py +476 -35
  15. synapse/lib/stormtypes.py +177 -2
  16. synapse/lib/version.py +2 -2
  17. synapse/models/inet.py +3 -0
  18. synapse/tests/files/stormpkg/badinits.yaml +12 -0
  19. synapse/tests/files/stormpkg/testpkg.yaml +12 -0
  20. synapse/tests/test_lib_grammar.py +2 -4
  21. synapse/tests/test_lib_json.py +29 -0
  22. synapse/tests/test_lib_layer.py +119 -0
  23. synapse/tests/test_lib_storm.py +184 -1
  24. synapse/tests/test_lib_stormlib_imap.py +1307 -230
  25. synapse/tests/test_lib_stormtypes.py +157 -0
  26. synapse/tests/test_model_inet.py +3 -0
  27. synapse/tests/test_telepath.py +31 -0
  28. synapse/tests/test_tools_genpkg.py +4 -0
  29. synapse/tests/utils.py +1 -1
  30. synapse/tools/genpkg.py +9 -0
  31. synapse/utils/stormcov/plugin.py +2 -5
  32. {synapse-2.218.1.dist-info → synapse-2.220.0.dist-info}/METADATA +2 -3
  33. {synapse-2.218.1.dist-info → synapse-2.220.0.dist-info}/RECORD +37 -34
  34. /synapse/{lib → data/lark}/storm.lark +0 -0
  35. {synapse-2.218.1.dist-info → synapse-2.220.0.dist-info}/WHEEL +0 -0
  36. {synapse-2.218.1.dist-info → synapse-2.220.0.dist-info}/licenses/LICENSE +0 -0
  37. {synapse-2.218.1.dist-info → synapse-2.220.0.dist-info}/top_level.txt +0 -0
synapse/lib/stormtypes.py CHANGED
@@ -2,7 +2,6 @@ import bz2
2
2
  import copy
3
3
  import gzip
4
4
  import time
5
-
6
5
  import regex
7
6
  import types
8
7
  import base64
@@ -10,6 +9,7 @@ import pprint
10
9
  import struct
11
10
  import asyncio
12
11
  import decimal
12
+ import hashlib
13
13
  import inspect
14
14
  import logging
15
15
  import binascii
@@ -730,6 +730,18 @@ class LibPkg(Lib):
730
730
  {'name': 'pkgdef', 'type': 'dict', 'desc': 'A Storm Package definition.', },
731
731
  ),
732
732
  'returns': {'type': 'dict', 'desc': 'A dictionary listing dependencies and if they are met.', }}},
733
+ {'name': 'vars',
734
+ 'desc': "Get a dictionary representing the package's persistent variables.",
735
+ 'type': {'type': 'function', '_funcname': '_libPkgVars',
736
+ 'args': (
737
+ {'name': 'name', 'type': 'str',
738
+ 'desc': 'A Storm Package name to get vars for.', },
739
+ ),
740
+ 'returns': {'type': 'pkg:vars', 'desc': 'A dictionary representing the package variables.', }}},
741
+ )
742
+ _storm_lib_perms = (
743
+ {'perm': ('power-ups', '<name>', 'admin'), 'gate': 'cortex',
744
+ 'desc': 'Controls the ability to interact with the vars for a Storm Package by name.'},
733
745
  )
734
746
  _storm_lib_path = ('pkg',)
735
747
 
@@ -741,6 +753,7 @@ class LibPkg(Lib):
741
753
  'del': self._libPkgDel,
742
754
  'list': self._libPkgList,
743
755
  'deps': self._libPkgDeps,
756
+ 'vars': self._libPkgVars,
744
757
  }
745
758
 
746
759
  async def _libPkgAdd(self, pkgdef, verify=False):
@@ -780,6 +793,11 @@ class LibPkg(Lib):
780
793
  pkgdef = await toprim(pkgdef)
781
794
  return await self.runt.snap.core.verifyStormPkgDeps(pkgdef)
782
795
 
796
+ async def _libPkgVars(self, name):
797
+ name = await tostr(name)
798
+ confirm(('power-ups', name, 'admin'))
799
+ return PkgVars(self.runt, name)
800
+
783
801
  @registry.registerLib
784
802
  class LibDmon(Lib):
785
803
  '''
@@ -2745,9 +2763,13 @@ class LibAxon(Lib):
2745
2763
 
2746
2764
  self.runt.confirm(('axon', 'upload'))
2747
2765
 
2766
+ sha256 = hashlib.sha256(byts).digest()
2767
+
2748
2768
  await self.runt.snap.core.getAxon()
2749
- size, sha256 = await self.runt.snap.core.axon.put(byts)
2769
+ if await self.runt.snap.core.axon.has(sha256):
2770
+ return (len(byts), s_common.ehex(sha256))
2750
2771
 
2772
+ size, sha256 = await self.runt.snap.core.axon.put(byts)
2751
2773
  return (size, s_common.ehex(sha256))
2752
2774
 
2753
2775
  @stormfunc(readonly=True)
@@ -4945,6 +4967,24 @@ class Bytes(Prim):
4945
4967
  {'name': 'offset', 'type': 'int', 'desc': 'An offset to begin unpacking from.', 'default': 0},
4946
4968
  ),
4947
4969
  'returns': {'type': 'list', 'desc': 'The unpacked primitive values.', }}},
4970
+ {'name': 'xor', 'desc': '''
4971
+ Perform an "exclusive or" bitwise operation on the bytes and another set of bytes.
4972
+
4973
+ Notes:
4974
+ The key bytes provided as an argument will be repeated as needed until all bytes have been
4975
+ xor'd.
4976
+
4977
+ If a string is provided as the key argument, it will be utf8 encoded before being xor'd.
4978
+
4979
+ Examples:
4980
+ Perform an xor operation on the bytes in $encoded using the bytes in $key::
4981
+
4982
+ $decoded = $encoded.xor($key)''',
4983
+ 'type': {'type': 'function', '_funcname': '_methXor',
4984
+ 'args': (
4985
+ {'name': 'key', 'type': ['str', 'bytes'], 'desc': 'The key bytes to perform the xor operation with.'},
4986
+ ),
4987
+ 'returns': {'type': 'bytes', 'desc': "The xor'd bytes."}}},
4948
4988
  )
4949
4989
  _storm_typename = 'bytes'
4950
4990
  _ismutable = False
@@ -4955,6 +4995,7 @@ class Bytes(Prim):
4955
4995
 
4956
4996
  def getObjLocals(self):
4957
4997
  return {
4998
+ 'xor': self._methXor,
4958
4999
  'decode': self._methDecode,
4959
5000
  'bunzip': self._methBunzip,
4960
5001
  'gunzip': self._methGunzip,
@@ -5043,6 +5084,26 @@ class Bytes(Prim):
5043
5084
  except UnicodeDecodeError as e:
5044
5085
  raise s_exc.StormRuntimeError(mesg=f'{e}: {s_common.trimText(repr(valu))}') from None
5045
5086
 
5087
+ @stormfunc(readonly=True)
5088
+ async def _methXor(self, key):
5089
+ key = await toprim(key)
5090
+ if isinstance(key, str):
5091
+ key = key.encode()
5092
+
5093
+ if not isinstance(key, bytes):
5094
+ raise s_exc.BadArg(mesg='$bytes.xor() key argument must be bytes or a str.')
5095
+
5096
+ if len(key) == 0:
5097
+ raise s_exc.BadArg(mesg='$bytes.xor() key length must be greater than 0.')
5098
+
5099
+ arry = bytearray(self.valu)
5100
+ keylen = len(key)
5101
+
5102
+ for i in range(len(arry)):
5103
+ arry[i] ^= key[i % keylen]
5104
+
5105
+ return bytes(arry)
5106
+
5046
5107
  @registry.registerType
5047
5108
  class Dict(Prim):
5048
5109
  '''
@@ -6015,6 +6076,45 @@ class LibVars(Lib):
6015
6076
  async def _libVarsType(self, valu):
6016
6077
  return await totype(valu)
6017
6078
 
6079
+ @registry.registerType
6080
+ class PkgVars(Prim):
6081
+ '''
6082
+ The Storm deref/setitem/iter convention on top of pkg vars information.
6083
+ '''
6084
+ _storm_typename = 'pkg:vars'
6085
+ _ismutable = True
6086
+
6087
+ def __init__(self, runt, valu, path=None):
6088
+ Prim.__init__(self, valu, path=path)
6089
+ self.runt = runt
6090
+
6091
+ def _reqPkgAdmin(self):
6092
+ confirm(('power-ups', self.valu, 'admin'))
6093
+
6094
+ @stormfunc(readonly=True)
6095
+ async def deref(self, name):
6096
+ self._reqPkgAdmin()
6097
+ name = await tostr(name)
6098
+ return await self.runt.snap.core.getStormPkgVar(self.valu, name)
6099
+
6100
+ async def setitem(self, name, valu):
6101
+ self._reqPkgAdmin()
6102
+ name = await tostr(name)
6103
+
6104
+ if valu is undef:
6105
+ await self.runt.snap.core.popStormPkgVar(self.valu, name)
6106
+ return
6107
+
6108
+ valu = await toprim(valu)
6109
+ await self.runt.snap.core.setStormPkgVar(self.valu, name, valu)
6110
+
6111
+ @stormfunc(readonly=True)
6112
+ async def iter(self):
6113
+ self._reqPkgAdmin()
6114
+ async for name, valu in self.runt.snap.core.iterStormPkgVars(self.valu):
6115
+ yield name, valu
6116
+ await asyncio.sleep(0)
6117
+
6018
6118
  @registry.registerType
6019
6119
  class Query(Prim):
6020
6120
  '''
@@ -7069,6 +7169,37 @@ class Layer(Prim):
7069
7169
  'desc': 'The name of the form to get storage nodes for.'},
7070
7170
  ),
7071
7171
  'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of buid, sode values.', }}},
7172
+ {'name': 'getStorNodesByProp', 'desc': '''
7173
+ Get buid, sode tuples representing the data stored in the layer for a given property.
7174
+ Notes:
7175
+ The storage nodes represent **only** the data stored in the layer
7176
+ and may not represent whole nodes.
7177
+ ''',
7178
+ 'type': {'type': 'function', '_funcname': 'getStorNodesByProp',
7179
+ 'args': (
7180
+ {'name': 'propname', 'type': 'str', 'desc': 'The full property name to lift by.'},
7181
+ {'name': 'propvalu', 'type': 'obj', 'desc': 'The value for the property.', 'default': None},
7182
+ {'name': 'propcmpr', 'type': 'str', 'desc': 'The comparison operation to use on the value.',
7183
+ 'default': '='},
7184
+ ),
7185
+ 'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of buid, sode values.', }}},
7186
+ {'name': 'setStorNodeProp',
7187
+ 'desc': 'Set a property on a node in this layer.',
7188
+ 'type': {'type': 'function', '_funcname': 'setStorNodeProp',
7189
+ 'args': (
7190
+ {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7191
+ {'name': 'prop', 'type': 'str', 'desc': 'The property name to set.'},
7192
+ {'name': 'valu', 'type': 'any', 'desc': 'The value to set.'},
7193
+ ),
7194
+ 'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7195
+ {'name': 'delStorNodeProp',
7196
+ 'desc': 'Delete a property from a node in this layer.',
7197
+ 'type': {'type': 'function', '_funcname': 'delStorNodeProp',
7198
+ 'args': (
7199
+ {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7200
+ {'name': 'prop', 'type': 'str', 'desc': 'The property name to delete.'},
7201
+ ),
7202
+ 'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7072
7203
  {'name': 'getMirrorStatus', 'desc': '''
7073
7204
  Return a dictionary of the mirror synchronization status for the layer.
7074
7205
  ''',
@@ -7287,10 +7418,13 @@ class Layer(Prim):
7287
7418
  'getStorNode': self.getStorNode,
7288
7419
  'getStorNodes': self.getStorNodes,
7289
7420
  'getStorNodesByForm': self.getStorNodesByForm,
7421
+ 'getStorNodesByProp': self.getStorNodesByProp,
7290
7422
  'getEdgesByN1': self.getEdgesByN1,
7291
7423
  'getEdgesByN2': self.getEdgesByN2,
7292
7424
  'getNodeData': self.getNodeData,
7293
7425
  'getMirrorStatus': self.getMirrorStatus,
7426
+ 'setStorNodeProp': self.setStorNodeProp,
7427
+ 'delStorNodeProp': self.delStorNodeProp,
7294
7428
  }
7295
7429
 
7296
7430
  @stormfunc(readonly=True)
@@ -7364,6 +7498,25 @@ class Layer(Prim):
7364
7498
  layr = self.runt.snap.core.getLayer(iden)
7365
7499
  return await layr.getMirrorStatus()
7366
7500
 
7501
+ async def setStorNodeProp(self, nodeid, prop, valu):
7502
+ iden = self.valu.get('iden')
7503
+ layr = self.runt.snap.core.getLayer(iden)
7504
+ buid = s_common.uhex(await tostr(nodeid))
7505
+ prop = await tostr(prop)
7506
+ valu = await tostor(valu)
7507
+ self.runt.reqAdmin(mesg='setStorNodeProp() requires admin privileges.')
7508
+ meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7509
+ return await layr.setStorNodeProp(buid, prop, valu, meta=meta)
7510
+
7511
+ async def delStorNodeProp(self, nodeid, prop):
7512
+ iden = self.valu.get('iden')
7513
+ layr = self.runt.snap.core.getLayer(iden)
7514
+ buid = s_common.uhex(await tostr(nodeid))
7515
+ prop = await tostr(prop)
7516
+ self.runt.reqAdmin(mesg='delStorNodeProp() requires admin privileges.')
7517
+ meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7518
+ return await layr.delStorNodeProp(buid, prop, meta=meta)
7519
+
7367
7520
  async def _addPull(self, url, offs=0, queue_size=s_const.layer_pdef_qsize, chunk_size=s_const.layer_pdef_csize):
7368
7521
  url = await tostr(url)
7369
7522
  offs = await toint(offs)
@@ -7646,6 +7799,28 @@ class Layer(Prim):
7646
7799
  async for item in layr.getStorNodesByForm(form):
7647
7800
  yield item
7648
7801
 
7802
+ @stormfunc(readonly=True)
7803
+ async def getStorNodesByProp(self, propname, propvalu=None, propcmpr='='):
7804
+ propname = await tostr(propname)
7805
+ propvalu = await tostor(propvalu)
7806
+ propcmpr = await tostr(propcmpr)
7807
+
7808
+ layriden = self.valu.get('iden')
7809
+ await self.runt.reqUserCanReadLayer(layriden)
7810
+ layr = self.runt.snap.core.getLayer(layriden)
7811
+
7812
+ prop = self.runt.snap.core.model.reqProp(propname)
7813
+
7814
+ if propvalu is not None:
7815
+ norm, info = prop.type.norm(propvalu)
7816
+ cmprvals = prop.type.getStorCmprs(propcmpr, norm)
7817
+ async for _, buid, sode in layr.liftByPropValu(prop.form.name, prop.name, cmprvals):
7818
+ yield (s_common.ehex(buid), sode)
7819
+ return
7820
+
7821
+ async for _, buid, sode in layr.liftByProp(prop.form.name, prop.name):
7822
+ yield (s_common.ehex(buid), sode)
7823
+
7649
7824
  @stormfunc(readonly=True)
7650
7825
  async def getEdges(self):
7651
7826
  layriden = self.valu.get('iden')
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, 218, 1)
226
+ version = (2, 220, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = 'f91b8902226a2b8d984804f164c4c65e8b7e5ce3'
228
+ commit = '913156d6c195af916a32860341f285954cb4e882'
synapse/models/inet.py CHANGED
@@ -3796,6 +3796,9 @@ class InetModule(s_module.CoreModule):
3796
3796
  ('user', ('inet:user', {}), {
3797
3797
  'doc': 'The current user name of the account.'}),
3798
3798
 
3799
+ ('parent', ('inet:service:account', {}), {
3800
+ 'doc': 'A parent account which owns this account.'}),
3801
+
3799
3802
  ('email', ('inet:email', {}), {
3800
3803
  'doc': 'The current email address associated with the account.'}),
3801
3804
 
@@ -0,0 +1,12 @@
1
+ name: badinits
2
+ version: 0.0.1
3
+
4
+ inits:
5
+ name: badinits:version
6
+ versions:
7
+ - name: first
8
+ version: 7
9
+ query: ""
10
+ - name: second
11
+ version: 2
12
+ query: ""
@@ -45,6 +45,18 @@ onload: |
45
45
  $lib.time.sleep($lib.globals.get(onload_sleep, 0))
46
46
  $lib.globals.set(testpkg, testpkg-done)
47
47
 
48
+ inits:
49
+ key: testpkg:version
50
+ versions:
51
+ - name: first
52
+ version: 0
53
+ inaugural: true
54
+ query: "$lib.globals.set(testpkg-first, (true))"
55
+ - name: second
56
+ desc: Another init
57
+ version: 2
58
+ query: "$lib.globals.set(testpkg-second, (true))"
59
+
48
60
  external_modules:
49
61
  - name: testext
50
62
  package: synapse.tests.files
@@ -1,9 +1,9 @@
1
1
  import lark # type: ignore
2
2
 
3
3
  import synapse.exc as s_exc
4
+ import synapse.data as s_data
4
5
 
5
6
  import synapse.lib.parser as s_parser
6
- import synapse.lib.datfile as s_datfile
7
7
  import synapse.lib.grammar as s_grammar
8
8
 
9
9
  import synapse.tests.utils as s_t_utils
@@ -1414,9 +1414,7 @@ class GrammarTest(s_t_utils.SynTest):
1414
1414
  '''
1415
1415
  Validates that we have no grammar ambiguities
1416
1416
  '''
1417
- with s_datfile.openDatFile('synapse.lib/storm.lark') as larkf:
1418
- grammar = larkf.read().decode()
1419
-
1417
+ grammar = s_data.getLark('storm')
1420
1418
  parser = lark.Lark(grammar, start='query', debug=True, regex=True, parser='lalr',
1421
1419
  keep_all_tokens=True, maybe_placeholders=False,
1422
1420
  propagate_positions=True)
@@ -125,6 +125,35 @@ class JsonTest(s_test.SynTest):
125
125
 
126
126
  self.eq(valu, s_json.loads(s_json.dumps(valu)))
127
127
 
128
+ async def test_lib_json_control_strings(self):
129
+ valus = [
130
+ 'line1"line2',
131
+ 'line1/line2',
132
+ 'line1\\line2',
133
+ 'line1\bline2',
134
+ 'line1\fline2',
135
+ 'line1\nline2',
136
+ 'line1\rline2',
137
+ 'line1\tline2',
138
+ 'line1\u0009line2',
139
+ 'line1\u1000line2',
140
+ 'line1\u2000line2',
141
+ 'line1\u3000line2',
142
+ ]
143
+
144
+ with self.getLoggerStream('synapse.lib.json') as stream:
145
+ async with self.getTestCore() as core:
146
+ for valu in valus:
147
+ q = '$lib.print($valu) $lib.print($lib.json.save($valu))'
148
+ msgs = await core.stormlist(q, opts={'vars': {'valu': valu}})
149
+ self.stormHasNoWarnErr(msgs)
150
+
151
+ self.eq(s_json.loads(s_json.dumps(valu)), valu)
152
+
153
+ stream.seek(0)
154
+ data = stream.read()
155
+ self.notin('fallback JSON', data)
156
+
128
157
  async def test_jsload(self):
129
158
  with self.getTestDir() as dirn:
130
159
  with s_common.genfile(dirn, 'jsload.json') as fp:
@@ -2283,3 +2283,122 @@ class LayerTest(s_t_utils.SynTest):
2283
2283
 
2284
2284
  sodes = await s_t_utils.alist(layr00.getStorNodesByForm('inet:ipv4'))
2285
2285
  self.len(0, sodes)
2286
+
2287
+ async def test_layer_migrate_props_fork(self):
2288
+
2289
+ async with self.getTestCore() as core:
2290
+
2291
+ iden = (await core.addUser('lowuser')).get('iden')
2292
+ lowuser = {'user': iden}
2293
+
2294
+ fork00 = await core.view.fork()
2295
+ layr00 = core.getLayer(fork00['layers'][0]['iden'])
2296
+
2297
+ await core.nodes('''
2298
+ for $prop in (_custom:risk:level, _custom:risk:severity) {
2299
+ $lib.model.ext.addFormProp(
2300
+ test:guid,
2301
+ $prop,
2302
+ (["int", {"enums": [[10, "low"], [20, "medium"], [30, "high"]]}]),
2303
+ ({"doc": "hey now"}),
2304
+ )
2305
+ }
2306
+
2307
+ ''')
2308
+ self.len(1, await core.nodes('syn:prop=test:guid:_custom:risk:level'))
2309
+ self.len(1, await core.nodes('syn:prop=test:guid:_custom:risk:severity'))
2310
+
2311
+ await core.nodes('[ test:guid=* :name=test1 :_custom:risk:level=low ]', opts={'view': fork00['iden']})
2312
+
2313
+ await core.getView(fork00['iden']).delete()
2314
+
2315
+ with self.raises(s_exc.CantDelProp) as cm:
2316
+ await core.callStorm('''
2317
+ $fullprop = "test:guid:_custom:risk:level"
2318
+ for $view in $lib.view.list(deporder=$lib.true) {
2319
+ view.exec $view.iden {
2320
+ yield $lib.layer.get().liftByProp($fullprop)
2321
+ $repr = $node.repr("_custom:risk:level")
2322
+ [ :severity=$repr -:_custom:risk:level ]
2323
+ }
2324
+ }
2325
+ $lib.model.ext.delFormProp("test:guid", "_custom:risk:level")
2326
+ ''')
2327
+ self.isin('Nodes still exist with prop: test:guid:_custom:risk:level', str(cm.exception))
2328
+ self.len(1, await core.nodes('syn:prop=test:guid:_custom:risk:level'))
2329
+
2330
+ with self.raises(s_exc.NoSuchProp) as cm:
2331
+ await core.callStorm('''
2332
+ $layer = $lib.layer.get()
2333
+ $layer.getStorNodesByProp("foo:bar:_custom:risk:level")
2334
+ ''')
2335
+ self.isin('No property named', str(cm.exception))
2336
+
2337
+ with self.raises(s_exc.NoSuchProp):
2338
+ await core.callStorm('''
2339
+ $fullprop = "test:guid:_custom:risk:level"
2340
+ for $layer in $lib.layer.list() {
2341
+ for ($buid, $sode) in $layer.getStorNodesByProp($fullprop) {
2342
+ $oldv = $sode.props."_custom:risk:level"
2343
+ $layer.setStorNodeProp($buid, "foo:bar:severity", $oldv.0)
2344
+ }
2345
+ }
2346
+ ''')
2347
+
2348
+ with self.raises(s_exc.BadTypeValu):
2349
+ await core.callStorm('''
2350
+ $fullprop = "test:guid:_custom:risk:level"
2351
+ for $layer in $lib.layer.list() {
2352
+ for ($buid, $sode) in $layer.getStorNodesByProp($fullprop) {
2353
+ $layer.setStorNodeProp($buid, $fullprop, "newp")
2354
+ }
2355
+ }
2356
+ ''')
2357
+
2358
+ with self.raises(s_exc.NoSuchProp):
2359
+ await core.callStorm('''
2360
+ $fullprop = "test:guid:_custom:risk:level"
2361
+ for $layer in $lib.layer.list() {
2362
+ for ($buid, $sode) in $layer.getStorNodesByProp($fullprop) {
2363
+ $layer.delStorNodeProp($buid, "foo:bar:severity")
2364
+ }
2365
+ }
2366
+ ''')
2367
+
2368
+ with self.raises(s_exc.AuthDeny) as cm:
2369
+ await core.callStorm('''
2370
+ $buid = "8c454b27df9c0ba109c123265b50869759bccac5bbec83b41992b4e91207f4a4"
2371
+ $layer = $lib.layer.get()
2372
+ $layer.setStorNodeProp($buid, "foo:bar:severity", "newp")
2373
+ ''', opts=lowuser)
2374
+ self.isin('requires admin privileges', str(cm.exception))
2375
+
2376
+ with self.raises(s_exc.AuthDeny) as cm:
2377
+ await core.callStorm('''
2378
+ $buid = "8c454b27df9c0ba109c123265b50869759bccac5bbec83b41992b4e91207f4a4"
2379
+ $layer = $lib.layer.get()
2380
+ $layer.delStorNodeProp($buid, "foo:bar:severity")
2381
+ ''', opts=lowuser)
2382
+ self.isin('requires admin privileges', str(cm.exception))
2383
+
2384
+ await core.callStorm('''
2385
+ $fullprop = "test:guid:_custom:risk:level"
2386
+ for $layer in $lib.layer.list() {
2387
+ if $layer.getPropCount($fullprop, maxsize=1) {
2388
+ for ($buid, $sode) in $layer.getStorNodesByProp($fullprop, (10), "=") {
2389
+ $oldv = $sode.props."_custom:risk:level"
2390
+ $layer.setStorNodeProp($buid, "test:guid:_custom:risk:severity", $oldv.0)
2391
+ $layer.delStorNodeProp($buid, $fullprop)
2392
+ }
2393
+ }
2394
+ }
2395
+ $lib.model.ext.delFormProp("test:guid", "_custom:risk:level")
2396
+ ''')
2397
+ self.len(0, await core.nodes('syn:prop=test:guid:_custom:risk:level'))
2398
+ self.len(0, await core.nodes('test:guid:_custom:risk:severity'))
2399
+
2400
+ view00 = (await core.addView(vdef={'layers': [layr00.iden]}))['iden']
2401
+ nodes = await core.nodes('test:guid', opts={'view': view00})
2402
+ self.len(1, nodes)
2403
+ self.none(nodes[0].props.get('_custom:risk:level'))
2404
+ self.eq(nodes[0].props.get('_custom:risk:severity'), 10)