synapse 2.161.0__py311-none-any.whl → 2.162.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/daemon.py +7 -2
- synapse/lib/cell.py +7 -3
- synapse/lib/layer.py +20 -1
- synapse/lib/rstorm.py +16 -0
- synapse/lib/storm.py +17 -1
- synapse/lib/stormlib/stix.py +6 -3
- synapse/lib/stormtypes.py +148 -19
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +15 -2
- synapse/models/inet.py +9 -0
- synapse/models/infotech.py +28 -26
- synapse/models/orgs.py +3 -0
- synapse/models/proj.py +9 -2
- synapse/models/risk.py +32 -0
- synapse/telepath.py +2 -0
- synapse/tests/files/rstorm/testsvc.py +8 -1
- synapse/tests/files/stormpkg/testpkg.yaml +4 -0
- synapse/tests/test_axon.py +4 -4
- synapse/tests/test_cortex.py +6 -6
- synapse/tests/test_daemon.py +19 -0
- synapse/tests/test_lib_ast.py +16 -16
- synapse/tests/test_lib_grammar.py +4 -4
- synapse/tests/test_lib_rstorm.py +38 -2
- synapse/tests/test_lib_storm.py +14 -14
- synapse/tests/test_lib_stormhttp.py +18 -18
- synapse/tests/test_lib_stormlib_auth.py +3 -3
- synapse/tests/test_lib_stormlib_cell.py +1 -1
- synapse/tests/test_lib_stormlib_cortex.py +50 -2
- synapse/tests/test_lib_stormlib_json.py +2 -2
- synapse/tests/test_lib_stormlib_macro.py +1 -1
- synapse/tests/test_lib_stormlib_modelext.py +37 -37
- synapse/tests/test_lib_stormlib_oauth.py +20 -20
- synapse/tests/test_lib_stormlib_stix.py +3 -1
- synapse/tests/test_lib_stormtypes.py +127 -45
- synapse/tests/test_lib_stormwhois.py +1 -1
- synapse/tests/test_lib_trigger.py +11 -11
- synapse/tests/test_lib_view.py +23 -1
- synapse/tests/test_model_crypto.py +1 -1
- synapse/tests/test_model_inet.py +6 -0
- synapse/tests/test_model_orgs.py +2 -1
- synapse/tests/test_model_proj.py +6 -0
- synapse/tests/test_model_risk.py +10 -0
- synapse/tests/test_tools_storm.py +1 -1
- {synapse-2.161.0.dist-info → synapse-2.162.0.dist-info}/METADATA +3 -1
- {synapse-2.161.0.dist-info → synapse-2.162.0.dist-info}/RECORD +48 -48
- {synapse-2.161.0.dist-info → synapse-2.162.0.dist-info}/LICENSE +0 -0
- {synapse-2.161.0.dist-info → synapse-2.162.0.dist-info}/WHEEL +0 -0
- {synapse-2.161.0.dist-info → synapse-2.162.0.dist-info}/top_level.txt +0 -0
synapse/daemon.py
CHANGED
|
@@ -218,7 +218,7 @@ async def t2call(link, meth, args, kwargs):
|
|
|
218
218
|
|
|
219
219
|
class Daemon(s_base.Base):
|
|
220
220
|
|
|
221
|
-
async def __anit__(self, certdir=None):
|
|
221
|
+
async def __anit__(self, certdir=None, ahainfo=None):
|
|
222
222
|
|
|
223
223
|
await s_base.Base.__anit__(self)
|
|
224
224
|
|
|
@@ -227,6 +227,8 @@ class Daemon(s_base.Base):
|
|
|
227
227
|
if certdir is None:
|
|
228
228
|
certdir = s_certdir.getCertDir()
|
|
229
229
|
|
|
230
|
+
self.ahainfo = ahainfo
|
|
231
|
+
|
|
230
232
|
self.certdir = certdir
|
|
231
233
|
self.televers = s_telepath.televers
|
|
232
234
|
|
|
@@ -414,13 +416,16 @@ class Daemon(s_base.Base):
|
|
|
414
416
|
async def _getSharedItem(self, name):
|
|
415
417
|
return self.shared.get(name)
|
|
416
418
|
|
|
417
|
-
async def _onTeleSyn(self, link, mesg):
|
|
419
|
+
async def _onTeleSyn(self, link: s_link.Link, mesg):
|
|
418
420
|
|
|
419
421
|
reply = ('tele:syn', {
|
|
420
422
|
'vers': self.televers,
|
|
421
423
|
'retn': (True, None),
|
|
422
424
|
})
|
|
423
425
|
|
|
426
|
+
if self.ahainfo is not None:
|
|
427
|
+
reply[1]['ahainfo'] = self.ahainfo
|
|
428
|
+
|
|
424
429
|
try:
|
|
425
430
|
|
|
426
431
|
vers = mesg[1].get('vers')
|
synapse/lib/cell.py
CHANGED
|
@@ -2647,8 +2647,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2647
2647
|
for name, valu in vals.items():
|
|
2648
2648
|
self.sessstor.set(iden, name, valu)
|
|
2649
2649
|
sess = self.sessions.get(iden)
|
|
2650
|
-
|
|
2651
|
-
sess.info
|
|
2650
|
+
if sess is not None:
|
|
2651
|
+
sess.info.update(vals)
|
|
2652
2652
|
|
|
2653
2653
|
@contextlib.contextmanager
|
|
2654
2654
|
def getTempDir(self):
|
|
@@ -2840,7 +2840,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2840
2840
|
|
|
2841
2841
|
async def _initCellDmon(self):
|
|
2842
2842
|
|
|
2843
|
-
|
|
2843
|
+
ahainfo = {
|
|
2844
|
+
'name': self.ahasvcname
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
self.dmon = await s_daemon.Daemon.anit(ahainfo=ahainfo)
|
|
2844
2848
|
self.dmon.share('*', self)
|
|
2845
2849
|
|
|
2846
2850
|
self.onfini(self.dmon.fini)
|
synapse/lib/layer.py
CHANGED
|
@@ -1529,12 +1529,14 @@ class Layer(s_nexus.Pusher):
|
|
|
1529
1529
|
|
|
1530
1530
|
mirror = self.layrinfo.get('mirror')
|
|
1531
1531
|
if mirror is not None:
|
|
1532
|
+
s_common.deprecated('mirror layer configuration option', curv='2.162.0')
|
|
1532
1533
|
conf = {'retrysleep': 2}
|
|
1533
1534
|
self.leader = await s_telepath.Client.anit(mirror, conf=conf)
|
|
1534
1535
|
self.leadtask = self.schedCoro(self._runMirrorLoop())
|
|
1535
1536
|
|
|
1536
1537
|
uplayr = self.layrinfo.get('upstream')
|
|
1537
1538
|
if uplayr is not None:
|
|
1539
|
+
s_common.deprecated('upstream layer configuration option', curv='2.162.0')
|
|
1538
1540
|
if isinstance(uplayr, (tuple, list)):
|
|
1539
1541
|
for layr in uplayr:
|
|
1540
1542
|
await self.initUpstreamSync(layr)
|
|
@@ -4382,9 +4384,14 @@ def getNodeEditPerms(nodeedits):
|
|
|
4382
4384
|
'''
|
|
4383
4385
|
Yields (offs, perm) tuples that can be used in user.allowed()
|
|
4384
4386
|
'''
|
|
4387
|
+
tags = []
|
|
4388
|
+
tagadds = []
|
|
4385
4389
|
|
|
4386
4390
|
for nodeoffs, (buid, form, edits) in enumerate(nodeedits):
|
|
4387
4391
|
|
|
4392
|
+
tags.clear()
|
|
4393
|
+
tagadds.clear()
|
|
4394
|
+
|
|
4388
4395
|
for editoffs, (edit, info, _) in enumerate(edits):
|
|
4389
4396
|
|
|
4390
4397
|
permoffs = (nodeoffs, editoffs)
|
|
@@ -4406,7 +4413,11 @@ def getNodeEditPerms(nodeedits):
|
|
|
4406
4413
|
continue
|
|
4407
4414
|
|
|
4408
4415
|
if edit == EDIT_TAG_SET:
|
|
4409
|
-
|
|
4416
|
+
if info[1] != (None, None):
|
|
4417
|
+
tagadds.append(info[0])
|
|
4418
|
+
yield (permoffs, ('node', 'tag', 'add', *info[0].split('.')))
|
|
4419
|
+
else:
|
|
4420
|
+
tags.append((len(info[0]), editoffs, info[0]))
|
|
4410
4421
|
continue
|
|
4411
4422
|
|
|
4412
4423
|
if edit == EDIT_TAG_DEL:
|
|
@@ -4436,3 +4447,11 @@ def getNodeEditPerms(nodeedits):
|
|
|
4436
4447
|
if edit == EDIT_EDGE_DEL:
|
|
4437
4448
|
yield (permoffs, ('node', 'edge', 'del', info[0]))
|
|
4438
4449
|
continue
|
|
4450
|
+
|
|
4451
|
+
for _, editoffs, tag in sorted(tags, reverse=True):
|
|
4452
|
+
look = tag + '.'
|
|
4453
|
+
if any([tagadd.startswith(look) for tagadd in tagadds]):
|
|
4454
|
+
continue
|
|
4455
|
+
|
|
4456
|
+
yield ((nodeoffs, editoffs), ('node', 'tag', 'add', *tag.split('.')))
|
|
4457
|
+
tagadds.append(tag)
|
synapse/lib/rstorm.py
CHANGED
|
@@ -29,6 +29,7 @@ re_directive = regex.compile(r'^\.\.\s(storm.*|[^:])::(?:\s(.*)$|$)')
|
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
32
|
+
ONLOAD_TIMEOUT = int(os.getenv('SYNDEV_PKG_LOAD_TIMEOUT', 30)) # seconds
|
|
32
33
|
|
|
33
34
|
class OutPutRst(s_output.OutPutStr):
|
|
34
35
|
'''
|
|
@@ -413,8 +414,17 @@ class StormRst(s_base.Base):
|
|
|
413
414
|
core = self._reqCore()
|
|
414
415
|
|
|
415
416
|
pkg = s_genpkg.loadPkgProto(text)
|
|
417
|
+
|
|
418
|
+
if pkg.get('onload') is not None:
|
|
419
|
+
waiter = core.waiter(1, 'core:pkg:onload:complete')
|
|
420
|
+
else:
|
|
421
|
+
waiter = None
|
|
422
|
+
|
|
416
423
|
await core.addStormPkg(pkg)
|
|
417
424
|
|
|
425
|
+
if waiter is not None and not await waiter.wait(timeout=ONLOAD_TIMEOUT):
|
|
426
|
+
raise s_exc.SynErr(mesg=f'Package onload failed to run for {pkg.get("name")}')
|
|
427
|
+
|
|
418
428
|
async def _handleStormPre(self, text):
|
|
419
429
|
'''
|
|
420
430
|
Run a Storm query to prepare the Cortex without output.
|
|
@@ -453,6 +463,9 @@ class StormRst(s_base.Base):
|
|
|
453
463
|
|
|
454
464
|
svc = await self._getCell(ctor, conf=svcconf)
|
|
455
465
|
|
|
466
|
+
onloadcnt = len([p for p in svc.cellapi._storm_svc_pkgs if p.get('onload') is not None])
|
|
467
|
+
waiter = core.waiter(onloadcnt, 'core:pkg:onload:complete') if onloadcnt else None
|
|
468
|
+
|
|
456
469
|
svc.dmon.share('svc', svc)
|
|
457
470
|
root = await svc.auth.getUserByName('root')
|
|
458
471
|
await root.setPasswd('root')
|
|
@@ -463,6 +476,9 @@ class StormRst(s_base.Base):
|
|
|
463
476
|
await core.nodes(f'service.add {svcname} {surl}')
|
|
464
477
|
await core.nodes(f'$lib.service.wait({svcname})')
|
|
465
478
|
|
|
479
|
+
if waiter is not None and not await waiter.wait(timeout=ONLOAD_TIMEOUT):
|
|
480
|
+
raise s_exc.SynErr(mesg=f'Package onload failed to run for service {svcname}')
|
|
481
|
+
|
|
466
482
|
async def _handleStormFail(self, text):
|
|
467
483
|
valu = json.loads(text)
|
|
468
484
|
assert valu in (True, False), f'storm-fail must be a boolean: {text}'
|
synapse/lib/storm.py
CHANGED
|
@@ -181,7 +181,7 @@ wgetdescr = '''Retrieve bytes from a URL and store them in the axon. Yields inet
|
|
|
181
181
|
Examples:
|
|
182
182
|
|
|
183
183
|
# Specify custom headers and parameters
|
|
184
|
-
inet:url=https://vertex.link/foo.bar.txt | wget --headers
|
|
184
|
+
inet:url=https://vertex.link/foo.bar.txt | wget --headers ({"User-Agent": "Foo/Bar"}) --params ({"clientid": "42"})
|
|
185
185
|
|
|
186
186
|
# Download multiple URL targets without inbound nodes
|
|
187
187
|
wget https://vertex.link https://vtx.lk
|
|
@@ -3787,7 +3787,23 @@ class MergeCmd(Cmd):
|
|
|
3787
3787
|
runt.confirmPropDel(prop, layriden=layr0)
|
|
3788
3788
|
runt.confirmPropSet(prop, layriden=layr1)
|
|
3789
3789
|
|
|
3790
|
+
tags = []
|
|
3791
|
+
tagadds = []
|
|
3790
3792
|
for tag, valu in sode.get('tags', {}).items():
|
|
3793
|
+
if valu != (None, None):
|
|
3794
|
+
tagadds.append(tag)
|
|
3795
|
+
tagperm = tuple(tag.split('.'))
|
|
3796
|
+
runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
|
|
3797
|
+
runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
|
|
3798
|
+
else:
|
|
3799
|
+
tags.append((len(tag), tag))
|
|
3800
|
+
|
|
3801
|
+
for _, tag in sorted(tags, reverse=True):
|
|
3802
|
+
look = tag + '.'
|
|
3803
|
+
if any([tagadd.startswith(look) for tagadd in tagadds]):
|
|
3804
|
+
continue
|
|
3805
|
+
|
|
3806
|
+
tagadds.append(tag)
|
|
3791
3807
|
tagperm = tuple(tag.split('.'))
|
|
3792
3808
|
runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
|
|
3793
3809
|
runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
|
synapse/lib/stormlib/stix.py
CHANGED
|
@@ -247,7 +247,7 @@ _DefaultConfig = {
|
|
|
247
247
|
'name': '{+:name return(:name)} return($node.repr())',
|
|
248
248
|
'size': '+:size return(:size)',
|
|
249
249
|
'hashes': '''
|
|
250
|
-
init { $dict =
|
|
250
|
+
init { $dict = ({}) }
|
|
251
251
|
{ +:md5 $dict.MD5 = :md5 }
|
|
252
252
|
{ +:sha1 $dict."SHA-1" = :sha1 }
|
|
253
253
|
{ +:sha256 $dict."SHA-256" = :sha256 }
|
|
@@ -392,7 +392,7 @@ _DefaultConfig = {
|
|
|
392
392
|
'description': 'if (:desc) { return (:desc) }',
|
|
393
393
|
'created': 'return($lib.stix.export.timestamp(.created))',
|
|
394
394
|
'modified': 'return($lib.stix.export.timestamp(.created))',
|
|
395
|
-
'external_references': 'if :cve { $cve=:cve $cve=$cve.upper() $list=$lib.list(
|
|
395
|
+
'external_references': 'if :cve { $cve=:cve $cve=$cve.upper() $list=$lib.list(({"source_name": "cve", "external_id": $cve})) return($list) }'
|
|
396
396
|
},
|
|
397
397
|
'rels': (
|
|
398
398
|
|
|
@@ -1405,7 +1405,10 @@ class StixBundle(s_stormtypes.Prim):
|
|
|
1405
1405
|
|
|
1406
1406
|
async def _callStorm(self, text, node):
|
|
1407
1407
|
|
|
1408
|
-
|
|
1408
|
+
varz = self.runt.getScopeVars()
|
|
1409
|
+
varz['bundle'] = self
|
|
1410
|
+
|
|
1411
|
+
opts = {'vars': varz}
|
|
1409
1412
|
query = await self.runt.snap.core.getStormQuery(text)
|
|
1410
1413
|
async with self.runt.getCmdRuntime(query, opts=opts) as runt:
|
|
1411
1414
|
|
synapse/lib/stormtypes.py
CHANGED
|
@@ -40,6 +40,7 @@ import synapse.lib.provenance as s_provenance
|
|
|
40
40
|
logger = logging.getLogger(__name__)
|
|
41
41
|
|
|
42
42
|
class Undef:
|
|
43
|
+
_storm_typename = 'undef'
|
|
43
44
|
async def stormrepr(self):
|
|
44
45
|
return '$lib.undef'
|
|
45
46
|
|
|
@@ -1085,13 +1086,6 @@ class LibBase(Lib):
|
|
|
1085
1086
|
{'name': '*vals', 'type': 'any', 'desc': 'Initial values to place in the set.', },
|
|
1086
1087
|
),
|
|
1087
1088
|
'returns': {'type': 'set', 'desc': 'The new set.', }}},
|
|
1088
|
-
{'name': 'dict', 'desc': 'Get a Storm Dict object.',
|
|
1089
|
-
'type': {'type': 'function', '_funcname': '_dict',
|
|
1090
|
-
'args': (
|
|
1091
|
-
{'name': '**kwargs', 'type': 'any',
|
|
1092
|
-
'desc': 'Initial set of keyword argumetns to place into the dict.', },
|
|
1093
|
-
),
|
|
1094
|
-
'returns': {'type': 'dict', 'desc': 'A dictionary object.', }}},
|
|
1095
1089
|
{'name': 'exit', 'desc': 'Cause a Storm Runtime to stop running.',
|
|
1096
1090
|
'type': {'type': 'function', '_funcname': '_exit',
|
|
1097
1091
|
'args': (
|
|
@@ -1147,7 +1141,7 @@ class LibBase(Lib):
|
|
|
1147
1141
|
Examples:
|
|
1148
1142
|
Create a dictionary object with a key whose value is null, and call ``$lib.fire()`` with it::
|
|
1149
1143
|
|
|
1150
|
-
cli> storm $d
|
|
1144
|
+
cli> storm $d=({"key": $lib.null}) $lib.fire('demo', d=$d)
|
|
1151
1145
|
('storm:fire', {'type': 'demo', 'data': {'d': {'key': None}}})
|
|
1152
1146
|
''',
|
|
1153
1147
|
'type': 'null', },
|
|
@@ -1227,7 +1221,7 @@ class LibBase(Lib):
|
|
|
1227
1221
|
|
|
1228
1222
|
Format and print string based on variables::
|
|
1229
1223
|
|
|
1230
|
-
cli> storm $d
|
|
1224
|
+
cli> storm $d=({"key1": (1), "key2": "two"})
|
|
1231
1225
|
for ($key, $value) in $d { $lib.print('{k} => {v}', k=$key, v=$value) }
|
|
1232
1226
|
key1 => 1
|
|
1233
1227
|
key2 => two
|
|
@@ -1377,7 +1371,6 @@ class LibBase(Lib):
|
|
|
1377
1371
|
'max': self._max,
|
|
1378
1372
|
'set': self._set,
|
|
1379
1373
|
'copy': self._copy,
|
|
1380
|
-
'dict': self._dict,
|
|
1381
1374
|
'exit': self._exit,
|
|
1382
1375
|
'guid': self._guid,
|
|
1383
1376
|
'fire': self._fire,
|
|
@@ -1677,16 +1670,125 @@ class LibBase(Lib):
|
|
|
1677
1670
|
mesg = await self._get_mesg(mesg, **kwargs)
|
|
1678
1671
|
await self.runt.warn(mesg, log=False)
|
|
1679
1672
|
|
|
1680
|
-
@stormfunc(readonly=True)
|
|
1681
|
-
async def _dict(self, **kwargs):
|
|
1682
|
-
return Dict(kwargs)
|
|
1683
|
-
|
|
1684
1673
|
@stormfunc(readonly=True)
|
|
1685
1674
|
async def _fire(self, name, **info):
|
|
1686
1675
|
info = await toprim(info)
|
|
1687
1676
|
s_common.reqjsonsafe(info)
|
|
1688
1677
|
await self.runt.snap.fire('storm:fire', type=name, data=info)
|
|
1689
1678
|
|
|
1679
|
+
@registry.registerLib
|
|
1680
|
+
class LibDict(Lib):
|
|
1681
|
+
'''
|
|
1682
|
+
A Storm Library for interacting with dictionaries.
|
|
1683
|
+
'''
|
|
1684
|
+
_storm_locals = (
|
|
1685
|
+
{'name': 'keys', 'desc': 'Retrieve a list of keys in the specified dictionary.',
|
|
1686
|
+
'type': {'type': 'function', '_funcname': '_keys',
|
|
1687
|
+
'args': (
|
|
1688
|
+
{'name': 'valu', 'type': 'dict', 'desc': 'The dictionary to operate on.'},
|
|
1689
|
+
),
|
|
1690
|
+
'returns': {'type': 'list', 'desc': 'List of keys in the specified dictionary.', }}},
|
|
1691
|
+
{'name': 'pop', 'desc': 'Remove specified key and return the corresponding value.',
|
|
1692
|
+
'type': {'type': 'function', '_funcname': '_pop',
|
|
1693
|
+
'args': (
|
|
1694
|
+
{'name': 'valu', 'type': 'dict', 'desc': 'The dictionary to operate on.'},
|
|
1695
|
+
{'name': 'key', 'type': 'str', 'desc': 'The key name of the value to pop.'},
|
|
1696
|
+
{'name': 'default', 'type': 'any', 'default': '$lib.undef',
|
|
1697
|
+
'desc': 'Optional default value to return if the key does not exist in the dictionary.'},
|
|
1698
|
+
),
|
|
1699
|
+
'returns': {'type': 'any', 'desc': 'The popped value.', }}},
|
|
1700
|
+
{'name': 'update', 'desc': 'Update the specified dictionary with keys/values from another dictionary.',
|
|
1701
|
+
'type': {'type': 'function', '_funcname': '_update',
|
|
1702
|
+
'args': (
|
|
1703
|
+
{'name': 'valu', 'type': 'dict', 'desc': 'The target dictionary (update to).'},
|
|
1704
|
+
{'name': 'other', 'type': 'dict', 'desc': 'The source dictionary (update from).'},
|
|
1705
|
+
),
|
|
1706
|
+
'returns': {'type': 'null'}}},
|
|
1707
|
+
{'name': 'values', 'desc': 'Retrieve a list of values in the specified dictionary.',
|
|
1708
|
+
'type': {'type': 'function', '_funcname': '_values',
|
|
1709
|
+
'args': (
|
|
1710
|
+
{'name': 'valu', 'type': 'dict', 'desc': 'The dictionary to operate on.'},
|
|
1711
|
+
),
|
|
1712
|
+
'returns': {'type': 'list', 'desc': 'List of values in the specified dictionary.', }}},
|
|
1713
|
+
)
|
|
1714
|
+
_storm_lib_path = ('dict',)
|
|
1715
|
+
|
|
1716
|
+
def getObjLocals(self):
|
|
1717
|
+
return {
|
|
1718
|
+
'has': self._has,
|
|
1719
|
+
'keys': self._keys,
|
|
1720
|
+
'pop': self._pop,
|
|
1721
|
+
'update': self._update,
|
|
1722
|
+
'values': self._values,
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
async def _check_type(self, valu, name='valu'):
|
|
1726
|
+
if isinstance(valu, (dict, Dict)):
|
|
1727
|
+
return
|
|
1728
|
+
|
|
1729
|
+
typ = getattr(valu, '_storm_typename', None)
|
|
1730
|
+
if typ is None:
|
|
1731
|
+
prim = await toprim(valu)
|
|
1732
|
+
typ = type(prim).__name__
|
|
1733
|
+
|
|
1734
|
+
mesg = f'{name} argument must be a dict, not {typ}.'
|
|
1735
|
+
raise s_exc.BadArg(mesg=mesg)
|
|
1736
|
+
|
|
1737
|
+
@stormfunc(readonly=True)
|
|
1738
|
+
async def _has(self, valu, name):
|
|
1739
|
+
await self._check_type(valu)
|
|
1740
|
+
valu = await toprim(valu)
|
|
1741
|
+
return name in valu
|
|
1742
|
+
|
|
1743
|
+
@stormfunc(readonly=True)
|
|
1744
|
+
async def _keys(self, valu):
|
|
1745
|
+
await self._check_type(valu)
|
|
1746
|
+
valu = await toprim(valu)
|
|
1747
|
+
return list(valu.keys())
|
|
1748
|
+
|
|
1749
|
+
@stormfunc(readonly=True)
|
|
1750
|
+
async def _pop(self, valu, key, default=undef):
|
|
1751
|
+
await self._check_type(valu)
|
|
1752
|
+
|
|
1753
|
+
real = await toprim(valu)
|
|
1754
|
+
key = await tostr(key)
|
|
1755
|
+
|
|
1756
|
+
if key not in real:
|
|
1757
|
+
if default == undef:
|
|
1758
|
+
mesg = f'Key {key} does not exist in dictionary.'
|
|
1759
|
+
raise s_exc.BadArg(mesg=mesg)
|
|
1760
|
+
return await toprim(default)
|
|
1761
|
+
|
|
1762
|
+
# Make sure we have a storm Dict
|
|
1763
|
+
valu = fromprim(valu)
|
|
1764
|
+
|
|
1765
|
+
ret = await valu.deref(key)
|
|
1766
|
+
await valu.setitem(key, undef)
|
|
1767
|
+
return ret
|
|
1768
|
+
|
|
1769
|
+
@stormfunc(readonly=True)
|
|
1770
|
+
async def _update(self, valu, other):
|
|
1771
|
+
await self._check_type(valu)
|
|
1772
|
+
await self._check_type(other, name='other')
|
|
1773
|
+
|
|
1774
|
+
valu = fromprim(valu)
|
|
1775
|
+
other = await toprim(other)
|
|
1776
|
+
|
|
1777
|
+
for k, v in other.items():
|
|
1778
|
+
await valu.setitem(k, v)
|
|
1779
|
+
|
|
1780
|
+
@stormfunc(readonly=True)
|
|
1781
|
+
async def _values(self, valu):
|
|
1782
|
+
await self._check_type(valu)
|
|
1783
|
+
|
|
1784
|
+
valu = await toprim(valu)
|
|
1785
|
+
return list(valu.values())
|
|
1786
|
+
|
|
1787
|
+
async def __call__(self, **kwargs):
|
|
1788
|
+
s_common.deprecated('$lib.dict()', curv='2.161.0')
|
|
1789
|
+
await self.runt.snap.warnonce('$lib.dict() is deprecated. Use ({}) instead.')
|
|
1790
|
+
return Dict(kwargs)
|
|
1791
|
+
|
|
1690
1792
|
@registry.registerLib
|
|
1691
1793
|
class LibPs(Lib):
|
|
1692
1794
|
'''
|
|
@@ -1700,7 +1802,7 @@ class LibPs(Lib):
|
|
|
1700
1802
|
'desc': 'The prefix of the task to stop. '
|
|
1701
1803
|
'Tasks will only be stopped if there is a single prefix match.'},
|
|
1702
1804
|
),
|
|
1703
|
-
'returns': {'type': 'boolean', 'desc': '
|
|
1805
|
+
'returns': {'type': 'boolean', 'desc': 'True if the task was cancelled, False otherwise.', }}},
|
|
1704
1806
|
{'name': 'list', 'desc': 'List tasks the current user can access.',
|
|
1705
1807
|
'type': {'type': 'function', '_funcname': '_list',
|
|
1706
1808
|
'returns': {'type': 'list', 'desc': 'A list of task definitions.', }}},
|
|
@@ -1826,7 +1928,7 @@ class LibAxon(Lib):
|
|
|
1826
1928
|
Example:
|
|
1827
1929
|
Get the Vertex Project website::
|
|
1828
1930
|
|
|
1829
|
-
$headers =
|
|
1931
|
+
$headers = ({})
|
|
1830
1932
|
$headers."User-Agent" = Foo/Bar
|
|
1831
1933
|
|
|
1832
1934
|
$resp = $lib.axon.wget("http://vertex.link", method=GET, headers=$headers)
|
|
@@ -1872,7 +1974,7 @@ class LibAxon(Lib):
|
|
|
1872
1974
|
),
|
|
1873
1975
|
'returns': {'type': 'dict', 'desc': 'A status dictionary of metadata.'}}},
|
|
1874
1976
|
{'name': 'urlfile', 'desc': '''
|
|
1875
|
-
|
|
1977
|
+
Retrieve the target URL using the wget() function and construct an inet:urlfile node from the response.
|
|
1876
1978
|
|
|
1877
1979
|
Notes:
|
|
1878
1980
|
This accepts the same arguments as ``$lib.axon.wget()``.
|
|
@@ -2365,6 +2467,9 @@ class LibBytes(Lib):
|
|
|
2365
2467
|
}
|
|
2366
2468
|
|
|
2367
2469
|
async def _libBytesUpload(self, genr):
|
|
2470
|
+
|
|
2471
|
+
self.runt.confirm(('axon', 'upload'), default=True)
|
|
2472
|
+
|
|
2368
2473
|
await self.runt.snap.core.getAxon()
|
|
2369
2474
|
async with await self.runt.snap.core.axon.upload() as upload:
|
|
2370
2475
|
async for byts in s_coro.agen(genr):
|
|
@@ -2378,6 +2483,8 @@ class LibBytes(Lib):
|
|
|
2378
2483
|
if sha256 is None:
|
|
2379
2484
|
return None
|
|
2380
2485
|
|
|
2486
|
+
self.runt.confirm(('axon', 'has'), default=True)
|
|
2487
|
+
|
|
2381
2488
|
await self.runt.snap.core.getAxon()
|
|
2382
2489
|
todo = s_common.todo('has', s_common.uhex(sha256))
|
|
2383
2490
|
ret = await self.dyncall('axon', todo)
|
|
@@ -2386,6 +2493,9 @@ class LibBytes(Lib):
|
|
|
2386
2493
|
@stormfunc(readonly=True)
|
|
2387
2494
|
async def _libBytesSize(self, sha256):
|
|
2388
2495
|
sha256 = await tostr(sha256)
|
|
2496
|
+
|
|
2497
|
+
self.runt.confirm(('axon', 'has'), default=True)
|
|
2498
|
+
|
|
2389
2499
|
await self.runt.snap.core.getAxon()
|
|
2390
2500
|
todo = s_common.todo('size', s_common.uhex(sha256))
|
|
2391
2501
|
ret = await self.dyncall('axon', todo)
|
|
@@ -2396,6 +2506,8 @@ class LibBytes(Lib):
|
|
|
2396
2506
|
mesg = '$lib.bytes.put() requires a bytes argument'
|
|
2397
2507
|
raise s_exc.BadArg(mesg=mesg)
|
|
2398
2508
|
|
|
2509
|
+
self.runt.confirm(('axon', 'upload'), default=True)
|
|
2510
|
+
|
|
2399
2511
|
await self.runt.snap.core.getAxon()
|
|
2400
2512
|
todo = s_common.todo('put', byts)
|
|
2401
2513
|
size, sha2 = await self.dyncall('axon', todo)
|
|
@@ -2405,6 +2517,9 @@ class LibBytes(Lib):
|
|
|
2405
2517
|
@stormfunc(readonly=True)
|
|
2406
2518
|
async def _libBytesHashset(self, sha256):
|
|
2407
2519
|
sha256 = await tostr(sha256)
|
|
2520
|
+
|
|
2521
|
+
self.runt.confirm(('axon', 'has'), default=True)
|
|
2522
|
+
|
|
2408
2523
|
await self.runt.snap.core.getAxon()
|
|
2409
2524
|
todo = s_common.todo('hashset', s_common.uhex(sha256))
|
|
2410
2525
|
ret = await self.dyncall('axon', todo)
|
|
@@ -3207,7 +3322,7 @@ class Pipe(StormType):
|
|
|
3207
3322
|
{'name': 'put', 'desc': 'Add a single item to the Pipe.',
|
|
3208
3323
|
'type': {'type': 'function', '_funcname': '_methPipePut',
|
|
3209
3324
|
'args': (
|
|
3210
|
-
{'name': 'item', 'type': 'any', 'desc': '
|
|
3325
|
+
{'name': 'item', 'type': 'any', 'desc': 'An object to add to the Pipe.', },
|
|
3211
3326
|
),
|
|
3212
3327
|
'returns': {'type': 'null', }}},
|
|
3213
3328
|
{'name': 'puts', 'desc': 'Add a list of items to the Pipe.',
|
|
@@ -4056,6 +4171,9 @@ class Str(Prim):
|
|
|
4056
4171
|
'desc': 'Keyword values which are substituted into the string.', },
|
|
4057
4172
|
),
|
|
4058
4173
|
'returns': {'type': 'str', 'desc': 'The new string.', }}},
|
|
4174
|
+
{'name': 'json', 'desc': 'Parse a JSON string and return the deserialized data.',
|
|
4175
|
+
'type': {'type': 'function', '_funcname': '_methStrJson', 'args': (),
|
|
4176
|
+
'returns': {'type': 'prim', 'desc': 'The JSON deserialized object.', }}},
|
|
4059
4177
|
)
|
|
4060
4178
|
_storm_typename = 'str'
|
|
4061
4179
|
_ismutable = False
|
|
@@ -4085,6 +4203,7 @@ class Str(Prim):
|
|
|
4085
4203
|
'slice': self._methStrSlice,
|
|
4086
4204
|
'reverse': self._methStrReverse,
|
|
4087
4205
|
'format': self._methStrFormat,
|
|
4206
|
+
'json': self._methStrJson,
|
|
4088
4207
|
}
|
|
4089
4208
|
|
|
4090
4209
|
def __int__(self):
|
|
@@ -4200,6 +4319,14 @@ class Str(Prim):
|
|
|
4200
4319
|
async def _methStrReverse(self):
|
|
4201
4320
|
return self.valu[::-1]
|
|
4202
4321
|
|
|
4322
|
+
@stormfunc(readonly=True)
|
|
4323
|
+
async def _methStrJson(self):
|
|
4324
|
+
try:
|
|
4325
|
+
return json.loads(self.valu, strict=True)
|
|
4326
|
+
except Exception as e:
|
|
4327
|
+
mesg = f'Text is not valid JSON: {self.valu}'
|
|
4328
|
+
raise s_exc.BadJsonText(mesg=mesg)
|
|
4329
|
+
|
|
4203
4330
|
@registry.registerType
|
|
4204
4331
|
class Bytes(Prim):
|
|
4205
4332
|
'''
|
|
@@ -5536,7 +5663,7 @@ class NodeData(Prim):
|
|
|
5536
5663
|
{'name': 'name', 'type': 'str', 'desc': 'Name of the data to get.', },
|
|
5537
5664
|
),
|
|
5538
5665
|
'returns': {'type': 'prim', 'desc': 'The stored node data.', }}},
|
|
5539
|
-
{'name': 'pop', 'desc': '
|
|
5666
|
+
{'name': 'pop', 'desc': 'Pop (remove) a the Node data from the Node.',
|
|
5540
5667
|
'type': {'type': 'function', '_funcname': '_popNodeData',
|
|
5541
5668
|
'args': (
|
|
5542
5669
|
{'name': 'name', 'type': 'str', 'desc': 'The name of the data to remove from the node.', },
|
|
@@ -7591,6 +7718,8 @@ class View(Prim):
|
|
|
7591
7718
|
mesg = 'You are not a member of a role with voting privileges for this merge request.'
|
|
7592
7719
|
raise s_exc.AuthDeny(mesg=mesg)
|
|
7593
7720
|
|
|
7721
|
+
view.reqValidVoter(self.runt.user.iden)
|
|
7722
|
+
|
|
7594
7723
|
vote = {'user': self.runt.user.iden, 'approved': await tobool(approved)}
|
|
7595
7724
|
|
|
7596
7725
|
if comment is not None:
|
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,
|
|
226
|
+
version = (2, 162, 0)
|
|
227
227
|
verstring = '.'.join([str(x) for x in version])
|
|
228
|
-
commit = '
|
|
228
|
+
commit = '4f4fa04685adceb99beb700b2d51b0f99afe801b'
|
synapse/lib/view.py
CHANGED
|
@@ -268,15 +268,28 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
268
268
|
vote['offset'] = await self.layers[0].getEditIndx()
|
|
269
269
|
return await self._push('merge:vote:set', vote)
|
|
270
270
|
|
|
271
|
+
def reqValidVoter(self, useriden):
|
|
272
|
+
|
|
273
|
+
merge = self.getMergeRequest()
|
|
274
|
+
if merge is None:
|
|
275
|
+
raise s_exc.BadState(mesg=f'View ({self.iden}) does not have a merge request.')
|
|
276
|
+
|
|
277
|
+
if merge.get('creator') == useriden:
|
|
278
|
+
raise s_exc.AuthDeny(mesg='A user may not vote for their own merge request.')
|
|
279
|
+
|
|
271
280
|
@s_nexus.Pusher.onPush('merge:vote:set')
|
|
272
281
|
async def _setMergeVote(self, vote):
|
|
273
282
|
|
|
274
283
|
self.reqParentQuorum()
|
|
275
284
|
s_schemas.reqValidVote(vote)
|
|
276
285
|
|
|
277
|
-
|
|
286
|
+
useriden = vote.get('user')
|
|
287
|
+
|
|
288
|
+
self.reqValidVoter(useriden)
|
|
289
|
+
|
|
290
|
+
bidn = s_common.uhex(useriden)
|
|
278
291
|
|
|
279
|
-
self.core.slab.put(self.bidn + b'merge:vote' +
|
|
292
|
+
self.core.slab.put(self.bidn + b'merge:vote' + bidn, s_msgpack.en(vote), db='view:meta')
|
|
280
293
|
|
|
281
294
|
await self.core.feedBeholder('view:merge:vote:set', {'view': self.iden, 'vote': vote})
|
|
282
295
|
|
synapse/models/inet.py
CHANGED
|
@@ -1560,6 +1560,15 @@ class InetModule(s_module.CoreModule):
|
|
|
1560
1560
|
('headers', ('array', {'type': 'inet:email:header'}), {
|
|
1561
1561
|
'doc': 'An array of email headers from the message.'}),
|
|
1562
1562
|
|
|
1563
|
+
('received:from:ipv4', ('inet:ipv4', {}), {
|
|
1564
|
+
'doc': 'The sending SMTP server IPv4, potentially from the Received: header.'}),
|
|
1565
|
+
|
|
1566
|
+
('received:from:ipv6', ('inet:ipv6', {}), {
|
|
1567
|
+
'doc': 'The sending SMTP server IPv6, potentially from the Received: header.'}),
|
|
1568
|
+
|
|
1569
|
+
('received:from:fqdn', ('inet:fqdn', {}), {
|
|
1570
|
+
'doc': 'The sending server FQDN, potentially from the Received: header.'}),
|
|
1571
|
+
|
|
1563
1572
|
)),
|
|
1564
1573
|
|
|
1565
1574
|
('inet:email:header', {}, (
|