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.

Files changed (71) hide show
  1. synapse/cortex.py +12 -7
  2. synapse/daemon.py +7 -2
  3. synapse/lib/agenda.py +8 -2
  4. synapse/lib/aha.py +4 -4
  5. synapse/lib/ast.py +3 -3
  6. synapse/lib/cell.py +20 -1
  7. synapse/lib/hiveauth.py +1 -1
  8. synapse/lib/httpapi.py +5 -0
  9. synapse/lib/layer.py +21 -1
  10. synapse/lib/nexus.py +9 -5
  11. synapse/lib/node.py +3 -4
  12. synapse/lib/rstorm.py +16 -0
  13. synapse/lib/schemas.py +2 -1
  14. synapse/lib/snap.py +20 -11
  15. synapse/lib/storm.py +19 -3
  16. synapse/lib/stormhttp.py +14 -2
  17. synapse/lib/stormlib/easyperm.py +5 -2
  18. synapse/lib/stormlib/gen.py +119 -44
  19. synapse/lib/stormlib/stix.py +6 -3
  20. synapse/lib/stormlib/vault.py +32 -15
  21. synapse/lib/stormtypes.py +187 -21
  22. synapse/lib/trigger.py +2 -0
  23. synapse/lib/version.py +2 -2
  24. synapse/lib/view.py +42 -10
  25. synapse/models/inet.py +9 -0
  26. synapse/models/infotech.py +28 -26
  27. synapse/models/orgs.py +3 -0
  28. synapse/models/proj.py +9 -2
  29. synapse/models/risk.py +32 -0
  30. synapse/telepath.py +6 -2
  31. synapse/tests/files/rstorm/testsvc.py +8 -1
  32. synapse/tests/files/stormpkg/testpkg.yaml +4 -0
  33. synapse/tests/test_axon.py +4 -4
  34. synapse/tests/test_cortex.py +66 -8
  35. synapse/tests/test_daemon.py +19 -0
  36. synapse/tests/test_lib_agenda.py +8 -0
  37. synapse/tests/test_lib_aha.py +18 -3
  38. synapse/tests/test_lib_ast.py +38 -16
  39. synapse/tests/test_lib_cell.py +3 -0
  40. synapse/tests/test_lib_grammar.py +4 -4
  41. synapse/tests/test_lib_httpapi.py +59 -0
  42. synapse/tests/test_lib_nexus.py +63 -0
  43. synapse/tests/test_lib_rstorm.py +38 -2
  44. synapse/tests/test_lib_snap.py +10 -0
  45. synapse/tests/test_lib_storm.py +61 -20
  46. synapse/tests/test_lib_stormhttp.py +21 -21
  47. synapse/tests/test_lib_stormlib_auth.py +3 -3
  48. synapse/tests/test_lib_stormlib_cell.py +1 -1
  49. synapse/tests/test_lib_stormlib_cortex.py +50 -2
  50. synapse/tests/test_lib_stormlib_gen.py +77 -0
  51. synapse/tests/test_lib_stormlib_json.py +2 -2
  52. synapse/tests/test_lib_stormlib_macro.py +1 -1
  53. synapse/tests/test_lib_stormlib_modelext.py +37 -37
  54. synapse/tests/test_lib_stormlib_oauth.py +20 -20
  55. synapse/tests/test_lib_stormlib_stix.py +3 -1
  56. synapse/tests/test_lib_stormlib_vault.py +1 -1
  57. synapse/tests/test_lib_stormtypes.py +159 -47
  58. synapse/tests/test_lib_stormwhois.py +1 -1
  59. synapse/tests/test_lib_trigger.py +11 -11
  60. synapse/tests/test_lib_view.py +23 -1
  61. synapse/tests/test_model_crypto.py +1 -1
  62. synapse/tests/test_model_inet.py +6 -0
  63. synapse/tests/test_model_orgs.py +2 -1
  64. synapse/tests/test_model_proj.py +6 -0
  65. synapse/tests/test_model_risk.py +10 -0
  66. synapse/tests/test_tools_storm.py +1 -1
  67. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/METADATA +5 -3
  68. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/RECORD +71 -71
  69. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/LICENSE +0 -0
  70. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/WHEEL +0 -0
  71. {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=$lib.dict(key=$lib.null) $lib.fire('demo', d=$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=$lib.dict(key1=(1), key2="two")
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': ' True if the task was cancelled, False otherwise.', }}},
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 = $lib.dict()
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
- Retrive the target URL using the wget() function and construct an inet:urlfile node from the response.
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': ' An object to add to the Pipe.', },
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': ' Pop (remove) a the Node data from the Node.',
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, 160, 0)
226
+ version = (2, 162, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '26159d5e188ef3c0dbaf2abf2e1aec803e90e7f3'
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
- return s_msgpack.un(byts)
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
- uidn = s_common.uhex(vote.get('user'))
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' + uidn, s_msgpack.en(vote), db='view:meta')
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
- if byts is not None:
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
- vdef = self.info.pack()
1062
- vdef['quorum'] = s_msgpack.deepcopy(valu)
1063
-
1064
- s_schemas.reqValidView(vdef)
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', {}, (
@@ -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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK group ID.',
1148
- 'ex': 'cno.mitre.g0100',
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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK tactic.',
1178
- 'ex': 'cno.mitre.ta0100',
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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK technique.',
1206
- 'ex': 'cno.mitre.t0100',
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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK software.',
1241
- 'ex': 'cno.mitre.s0100',
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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK mitigation.',
1268
- 'ex': 'cno.mitre.m0100',
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
- 'doc': 'The synapse tag used to annotate nodes included in this ATT&CK campaign.',
1319
- 'ex': 'cno.mitre.c0028',
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', {}), {