synapse 2.160.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/cortex.py +12 -7
- synapse/daemon.py +7 -2
- synapse/lib/agenda.py +8 -2
- synapse/lib/aha.py +4 -4
- synapse/lib/ast.py +3 -3
- synapse/lib/cell.py +20 -1
- synapse/lib/hiveauth.py +1 -1
- synapse/lib/httpapi.py +5 -0
- synapse/lib/layer.py +21 -1
- synapse/lib/nexus.py +9 -5
- synapse/lib/node.py +3 -4
- synapse/lib/rstorm.py +16 -0
- synapse/lib/schemas.py +2 -1
- synapse/lib/snap.py +20 -11
- synapse/lib/storm.py +19 -3
- synapse/lib/stormhttp.py +14 -2
- synapse/lib/stormlib/easyperm.py +5 -2
- synapse/lib/stormlib/gen.py +119 -44
- synapse/lib/stormlib/stix.py +6 -3
- synapse/lib/stormlib/vault.py +32 -15
- synapse/lib/stormtypes.py +187 -21
- synapse/lib/trigger.py +2 -0
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +42 -10
- 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 +6 -2
- 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 +66 -8
- synapse/tests/test_daemon.py +19 -0
- synapse/tests/test_lib_agenda.py +8 -0
- synapse/tests/test_lib_aha.py +18 -3
- synapse/tests/test_lib_ast.py +38 -16
- synapse/tests/test_lib_cell.py +3 -0
- synapse/tests/test_lib_grammar.py +4 -4
- synapse/tests/test_lib_httpapi.py +59 -0
- synapse/tests/test_lib_nexus.py +63 -0
- synapse/tests/test_lib_rstorm.py +38 -2
- synapse/tests/test_lib_snap.py +10 -0
- synapse/tests/test_lib_storm.py +61 -20
- synapse/tests/test_lib_stormhttp.py +21 -21
- 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_gen.py +77 -0
- 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_stormlib_vault.py +1 -1
- synapse/tests/test_lib_stormtypes.py +159 -47
- 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.160.0.dist-info → synapse-2.162.0.dist-info}/METADATA +5 -3
- {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/RECORD +71 -71
- {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/LICENSE +0 -0
- {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/WHEEL +0 -0
- {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/top_level.txt +0 -0
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
|
|
|
@@ -51,8 +52,8 @@ def confirm(perm, gateiden=None):
|
|
|
51
52
|
def allowed(perm, gateiden=None):
|
|
52
53
|
return s_scope.get('runt').allowed(perm, gateiden=gateiden)
|
|
53
54
|
|
|
54
|
-
def confirmEasyPerm(item, perm):
|
|
55
|
-
return s_scope.get('runt').confirmEasyPerm(item, perm)
|
|
55
|
+
def confirmEasyPerm(item, perm, mesg=None):
|
|
56
|
+
return s_scope.get('runt').confirmEasyPerm(item, perm, mesg=mesg)
|
|
56
57
|
|
|
57
58
|
def allowedEasyPerm(item, perm):
|
|
58
59
|
return s_scope.get('runt').allowedEasyPerm(item, perm)
|
|
@@ -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.', },
|
|
@@ -7177,6 +7304,17 @@ class View(Prim):
|
|
|
7177
7304
|
'args': (),
|
|
7178
7305
|
'returns': {'type': 'null', }}},
|
|
7179
7306
|
|
|
7307
|
+
{'name': 'getMergeRequestSummary',
|
|
7308
|
+
'desc': 'Return the merge request, votes, parent quorum definition, and current layer offset.',
|
|
7309
|
+
'type': {'type': 'function', '_funcname': 'getMergeRequestSummary',
|
|
7310
|
+
'args': (),
|
|
7311
|
+
'returns': {'type': 'dict', 'desc': 'The summary info.'}}},
|
|
7312
|
+
|
|
7313
|
+
{'name': 'getMergeRequest', 'desc': 'Return the existing merge request or null.',
|
|
7314
|
+
'type': {'type': 'function', '_funcname': 'getMergeRequest',
|
|
7315
|
+
'args': (),
|
|
7316
|
+
'returns': {'type': 'dict', 'desc': 'The merge request.'}}},
|
|
7317
|
+
|
|
7180
7318
|
{'name': 'setMergeRequest', 'desc': 'Setup a merge request for the view in the current state.',
|
|
7181
7319
|
'type': {'type': 'function', '_funcname': 'setMergeRequest',
|
|
7182
7320
|
'args': (
|
|
@@ -7188,6 +7326,7 @@ class View(Prim):
|
|
|
7188
7326
|
'type': {'type': 'function', '_funcname': 'delMergeRequest',
|
|
7189
7327
|
'args': (),
|
|
7190
7328
|
'returns': {'type': 'dict', 'desc': 'The deleted merge request.'}}},
|
|
7329
|
+
|
|
7191
7330
|
{'name': 'setMergeVote', 'desc': 'Register a vote for or against the current merge request.',
|
|
7192
7331
|
'type': {'type': 'function', '_funcname': 'setMergeVote',
|
|
7193
7332
|
'args': (
|
|
@@ -7197,6 +7336,7 @@ class View(Prim):
|
|
|
7197
7336
|
'desc': 'A comment attached to the vote.'},
|
|
7198
7337
|
),
|
|
7199
7338
|
'returns': {'type': 'dict', 'desc': 'The vote record that was created.'}}},
|
|
7339
|
+
|
|
7200
7340
|
{'name': 'delMergeVote', 'desc': '''
|
|
7201
7341
|
Remove a previously created merge vote.
|
|
7202
7342
|
|
|
@@ -7256,6 +7396,8 @@ class View(Prim):
|
|
|
7256
7396
|
'getMerges': self.getMerges,
|
|
7257
7397
|
'delMergeVote': self.delMergeVote,
|
|
7258
7398
|
'setMergeVote': self.setMergeVote,
|
|
7399
|
+
'getMergeRequest': self.getMergeRequest,
|
|
7400
|
+
'getMergeRequestSummary': self.getMergeRequestSummary,
|
|
7259
7401
|
'delMergeRequest': self.delMergeRequest,
|
|
7260
7402
|
'setMergeRequest': self.setMergeRequest,
|
|
7261
7403
|
}
|
|
@@ -7516,6 +7658,28 @@ class View(Prim):
|
|
|
7516
7658
|
async for merge in view.getMerges():
|
|
7517
7659
|
yield merge
|
|
7518
7660
|
|
|
7661
|
+
async def getMergeRequestSummary(self):
|
|
7662
|
+
|
|
7663
|
+
view = self._reqView()
|
|
7664
|
+
self.runt.confirm(('view', 'read'), gateiden=view.iden)
|
|
7665
|
+
|
|
7666
|
+
retn = {
|
|
7667
|
+
'quorum': view.reqParentQuorum(),
|
|
7668
|
+
'merge': view.getMergeRequest(),
|
|
7669
|
+
'merging': view.merging,
|
|
7670
|
+
'votes': [vote async for vote in view.getMergeVotes()],
|
|
7671
|
+
'offset': await view.layers[0].getEditIndx(),
|
|
7672
|
+
}
|
|
7673
|
+
return retn
|
|
7674
|
+
|
|
7675
|
+
async def getMergeRequest(self):
|
|
7676
|
+
|
|
7677
|
+
view = self._reqView()
|
|
7678
|
+
self.runt.confirm(('view', 'read'), gateiden=view.iden)
|
|
7679
|
+
|
|
7680
|
+
quorum = view.reqParentQuorum()
|
|
7681
|
+
return view.getMergeRequest()
|
|
7682
|
+
|
|
7519
7683
|
async def delMergeRequest(self):
|
|
7520
7684
|
|
|
7521
7685
|
view = self._reqView()
|
|
@@ -7554,6 +7718,8 @@ class View(Prim):
|
|
|
7554
7718
|
mesg = 'You are not a member of a role with voting privileges for this merge request.'
|
|
7555
7719
|
raise s_exc.AuthDeny(mesg=mesg)
|
|
7556
7720
|
|
|
7721
|
+
view.reqValidVoter(self.runt.user.iden)
|
|
7722
|
+
|
|
7557
7723
|
vote = {'user': self.runt.user.iden, 'approved': await tobool(approved)}
|
|
7558
7724
|
|
|
7559
7725
|
if comment is not None:
|
synapse/lib/trigger.py
CHANGED
|
@@ -49,6 +49,7 @@ TrigSchema = {
|
|
|
49
49
|
'storm': {'type': 'string'},
|
|
50
50
|
'async': {'type': 'boolean'},
|
|
51
51
|
'enabled': {'type': 'boolean'},
|
|
52
|
+
'created': {'type': 'integer', 'minimum': 0},
|
|
52
53
|
},
|
|
53
54
|
'additionalProperties': True,
|
|
54
55
|
'required': ['iden', 'user', 'storm', 'enabled'],
|
|
@@ -594,6 +595,7 @@ class Trigger:
|
|
|
594
595
|
'storm': self.tdef.get('storm'),
|
|
595
596
|
'enabled': self.tdef.get('enabled'),
|
|
596
597
|
'user': self.tdef.get('user'),
|
|
598
|
+
'.created': self.tdef.get('created')
|
|
597
599
|
}
|
|
598
600
|
|
|
599
601
|
tag = self.tdef.get('tag')
|
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
|
@@ -190,17 +190,23 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
190
190
|
s_schemas.reqValidMerge(mergeinfo)
|
|
191
191
|
lkey = self.bidn + b'merge:req'
|
|
192
192
|
self.core.slab.put(lkey, s_msgpack.en(mergeinfo), db='view:meta')
|
|
193
|
+
await self.core.feedBeholder('view:merge:request:set', {'view': self.iden, 'merge': mergeinfo})
|
|
193
194
|
return mergeinfo
|
|
194
195
|
|
|
195
|
-
@s_nexus.Pusher.onPushAuto('merge:del')
|
|
196
196
|
async def delMergeRequest(self):
|
|
197
|
+
return await self._push('merge:del')
|
|
198
|
+
|
|
199
|
+
@s_nexus.Pusher.onPush('merge:del')
|
|
200
|
+
async def _delMergeRequest(self):
|
|
197
201
|
self.reqParentQuorum()
|
|
198
202
|
byts = self.core.slab.pop(self.bidn + b'merge:req', db='view:meta')
|
|
199
203
|
|
|
200
204
|
await self._delMergeMeta()
|
|
201
205
|
|
|
202
206
|
if byts is not None:
|
|
203
|
-
|
|
207
|
+
merge = s_msgpack.un(byts)
|
|
208
|
+
await self.core.feedBeholder('view:merge:request:del', {'view': self.iden, 'merge': merge})
|
|
209
|
+
return merge
|
|
204
210
|
|
|
205
211
|
async def _delMergeMeta(self):
|
|
206
212
|
for lkey in self.core.slab.scanKeysByPref(self.bidn + b'merge:', db='view:meta'):
|
|
@@ -262,15 +268,30 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
262
268
|
vote['offset'] = await self.layers[0].getEditIndx()
|
|
263
269
|
return await self._push('merge:vote:set', vote)
|
|
264
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
|
+
|
|
265
280
|
@s_nexus.Pusher.onPush('merge:vote:set')
|
|
266
281
|
async def _setMergeVote(self, vote):
|
|
267
282
|
|
|
268
283
|
self.reqParentQuorum()
|
|
269
284
|
s_schemas.reqValidVote(vote)
|
|
270
285
|
|
|
271
|
-
|
|
286
|
+
useriden = vote.get('user')
|
|
287
|
+
|
|
288
|
+
self.reqValidVoter(useriden)
|
|
289
|
+
|
|
290
|
+
bidn = s_common.uhex(useriden)
|
|
272
291
|
|
|
273
|
-
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')
|
|
293
|
+
|
|
294
|
+
await self.core.feedBeholder('view:merge:vote:set', {'view': self.iden, 'vote': vote})
|
|
274
295
|
|
|
275
296
|
tick = vote.get('created')
|
|
276
297
|
await self.tryToMerge(tick)
|
|
@@ -286,12 +307,16 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
286
307
|
self.reqParentQuorum()
|
|
287
308
|
uidn = s_common.uhex(useriden)
|
|
288
309
|
|
|
310
|
+
vote = None
|
|
289
311
|
byts = self.core.slab.pop(self.bidn + b'merge:vote' + uidn, db='view:meta')
|
|
290
312
|
|
|
313
|
+
if byts is not None:
|
|
314
|
+
vote = s_msgpack.un(byts)
|
|
315
|
+
await self.core.feedBeholder('view:merge:vote:del', {'view': self.iden, 'vote': vote})
|
|
316
|
+
|
|
291
317
|
await self.tryToMerge(tick)
|
|
292
318
|
|
|
293
|
-
|
|
294
|
-
return s_msgpack.un(byts)
|
|
319
|
+
return vote
|
|
295
320
|
|
|
296
321
|
async def initMergeTask(self):
|
|
297
322
|
|
|
@@ -1058,10 +1083,16 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
1058
1083
|
# TODO hack a schema test until the setViewInfo API is updated to
|
|
1059
1084
|
# enforce ( which will need to be done very carefully to prevent
|
|
1060
1085
|
# existing non-compliant values from causing issues with existing views )
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1086
|
+
if valu is not None:
|
|
1087
|
+
vdef = self.info.pack()
|
|
1088
|
+
vdef['quorum'] = s_msgpack.deepcopy(valu)
|
|
1089
|
+
s_schemas.reqValidView(vdef)
|
|
1090
|
+
else:
|
|
1091
|
+
for view in self.core.views.values():
|
|
1092
|
+
if view.parent != self:
|
|
1093
|
+
continue
|
|
1094
|
+
if view.getMergeRequest() is not None:
|
|
1095
|
+
await view._delMergeRequest()
|
|
1065
1096
|
|
|
1066
1097
|
if valu is None:
|
|
1067
1098
|
await self.info.pop(name)
|
|
@@ -1387,6 +1418,7 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
1387
1418
|
|
|
1388
1419
|
root = await self.core.auth.getUserByName('root')
|
|
1389
1420
|
|
|
1421
|
+
tdef.setdefault('created', s_common.now())
|
|
1390
1422
|
tdef.setdefault('user', root.iden)
|
|
1391
1423
|
tdef.setdefault('async', False)
|
|
1392
1424
|
tdef.setdefault('enabled', True)
|
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', {}, (
|
synapse/models/infotech.py
CHANGED
|
@@ -1143,10 +1143,11 @@ class ItModule(s_module.CoreModule):
|
|
|
1143
1143
|
('url', ('inet:url', {}), {
|
|
1144
1144
|
'doc': 'The URL that documents the ATT&CK group.',
|
|
1145
1145
|
}),
|
|
1146
|
+
|
|
1146
1147
|
('tag', ('syn:tag', {}), {
|
|
1147
|
-
'
|
|
1148
|
-
'
|
|
1149
|
-
|
|
1148
|
+
'deprecated': True,
|
|
1149
|
+
'doc': 'Deprecated. Please use a risk:threat:tag.'}),
|
|
1150
|
+
|
|
1150
1151
|
('references', ('array', {'type': 'inet:url', 'uniq': True}), {
|
|
1151
1152
|
'doc': 'An array of URLs that document the ATT&CK group.',
|
|
1152
1153
|
}),
|
|
@@ -1171,12 +1172,12 @@ class ItModule(s_module.CoreModule):
|
|
|
1171
1172
|
'disp': {'hint': 'text'},
|
|
1172
1173
|
}),
|
|
1173
1174
|
('url', ('inet:url', {}), {
|
|
1174
|
-
'doc': 'The URL that documents the ATT&CK tactic.',
|
|
1175
|
-
|
|
1175
|
+
'doc': 'The URL that documents the ATT&CK tactic.'}),
|
|
1176
|
+
|
|
1176
1177
|
('tag', ('syn:tag', {}), {
|
|
1177
|
-
'
|
|
1178
|
-
'
|
|
1179
|
-
|
|
1178
|
+
'deprecated': True,
|
|
1179
|
+
'doc': 'Deprecated.'}),
|
|
1180
|
+
|
|
1180
1181
|
('references', ('array', {'type': 'inet:url', 'uniq': True}), {
|
|
1181
1182
|
'doc': 'An array of URLs that document the ATT&CK tactic.',
|
|
1182
1183
|
}),
|
|
@@ -1199,12 +1200,12 @@ class ItModule(s_module.CoreModule):
|
|
|
1199
1200
|
'disp': {'hint': 'text'},
|
|
1200
1201
|
}),
|
|
1201
1202
|
('url', ('inet:url', {}), {
|
|
1202
|
-
'doc': 'The URL that documents the ATT&CK technique.',
|
|
1203
|
-
|
|
1203
|
+
'doc': 'The URL that documents the ATT&CK technique.'}),
|
|
1204
|
+
|
|
1204
1205
|
('tag', ('syn:tag', {}), {
|
|
1205
|
-
'
|
|
1206
|
-
'
|
|
1207
|
-
|
|
1206
|
+
'deprecated': True,
|
|
1207
|
+
'doc': 'Deprecated. Please use ou:technique:tag.'}),
|
|
1208
|
+
|
|
1208
1209
|
('references', ('array', {'type': 'inet:url', 'uniq': True}), {
|
|
1209
1210
|
'doc': 'An array of URLs that document the ATT&CK technique.',
|
|
1210
1211
|
}),
|
|
@@ -1234,12 +1235,12 @@ class ItModule(s_module.CoreModule):
|
|
|
1234
1235
|
'doc': 'If deprecated, this field may contain the current value for the software.',
|
|
1235
1236
|
}),
|
|
1236
1237
|
('url', ('inet:url', {}), {
|
|
1237
|
-
'doc': 'The URL that documents the ATT&CK software.',
|
|
1238
|
-
|
|
1238
|
+
'doc': 'The URL that documents the ATT&CK software.'}),
|
|
1239
|
+
|
|
1239
1240
|
('tag', ('syn:tag', {}), {
|
|
1240
|
-
'
|
|
1241
|
-
'
|
|
1242
|
-
|
|
1241
|
+
'deprecated': True,
|
|
1242
|
+
'doc': 'Deprecated. Please use risk:tool:software:tag.'}),
|
|
1243
|
+
|
|
1243
1244
|
('references', ('array', {'type': 'inet:url', 'uniq': True}), {
|
|
1244
1245
|
'doc': 'An array of URLs that document the ATT&CK software.',
|
|
1245
1246
|
}),
|
|
@@ -1261,12 +1262,12 @@ class ItModule(s_module.CoreModule):
|
|
|
1261
1262
|
'disp': {'hint': 'text'},
|
|
1262
1263
|
}),
|
|
1263
1264
|
('url', ('inet:url', {}), {
|
|
1264
|
-
'doc': 'The URL that documents the ATT&CK mitigation.',
|
|
1265
|
-
|
|
1265
|
+
'doc': 'The URL that documents the ATT&CK mitigation.'}),
|
|
1266
|
+
|
|
1266
1267
|
('tag', ('syn:tag', {}), {
|
|
1267
|
-
'
|
|
1268
|
-
'
|
|
1269
|
-
|
|
1268
|
+
'deprecated': True,
|
|
1269
|
+
'doc': 'Deprecated. Please use risk:mitigation:tag.'}),
|
|
1270
|
+
|
|
1270
1271
|
('references', ('array', {'type': 'inet:url', 'uniq': True}), {
|
|
1271
1272
|
'doc': 'An array of URLs that document the ATT&CK mitigation.',
|
|
1272
1273
|
}),
|
|
@@ -1314,10 +1315,11 @@ class ItModule(s_module.CoreModule):
|
|
|
1314
1315
|
'doc': 'The time that the campaign was created by Mitre.'}),
|
|
1315
1316
|
('updated', ('time', {}), {
|
|
1316
1317
|
'doc': 'The time that the campaign was last updated by Mitre.'}),
|
|
1318
|
+
|
|
1317
1319
|
('tag', ('syn:tag', {}), {
|
|
1318
|
-
'
|
|
1319
|
-
'
|
|
1320
|
-
|
|
1320
|
+
'deprecated': True,
|
|
1321
|
+
'doc': 'Deprecated. Please use ou:campaign:tag.'}),
|
|
1322
|
+
|
|
1321
1323
|
)),
|
|
1322
1324
|
('it:mitre:attack:flow', {}, (
|
|
1323
1325
|
('name', ('str', {}), {
|
synapse/models/orgs.py
CHANGED
|
@@ -346,6 +346,9 @@ class OuModule(s_module.CoreModule):
|
|
|
346
346
|
('name', ('str', {}), {
|
|
347
347
|
'doc': 'The friendly name of the id number type.',
|
|
348
348
|
}),
|
|
349
|
+
('url', ('inet:url', {}), {
|
|
350
|
+
'doc': 'The official URL of the issuer.',
|
|
351
|
+
}),
|
|
349
352
|
)),
|
|
350
353
|
('ou:id:number', {}, (
|
|
351
354
|
('type', ('ou:id:type', {}), {
|