synapse 2.201.0__py311-none-any.whl → 2.203.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 (106) hide show
  1. synapse/axon.py +4 -4
  2. synapse/cmds/cortex.py +4 -6
  3. synapse/cmds/hive.py +10 -10
  4. synapse/common.py +17 -58
  5. synapse/cortex.py +36 -29
  6. synapse/data/__init__.py +3 -2
  7. synapse/data/iana.uris.mpk +1 -0
  8. synapse/lib/autodoc.py +3 -3
  9. synapse/lib/base.py +2 -12
  10. synapse/lib/cell.py +9 -13
  11. synapse/lib/cli.py +2 -2
  12. synapse/lib/config.py +2 -2
  13. synapse/lib/encoding.py +4 -3
  14. synapse/lib/httpapi.py +7 -11
  15. synapse/lib/json.py +224 -0
  16. synapse/lib/lmdbslab.py +1 -1
  17. synapse/lib/oauth.py +176 -54
  18. synapse/lib/parser.py +2 -1
  19. synapse/lib/rstorm.py +18 -14
  20. synapse/lib/schemas.py +87 -1
  21. synapse/lib/scrape.py +35 -13
  22. synapse/lib/snap.py +2 -1
  23. synapse/lib/storm.lark +5 -4
  24. synapse/lib/storm.py +2 -2
  25. synapse/lib/storm_format.py +2 -1
  26. synapse/lib/stormhttp.py +11 -13
  27. synapse/lib/stormlib/aha.py +4 -4
  28. synapse/lib/stormlib/auth.py +1 -1
  29. synapse/lib/stormlib/cache.py +2 -2
  30. synapse/lib/stormlib/cortex.py +5 -5
  31. synapse/lib/stormlib/graph.py +1 -1
  32. synapse/lib/stormlib/imap.py +1 -1
  33. synapse/lib/stormlib/json.py +8 -11
  34. synapse/lib/stormlib/model.py +1 -1
  35. synapse/lib/stormlib/notifications.py +2 -2
  36. synapse/lib/stormlib/oauth.py +105 -2
  37. synapse/lib/stormlib/stats.py +4 -0
  38. synapse/lib/stormlib/stix.py +3 -4
  39. synapse/lib/stormlib/vault.py +6 -6
  40. synapse/lib/stormlib/xml.py +2 -2
  41. synapse/lib/stormtypes.py +19 -28
  42. synapse/lib/structlog.py +3 -3
  43. synapse/lib/types.py +2 -1
  44. synapse/lib/version.py +2 -2
  45. synapse/lib/view.py +7 -3
  46. synapse/models/base.py +51 -2
  47. synapse/telepath.py +79 -18
  48. synapse/tests/files/__init__.py +0 -1
  49. synapse/tests/test_axon.py +1 -1
  50. synapse/tests/test_cmds_cortex.py +3 -2
  51. synapse/tests/test_cmds_hive.py +4 -4
  52. synapse/tests/test_common.py +29 -19
  53. synapse/tests/test_cortex.py +28 -8
  54. synapse/tests/test_lib_ast.py +3 -3
  55. synapse/tests/test_lib_autodoc.py +5 -5
  56. synapse/tests/test_lib_base.py +1 -1
  57. synapse/tests/test_lib_cell.py +24 -7
  58. synapse/tests/test_lib_config.py +2 -2
  59. synapse/tests/test_lib_encoding.py +2 -2
  60. synapse/tests/test_lib_grammar.py +68 -64
  61. synapse/tests/test_lib_httpapi.py +13 -13
  62. synapse/tests/test_lib_json.py +219 -0
  63. synapse/tests/test_lib_multislabseqn.py +2 -1
  64. synapse/tests/test_lib_node.py +2 -2
  65. synapse/tests/test_lib_scrape.py +50 -0
  66. synapse/tests/test_lib_storm.py +12 -6
  67. synapse/tests/test_lib_stormhttp.py +4 -4
  68. synapse/tests/test_lib_stormlib_auth.py +3 -2
  69. synapse/tests/test_lib_stormlib_cortex.py +10 -12
  70. synapse/tests/test_lib_stormlib_infosec.py +2 -3
  71. synapse/tests/test_lib_stormlib_json.py +18 -21
  72. synapse/tests/test_lib_stormlib_log.py +1 -1
  73. synapse/tests/test_lib_stormlib_oauth.py +603 -1
  74. synapse/tests/test_lib_stormlib_stats.py +13 -3
  75. synapse/tests/test_lib_stormlib_stix.py +5 -5
  76. synapse/tests/test_lib_stormtypes.py +4 -4
  77. synapse/tests/test_lib_structlog.py +5 -6
  78. synapse/tests/test_lib_view.py +8 -0
  79. synapse/tests/test_model_base.py +32 -0
  80. synapse/tests/test_model_infotech.py +2 -2
  81. synapse/tests/test_telepath.py +56 -35
  82. synapse/tests/test_tools_cryo_cat.py +4 -3
  83. synapse/tests/test_tools_docker_validate.py +4 -2
  84. synapse/tests/test_tools_feed.py +30 -2
  85. synapse/tests/test_tools_genpkg.py +1 -1
  86. synapse/tests/test_tools_healthcheck.py +8 -7
  87. synapse/tests/test_utils.py +2 -2
  88. synapse/tests/test_utils_getrefs.py +35 -28
  89. synapse/tests/utils.py +3 -3
  90. synapse/tools/autodoc.py +3 -3
  91. synapse/tools/changelog.py +2 -2
  92. synapse/tools/cryo/cat.py +3 -3
  93. synapse/tools/csvtool.py +2 -3
  94. synapse/tools/docker/validate.py +5 -5
  95. synapse/tools/feed.py +2 -1
  96. synapse/tools/genpkg.py +3 -2
  97. synapse/tools/healthcheck.py +2 -3
  98. synapse/tools/json2mpk.py +2 -2
  99. synapse/utils/getrefs.py +10 -8
  100. synapse/vendor/cpython/lib/json.py +35 -0
  101. synapse/vendor/cpython/lib/test/test_json.py +22 -0
  102. {synapse-2.201.0.dist-info → synapse-2.203.0.dist-info}/METADATA +2 -1
  103. {synapse-2.201.0.dist-info → synapse-2.203.0.dist-info}/RECORD +106 -101
  104. {synapse-2.201.0.dist-info → synapse-2.203.0.dist-info}/WHEEL +1 -1
  105. {synapse-2.201.0.dist-info → synapse-2.203.0.dist-info}/LICENSE +0 -0
  106. {synapse-2.201.0.dist-info → synapse-2.203.0.dist-info}/top_level.txt +0 -0
@@ -145,15 +145,118 @@ class OAuthV2Lib(s_stormtypes.Lib):
145
145
  })
146
146
 
147
147
  // Optionally enable PKCE
148
- $conf.extensions = ({"pkce": $lib.true})
148
+ $conf.extensions = ({"pkce": true})
149
149
 
150
150
  // Optionally disable SSL verification
151
- $conf.ssl_verify = $lib.false
151
+ $conf.ssl_verify = (false)
152
152
 
153
153
  // Optionally provide additional key-val parameters
154
154
  // to include when calling the auth URI
155
155
  $conf.extra_auth_params = ({"customparam": "foo"})
156
156
 
157
+ $lib.inet.http.oauth.v2.addProvider($conf)
158
+
159
+ Add a new provider which uses the Microsoft Azure Federated Workflow Identify token credentials.
160
+ This resolves the client_assertion from the AZURE_FEDERATED_TOKEN_FILE environment variable::
161
+
162
+ $iden = $lib.guid(azureexample, provider, oauth)
163
+ $authority_id = '4b70ee6f-d47b-3262-baa1-41cd7faed71b'
164
+ $conf = ({
165
+ "iden": $iden,
166
+ "name": "example_provider",
167
+ "auth_scheme": "client_assertion",
168
+ "client_id": "yourclientid",
169
+ "client_assertion": {
170
+ "msft:azure:workloadidentity": {
171
+ "token": true,
172
+ },
173
+ },
174
+ "scope": "first_scope second_scope",
175
+ "auth_uri": `https://login.microsoftonline.com/{$authority_id}/oauth2/v2.0/authorize`,
176
+ "token_uri": `https://login.microsoftonline.com/{$authority_id}/oauth2/v2.0/token`,
177
+ "redirect_uri": "https://local.redirect.com/oauth",
178
+ })
179
+
180
+ // Optionally enable PKCE
181
+ $conf.extensions = ({"pkce": true})
182
+
183
+ // Optionally disable SSL verification
184
+ $conf.ssl_verify = (false)
185
+
186
+ // Optionally provide additional key-val parameters
187
+ // to include when calling the auth URI
188
+ $conf.extra_auth_params = ({"customparam": "foo"})
189
+
190
+ $lib.inet.http.oauth.v2.addProvider($conf)
191
+
192
+ If the ``client_id`` value should come from the AZURE_CLIENT_ID environment variable, use the
193
+ following configuration::
194
+
195
+ $conf = ({
196
+ "iden": $iden,
197
+ "name": "example_provider",
198
+ "auth_scheme": "client_assertion",
199
+ "client_assertion": {
200
+ "msft:azure:workloadidentity": {
201
+ "token": true,
202
+ "client_id": true,
203
+ },
204
+ },
205
+ "scope": "first_scope second_scope",
206
+ "auth_uri": `https://login.microsoftonline.com/{$authority_id}/oauth2/v2.0/authorize`,
207
+ "token_uri": `https://login.microsoftonline.com/{$authority_id}/oauth2/v2.0/token`,
208
+ "redirect_uri": "https://local.redirect.com/oauth",
209
+ })
210
+
211
+ Add a new provider which uses a custom Storm callback to obtain the client_assertion data. These
212
+ callbacks are executed as the user who is performing the authorization_code workflow. The Storm
213
+ callback must return data in a tuple of ``bool`` and a dictionary containing the assertion in the
214
+ key ``token``. Error messages should be in the key ``error``::
215
+
216
+ $iden = $lib.guid(callstormexample, provider, oauth)
217
+
218
+ // Example callback
219
+ $callbackQuery = ${
220
+ $url = `{$baseurl}/api/oauth/getAssertion`
221
+ $resp = $lib.inet.http.get($url, ssl_verify=$ssl_verify)
222
+ if ($resp.code = 200) {
223
+ $resp = ([true, {'token': $resp.json().assertion}])
224
+ } else {
225
+ $resp = ([false, {"error": `Failed to get assertion from {$url}`}])
226
+ }
227
+ return ( $resp )
228
+ }
229
+
230
+ // Specify any variables that need to be provided to $callbackQuery
231
+ $myCallbackVars = ({
232
+ 'baseurl': 'https://local.assertion.provider.corp',
233
+ 'ssl_verify': true,
234
+ })
235
+
236
+ // Specify the view the callback is run in.
237
+ $view = $lib.view.get().iden
238
+
239
+ $conf = ({
240
+ "iden": $iden,
241
+ "name": "example_provider",
242
+ "auth_scheme": "client_assertion",
243
+ "client_id": "yourclientid",
244
+ "client_assertion": {
245
+ "cortex:callstorm": {
246
+ "query": $callbackQuery,
247
+ "view": $view,
248
+ "vars": $myCallbackVars,
249
+ },
250
+ },
251
+ "scope": "first_scope second_scope",
252
+ "auth_uri": "https://provider.com/auth",
253
+ "token_uri": "https://provider.com/token",
254
+ "redirect_uri": "https://local.redirect.com/oauth",
255
+ })
256
+
257
+ // Optionally enable PKCE
258
+ $conf.extensions = ({"pkce": true})
259
+
157
260
  $lib.inet.http.oauth.v2.addProvider($conf)
158
261
  ''',
159
262
  'type': {
@@ -220,11 +220,13 @@ class StatTally(s_stormtypes.Prim):
220
220
 
221
221
  @s_stormtypes.stormfunc(readonly=True)
222
222
  async def inc(self, name, valu=1):
223
+ name = await s_stormtypes.tostr(name)
223
224
  valu = await s_stormtypes.toint(valu)
224
225
  self.counters[name] += valu
225
226
 
226
227
  @s_stormtypes.stormfunc(readonly=True)
227
228
  async def get(self, name):
229
+ name = await s_stormtypes.tostr(name)
228
230
  return self.counters.get(name, 0)
229
231
 
230
232
  def value(self):
@@ -236,6 +238,8 @@ class StatTally(s_stormtypes.Prim):
236
238
 
237
239
  @s_stormtypes.stormfunc(readonly=True)
238
240
  async def sorted(self, byname=False, reverse=False):
241
+ byname = await s_stormtypes.tobool(byname)
242
+ reverse = await s_stormtypes.tobool(reverse)
239
243
  if byname:
240
244
  return list(sorted(self.counters.items(), reverse=reverse))
241
245
  else:
@@ -1,4 +1,3 @@
1
- import json
2
1
  import uuid
3
2
  import asyncio
4
3
  import logging
@@ -589,7 +588,7 @@ def validateStix(bundle, version='2.1'):
589
588
  'mesg': '',
590
589
  'result': {},
591
590
  }
592
- bundle = json.loads(json.dumps(bundle))
591
+ bundle = s_msgpack.deepcopy(bundle, use_list=True)
593
592
  opts = stix2validator.ValidationOptions(strict=True, version=version)
594
593
  try:
595
594
  results = stix2validator.validate_parsed_json(bundle, options=opts)
@@ -1172,7 +1171,7 @@ class LibStixExport(s_stormtypes.Lib):
1172
1171
  @s_stormtypes.stormfunc(readonly=True)
1173
1172
  async def config(self):
1174
1173
  # make a new mutable config
1175
- return json.loads(json.dumps(_DefaultConfig))
1174
+ return s_msgpack.deepcopy(_DefaultConfig, use_list=True)
1176
1175
 
1177
1176
  @s_stormtypes.stormfunc(readonly=True)
1178
1177
  async def bundle(self, config=None):
@@ -1372,7 +1371,7 @@ class StixBundle(s_stormtypes.Prim):
1372
1371
  return stixid
1373
1372
 
1374
1373
  def _initStixItem(self, stixid, stixtype, node):
1375
- ndef = json.loads(json.dumps(node.ndef))
1374
+ ndef = s_msgpack.deepcopy(node.ndef, use_list=True)
1376
1375
  retn = {
1377
1376
  'id': stixid,
1378
1377
  'type': stixtype,
@@ -321,7 +321,7 @@ class LibVault(s_stormtypes.Lib):
321
321
  {'name': 'vtype', 'type': 'str',
322
322
  'desc': 'The type of this vault.'},
323
323
  {'name': 'scope', 'type': 'str',
324
- 'desc': 'Scope for this vault. One of "user", "role", "global", or $lib.null for unscoped vaults.'},
324
+ 'desc': 'Scope for this vault. One of "user", "role", "global", or ``(null)`` for unscoped vaults.'},
325
325
  {'name': 'owner', 'type': 'str',
326
326
  'desc': 'User/role iden for this vault if scope is "user" or "role". None for "global" scope vaults.'},
327
327
  {'name': 'secrets', 'type': 'dict',
@@ -359,9 +359,9 @@ class LibVault(s_stormtypes.Lib):
359
359
  'args': (
360
360
  {'name': 'vtype', 'type': 'str', 'desc': 'The vault type to retrieved.'},
361
361
  {'name': 'scope', 'type': 'str', 'default': None,
362
- 'desc': 'The scope for the specified type. If $lib.null, then getByType will search.'},
362
+ 'desc': 'The scope for the specified type. If ``(null)``, then getByType will search.'},
363
363
  ),
364
- 'returns': {'type': 'vault', 'desc': 'Vault or $lib.null if the vault could not be retrieved.'}}},
364
+ 'returns': {'type': 'vault', 'desc': 'Vault or ``(null)`` if the vault could not be retrieved.'}}},
365
365
  {'name': 'list', 'desc': 'List vaults accessible to the current user.',
366
366
  'type': {'type': 'function', '_funcname': '_listVaults',
367
367
  'args': (),
@@ -649,14 +649,14 @@ class Vault(s_stormtypes.Prim):
649
649
  'type': {'type': 'function', '_funcname': '_methSetPerm',
650
650
  'args': (
651
651
  {'name': 'iden', 'type': 'str', 'desc': 'The user or role to modify.'},
652
- {'name': 'level', 'type': 'str', 'desc': 'The easyperm level for the iden. $lib.null to remove an existing permission.'},
652
+ {'name': 'level', 'type': 'str', 'desc': 'The easyperm level for the iden. ``(null)`` to remove an existing permission.'},
653
653
  ),
654
- 'returns': {'type': 'boolean', 'desc': '$lib.true if the permission was set, $lib.false otherwise.', }}},
654
+ 'returns': {'type': 'boolean', 'desc': '``(true)`` if the permission was set, ``(false)`` otherwise.', }}},
655
655
 
656
656
  {'name': 'delete', 'desc': 'Delete the Vault.',
657
657
  'type': {'type': 'function', '_funcname': '_methDelete',
658
658
  'args': (),
659
- 'returns': {'type': 'boolean', 'desc': '$lib.true if the vault was deleted, $lib.false otherwise.', }}},
659
+ 'returns': {'type': 'boolean', 'desc': '``(true)`` if the vault was deleted, ``(false)`` otherwise.', }}},
660
660
  )
661
661
  _storm_typename = 'vault'
662
662
  _ismutable = False
@@ -21,7 +21,7 @@ class XmlElement(s_stormtypes.Prim):
21
21
  'args': (
22
22
  {'name': 'name', 'type': 'str', 'desc': 'The name of the XML tag.'},
23
23
  {'name': 'nested', 'type': 'bool', 'default': True,
24
- 'desc': 'Set to $lib.false to only find direct children.'},
24
+ 'desc': 'Set to ``(false)`` to only find direct children.'},
25
25
  ),
26
26
  'returns': {'type': 'generator', 'desc': 'A generator which yields xml:elements.'}}},
27
27
 
@@ -30,7 +30,7 @@ class XmlElement(s_stormtypes.Prim):
30
30
  'args': (
31
31
  {'name': 'name', 'type': 'str', 'desc': 'The name of the child XML element tag.'},
32
32
  ),
33
- 'returns': {'type': 'xml:element', 'desc': 'The child XML element or $lib.null'}}},
33
+ 'returns': {'type': 'xml:element', 'desc': 'The child XML element or ``(null)``.'}}},
34
34
  )
35
35
 
36
36
  def __init__(self, runt, elem):
synapse/lib/stormtypes.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import bz2
2
2
  import copy
3
3
  import gzip
4
- import json
5
4
  import time
6
5
 
7
6
  import regex
@@ -25,6 +24,7 @@ import synapse.common as s_common
25
24
  import synapse.telepath as s_telepath
26
25
 
27
26
  import synapse.lib.coro as s_coro
27
+ import synapse.lib.json as s_json
28
28
  import synapse.lib.node as s_node
29
29
  import synapse.lib.time as s_time
30
30
  import synapse.lib.cache as s_cache
@@ -837,14 +837,14 @@ class LibDmon(Lib):
837
837
  {'name': 'iden', 'type': 'str', 'desc': 'The GUID of the Dmon to stop.'},
838
838
  ),
839
839
  'returns': {'type': 'boolean',
840
- 'desc': '$lib.true unless the dmon does not exist or was already stopped.'}}},
840
+ 'desc': '``(true)`` unless the dmon does not exist or was already stopped.'}}},
841
841
  {'name': 'start', 'desc': 'Start a storm dmon.',
842
842
  'type': {'type': 'function', '_funcname': '_libDmonStart',
843
843
  'args': (
844
844
  {'name': 'iden', 'type': 'str', 'desc': 'The GUID of the dmon to start.'},
845
845
  ),
846
846
  'returns': {'type': 'boolean',
847
- 'desc': '$lib.true unless the dmon does not exist or was already started.'}}},
847
+ 'desc': '``(true)`` unless the dmon does not exist or was already started.'}}},
848
848
  )
849
849
  _storm_lib_path = ('dmon',)
850
850
 
@@ -1469,7 +1469,7 @@ class LibBase(Lib):
1469
1469
 
1470
1470
  Update the current runtime to enable debugging::
1471
1471
 
1472
- $lib.debug = $lib.true''',
1472
+ $lib.debug = (true)''',
1473
1473
  'type': {
1474
1474
  'type': ['gtor', 'stor'],
1475
1475
  '_storfunc': '_setRuntDebug',
@@ -1782,7 +1782,7 @@ class LibBase(Lib):
1782
1782
  name = await tostr(name)
1783
1783
  mesg = await tostr(mesg)
1784
1784
  info = await toprim(info)
1785
- s_common.reqjsonsafe(info)
1785
+ s_json.reqjsonsafe(info)
1786
1786
 
1787
1787
  ctor = getattr(s_exc, name, None)
1788
1788
  if ctor is not None:
@@ -1841,7 +1841,7 @@ class LibBase(Lib):
1841
1841
  @stormfunc(readonly=True)
1842
1842
  async def _fire(self, name, **info):
1843
1843
  info = await toprim(info)
1844
- s_common.reqjsonsafe(info)
1844
+ s_json.reqjsonsafe(info)
1845
1845
  await self.runt.snap.fire('storm:fire', type=name, data=info)
1846
1846
 
1847
1847
  @registry.registerLib
@@ -2103,9 +2103,9 @@ class LibAxon(Lib):
2103
2103
 
2104
2104
  For APIs that accept a proxy argument, the following values are supported::
2105
2105
 
2106
- $lib.null: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
2107
- $lib.true: Use the proxy defined by the http:proxy configuration option if set.
2108
- $lib.false: Do not use the proxy defined by the http:proxy configuration option if set.
2106
+ ``(null)``: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
2107
+ ``(true)``: Use the proxy defined by the http:proxy configuration option if set.
2108
+ ``(false)``: Do not use the proxy defined by the http:proxy configuration option if set.
2109
2109
  <str>: A proxy URL string.
2110
2110
  '''
2111
2111
  _storm_locals = (
@@ -3025,7 +3025,7 @@ class LibTime(Lib):
3025
3025
  {'name': 'valu', 'type': 'str', 'desc': 'The timestamp string to parse.', },
3026
3026
  {'name': 'format', 'type': 'str', 'desc': 'The format string to use for parsing.', },
3027
3027
  {'name': 'errok', 'type': 'boolean', 'default': False,
3028
- 'desc': 'If set, parsing errors will return ``$lib.null`` instead of raising an exception.'}
3028
+ 'desc': 'If set, parsing errors will return ``(null)`` instead of raising an exception.'}
3029
3029
  ),
3030
3030
  'returns': {'type': 'int', 'desc': 'The epoch timestamp for the string.', }}},
3031
3031
  {'name': 'format', 'desc': '''
@@ -3365,7 +3365,7 @@ class LibRegx(Lib):
3365
3365
  In order to get the matching groups, patterns must use parentheses
3366
3366
  to indicate the start and stop of the regex to return portions of.
3367
3367
  If groups are not used, a successful match will return a empty list
3368
- and a unsuccessful match will return ``$lib.null``.
3368
+ and a unsuccessful match will return ``(null)``.
3369
3369
 
3370
3370
  Example:
3371
3371
  Extract the matching groups from a piece of text::
@@ -3407,7 +3407,6 @@ class LibRegx(Lib):
3407
3407
  'returns': {'type': 'list', 'desc': 'A list of lists of strings for the matching groups in the pattern.', }}},
3408
3408
  {'name': 'matches', 'desc': '''
3409
3409
  Check if text matches a pattern.
3410
- Returns $lib.true if the text matches the pattern, otherwise $lib.false.
3411
3410
 
3412
3411
  Notes:
3413
3412
  This API requires the pattern to match at the start of the string.
@@ -4797,11 +4796,7 @@ class Str(Prim):
4797
4796
 
4798
4797
  @stormfunc(readonly=True)
4799
4798
  async def _methStrJson(self):
4800
- try:
4801
- return json.loads(self.valu, strict=True)
4802
- except Exception as e:
4803
- mesg = f'Text is not valid JSON: {self.valu}'
4804
- raise s_exc.BadJsonText(mesg=mesg)
4799
+ return s_json.loads(self.valu)
4805
4800
 
4806
4801
  @registry.registerType
4807
4802
  class Bytes(Prim):
@@ -4990,19 +4985,15 @@ class Bytes(Prim):
4990
4985
  errors = await tostr(errors)
4991
4986
 
4992
4987
  if encoding is None:
4993
- encoding = json.detect_encoding(valu)
4988
+ encoding = s_json.detect_encoding(valu)
4994
4989
  else:
4995
4990
  encoding = await tostr(encoding)
4996
4991
 
4997
- return json.loads(valu.decode(encoding, errors))
4992
+ return s_json.loads(valu.decode(encoding, errors))
4998
4993
 
4999
4994
  except UnicodeDecodeError as e:
5000
4995
  raise s_exc.StormRuntimeError(mesg=f'{e}: {s_common.trimText(repr(valu))}') from None
5001
4996
 
5002
- except json.JSONDecodeError as e:
5003
- mesg = f'Unable to decode bytes as json: {e.args[0]}'
5004
- raise s_exc.BadJsonText(mesg=mesg)
5005
-
5006
4997
  @registry.registerType
5007
4998
  class Dict(Prim):
5008
4999
  '''
@@ -6262,7 +6253,7 @@ class NodeData(Prim):
6262
6253
  gateiden = self.valu.snap.wlyr.iden
6263
6254
  confirm(('node', 'data', 'set', name), gateiden=gateiden)
6264
6255
  valu = await toprim(valu)
6265
- s_common.reqjsonsafe(valu)
6256
+ s_json.reqjsonsafe(valu)
6266
6257
  return await self.valu.setData(name, valu)
6267
6258
 
6268
6259
  async def _popNodeData(self, name):
@@ -7790,7 +7781,7 @@ class View(Prim):
7790
7781
  _storm_locals = (
7791
7782
  {'name': 'iden', 'desc': 'The iden of the View.', 'type': 'str', },
7792
7783
  {'name': 'layers', 'desc': 'The ``layer`` objects associated with the ``view``.', 'type': 'list', },
7793
- {'name': 'parent', 'desc': 'The parent View. Will be ``$lib.null`` if the view is not a fork.', 'type': 'str'},
7784
+ {'name': 'parent', 'desc': 'The parent View. Will be ``(null)`` if the view is not a fork.', 'type': 'str'},
7794
7785
  {'name': 'triggers', 'desc': 'The ``trigger`` objects associated with the ``view``.',
7795
7786
  'type': 'list', },
7796
7787
  {'name': 'children', 'desc': 'Yield Views which are children of this View.',
@@ -7815,7 +7806,7 @@ class View(Prim):
7815
7806
  the protected option (below) until this option is removed.
7816
7807
 
7817
7808
  protected (bool)
7818
- Setting to $lib.true will prevent the layer from being merged or deleted.
7809
+ Setting to ``(true)`` will prevent the layer from being merged or deleted.
7819
7810
 
7820
7811
  layers (list(str))
7821
7812
  Set the list of layer idens for a non-forked view. Layers are specified
@@ -8752,7 +8743,7 @@ class LibTrigger(Lib):
8752
8743
  pass
8753
8744
 
8754
8745
  if trigger is None:
8755
- raise s_exc.NoSuchIden('Trigger not found')
8746
+ raise s_exc.NoSuchIden(mesg='Trigger not found', iden=iden)
8756
8747
 
8757
8748
  self.runt.confirm(('trigger', 'get'), gateiden=iden)
8758
8749
 
@@ -8906,7 +8897,7 @@ class LibJsonStor(Lib):
8906
8897
  {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path parts.'},
8907
8898
  {'name': 'prop', 'type': 'str|list', 'desc': 'A property name or list of name parts.', 'default': None},
8908
8899
  ),
8909
- 'returns': {'type': 'prim', 'desc': 'The previously stored value or $lib.null'}}},
8900
+ 'returns': {'type': 'prim', 'desc': 'The previously stored value or ``(null)``.'}}},
8910
8901
 
8911
8902
  {'name': 'set', 'desc': 'Set a JSON object or object property.',
8912
8903
  'type': {'type': 'function', '_funcname': 'set',
synapse/lib/structlog.py CHANGED
@@ -1,9 +1,9 @@
1
- import json
2
-
3
1
  import logging
4
2
 
5
3
  import synapse.common as s_common
6
4
 
5
+ import synapse.lib.json as s_json
6
+
7
7
  class JsonFormatter(logging.Formatter):
8
8
  def __init__(self, *args, **kwargs):
9
9
  super().__init__(*args, **kwargs)
@@ -37,4 +37,4 @@ class JsonFormatter(logging.Formatter):
37
37
  if extras:
38
38
  ret.update({k: v for k, v in extras.items() if k not in ret})
39
39
 
40
- return json.dumps(ret, default=str)
40
+ return s_json.dumps(ret, default=str).decode()
synapse/lib/types.py CHANGED
@@ -11,6 +11,7 @@ import synapse.exc as s_exc
11
11
  import synapse.common as s_common
12
12
 
13
13
  import synapse.lib.chop as s_chop
14
+ import synapse.lib.json as s_json
14
15
  import synapse.lib.node as s_node
15
16
  import synapse.lib.time as s_time
16
17
  import synapse.lib.cache as s_cache
@@ -1601,7 +1602,7 @@ class Data(Type):
1601
1602
 
1602
1603
  def norm(self, valu):
1603
1604
  try:
1604
- s_common.reqjsonsafe(valu)
1605
+ s_json.reqjsonsafe(valu)
1605
1606
  if self.validator is not None:
1606
1607
  self.validator(valu)
1607
1608
  except (s_exc.MustBeJsonSafe, s_exc.SchemaViolation) as e:
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, 201, 0)
226
+ version = (2, 203, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = 'eed8c13ff316a77ae835539215c862d052d31994'
228
+ commit = '809ecdca2e2b05f1cd111bb23c237127b2b9c177'
synapse/lib/view.py CHANGED
@@ -1360,17 +1360,21 @@ class View(s_nexus.Pusher): # type: ignore
1360
1360
  'readonly': False
1361
1361
  }
1362
1362
 
1363
+ if name is None:
1364
+ if (pname := self.parent.info.get('name')) is not None:
1365
+ name = f'inserted fork of {pname}'
1366
+ else:
1367
+ name = f'inserted fork of {self.parent.iden}'
1368
+
1363
1369
  vdef = {
1364
1370
  'iden': s_common.guid(),
1371
+ 'name': name,
1365
1372
  'created': ctime,
1366
1373
  'creator': useriden,
1367
1374
  'parent': self.parent.iden,
1368
1375
  'layers': [layriden] + [lyr.iden for lyr in self.parent.layers]
1369
1376
  }
1370
1377
 
1371
- if name is not None:
1372
- vdef['name'] = name
1373
-
1374
1378
  s_layer.reqValidLdef(ldef)
1375
1379
  s_schemas.reqValidView(vdef)
1376
1380
 
synapse/models/base.py CHANGED
@@ -29,10 +29,18 @@ class BaseModule(s_module.CoreModule):
29
29
 
30
30
  'types': (
31
31
 
32
+ ('meta:feed', ('guid', {}), {
33
+ 'doc': 'A data feed provided by a specific source.'}),
34
+
35
+ ('meta:feed:type:taxonomy', ('taxonomy', {}), {
36
+ 'interfaces': ('meta:taxonomy',),
37
+ 'doc': 'A data feed type taxonomy.'}),
38
+
32
39
  ('meta:source', ('guid', {}), {
33
40
  'doc': 'A data source unique identifier.'}),
34
41
 
35
42
  ('meta:seen', ('comp', {'fields': (('source', 'meta:source'), ('node', 'ndef'))}), {
43
+ 'deprecated': True,
36
44
  'doc': 'Annotates that the data in a node was obtained from or observed by a given source.'}),
37
45
 
38
46
  ('meta:note', ('guid', {}), {
@@ -167,15 +175,22 @@ class BaseModule(s_module.CoreModule):
167
175
  'edges': (
168
176
  ((None, 'refs', None), {
169
177
  'doc': 'The source node contains a reference to the target node.'}),
178
+
170
179
  (('meta:source', 'seen', None), {
171
180
  'doc': 'The meta:source observed the target node.'}),
181
+
182
+ (('meta:feed', 'found', None), {
183
+ 'doc': 'The meta:feed produced the target node.'}),
184
+
172
185
  (('meta:note', 'about', None), {
173
- 'doc': 'The meta:note is about the target node.'
174
- }),
186
+ 'doc': 'The meta:note is about the target node.'}),
187
+
175
188
  (('meta:ruleset', 'has', 'meta:rule'), {
176
189
  'doc': 'The meta:ruleset includes the meta:rule.'}),
190
+
177
191
  (('meta:rule', 'matches', None), {
178
192
  'doc': 'The meta:rule has matched on target node.'}),
193
+
179
194
  (('meta:rule', 'detects', None), {
180
195
  'doc': 'The meta:rule is designed to detect instances of the target node.'}),
181
196
  ),
@@ -213,6 +228,40 @@ class BaseModule(s_module.CoreModule):
213
228
 
214
229
  )),
215
230
 
231
+ ('meta:feed:type:taxonomy', {}, ()),
232
+ ('meta:feed', {}, (
233
+
234
+ ('name', ('str', {'lower': True, 'onespace': True}), {
235
+ 'doc': 'A name for the feed.'}),
236
+
237
+ ('type', ('meta:feed:type:taxonomy', {}), {
238
+ 'doc': 'The type of data feed.'}),
239
+
240
+ ('source', ('meta:source', {}), {
241
+ 'doc': 'The meta:source which provides the feed.'}),
242
+
243
+ ('url', ('inet:url', {}), {
244
+ 'doc': 'The URL of the feed API endpoint.'}),
245
+
246
+ ('query', ('str', {}), {
247
+ 'doc': 'The query logic associated with generating the feed output.'}),
248
+
249
+ ('opts', ('data', {}), {
250
+ 'doc': 'An opaque JSON object containing feed parameters and options.'}),
251
+
252
+ ('period', ('ival', {}), {
253
+ 'doc': 'The time window over which results have been ingested.'}),
254
+
255
+ ('latest', ('time', {}), {
256
+ 'doc': 'The time of the last record consumed from the feed.'}),
257
+
258
+ ('offset', ('int', {}), {
259
+ 'doc': 'The offset of the last record consumed from the feed.'}),
260
+
261
+ ('cursor', ('str', {'strip': True}), {
262
+ 'doc': 'A cursor used to track ingest offset within the feed.'}),
263
+ )),
264
+
216
265
  ('meta:note:type:taxonomy', {}, ()),
217
266
  ('meta:note', {}, (
218
267