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
synapse/lib/schemas.py CHANGED
@@ -383,6 +383,7 @@ _changelogTypes = {'migration': 'Automatic Migrations',
383
383
  'model': 'Model Changes',
384
384
  'feat': 'Features and Enhancements',
385
385
  'bug': 'Bugfixes',
386
+ 'note': 'Notes',
386
387
  'doc': 'Improved documentation',
387
388
  'deprecation': 'Deprecations'}
388
389
 
@@ -407,7 +408,7 @@ _changelogSchema = {
407
408
  'additionalProperties': False,
408
409
  'required': ['type', 'desc']
409
410
  }
410
- _reqChanglogSchema = s_config.getJsValidator(_changelogSchema)
411
+ _reqChangelogSchema = s_config.getJsValidator(_changelogSchema)
411
412
 
412
413
  tabularConfSchema = {
413
414
  'type': 'object',
@@ -1042,3 +1043,88 @@ _reqValidDdefSchema = {
1042
1043
  }
1043
1044
  }
1044
1045
  reqValidDdef = s_config.getJsValidator(_reqValidDdefSchema)
1046
+
1047
+ _client_assertion_schema = {
1048
+ 'type': 'object',
1049
+ 'oneOf': [
1050
+ {
1051
+ 'required': ['cortex:callstorm'],
1052
+ 'properties': {
1053
+ 'cortex:callstorm': {
1054
+ 'type': 'object',
1055
+ 'properties': {
1056
+ 'query': {'type': 'string'},
1057
+ 'vars': {'type': 'object'},
1058
+ 'view': {'type': 'string', 'pattern': s_config.re_iden},
1059
+ },
1060
+ 'required': ['query', 'view'],
1061
+ 'additionalProperties': False,
1062
+ },
1063
+ },
1064
+ 'additionalProperties': False,
1065
+ 'not': {
1066
+ 'required': ['msft:azure:workloadidentity'],
1067
+ }
1068
+ },
1069
+ {
1070
+ 'required': ['msft:azure:workloadidentity'],
1071
+ 'properties': {
1072
+ 'msft:azure:workloadidentity': {
1073
+ 'type': 'object',
1074
+ 'properties': {
1075
+ 'token': {'type': 'boolean'},
1076
+ 'client_id': {'type': 'boolean'},
1077
+ },
1078
+ 'required': ['token'],
1079
+ 'additionalProperties': False,
1080
+ }
1081
+ },
1082
+ 'additionalProperties': False,
1083
+ 'not': {
1084
+ 'required': ['cortex:callstorm'],
1085
+ }
1086
+ }
1087
+ ]
1088
+ }
1089
+ _reqValidOauth2ProviderSchema = {
1090
+ 'type': 'object',
1091
+ 'properties': {
1092
+ 'iden': {'type': 'string', 'pattern': s_config.re_iden},
1093
+ 'name': {'type': 'string'},
1094
+ 'flow_type': {'type': 'string', 'default': 'authorization_code', 'enum': ['authorization_code']},
1095
+ 'auth_scheme': {'type': 'string', 'default': 'basic', 'enum': ['basic', 'client_assertion']},
1096
+ 'client_id': {'type': 'string'},
1097
+ 'client_secret': {'type': 'string'},
1098
+ 'client_assertion': _client_assertion_schema,
1099
+ 'scope': {'type': 'string'},
1100
+ 'ssl_verify': {'type': 'boolean', 'default': True},
1101
+ 'auth_uri': {'type': 'string'},
1102
+ 'token_uri': {'type': 'string'},
1103
+ 'redirect_uri': {'type': 'string'},
1104
+ 'extensions': {
1105
+ 'type': 'object',
1106
+ 'properties': {
1107
+ 'pkce': {'type': 'boolean'},
1108
+ },
1109
+ 'additionalProperties': False,
1110
+ },
1111
+ 'extra_auth_params': {
1112
+ 'type': 'object',
1113
+ 'additionalProperties': {'type': 'string'},
1114
+ },
1115
+ },
1116
+ 'additionalProperties': False,
1117
+ 'required': ['iden', 'name', 'scope', 'auth_uri', 'token_uri', 'redirect_uri'],
1118
+ }
1119
+ reqValidOauth2Provider = s_config.getJsValidator(_reqValidOauth2ProviderSchema)
1120
+
1121
+ _reqValidOauth2TokenResponseSchema = {
1122
+ 'type': 'object',
1123
+ 'properties': {
1124
+ 'access_token': {'type': 'string'},
1125
+ 'expires_in': {'type': 'number', 'exclusiveMinimum': 0},
1126
+ },
1127
+ 'additionalProperties': True,
1128
+ 'required': ['access_token', 'expires_in'],
1129
+ }
1130
+ reqValidOauth2TokenResponse = s_config.getJsValidator(_reqValidOauth2TokenResponseSchema)
synapse/lib/scrape.py CHANGED
@@ -24,6 +24,22 @@ ipaddress = s_common.ipaddress
24
24
 
25
25
  logger = logging.getLogger(__name__)
26
26
 
27
+ urilist = set(s_data.get('iana.uris'))
28
+ urilist.update([
29
+ 'aha',
30
+ 'ftps',
31
+ 'mysql',
32
+ 'postgresql',
33
+ 'slack',
34
+ 'socks4',
35
+ 'socks4a',
36
+ 'socks5',
37
+ 'socks5h',
38
+ 'tcp',
39
+ 'unk',
40
+ 'webpack'
41
+ ])
42
+
27
43
  tldlist = list(s_data.get('iana.tlds'))
28
44
  tldlist.extend([
29
45
  'bit',
@@ -53,18 +69,6 @@ inverse_prefixs = {
53
69
 
54
70
  cve_dashes = ''.join(('-',) + s_chop.unicode_dashes)
55
71
 
56
- def fqdn_prefix_check(match: regex.Match):
57
- mnfo = match.groupdict()
58
- valu = mnfo.get('valu')
59
- prefix = mnfo.get('prefix')
60
- cbfo = {}
61
- if prefix is not None:
62
- new_valu = valu.rstrip(inverse_prefixs.get(prefix))
63
- if new_valu != valu:
64
- valu = new_valu
65
- cbfo['match'] = valu
66
- return valu, cbfo
67
-
68
72
  def fqdn_check(match: regex.Match):
69
73
  mnfo = match.groupdict()
70
74
  valu = mnfo.get('valu')
@@ -261,12 +265,30 @@ def unc_path_check(match: regex.Match):
261
265
 
262
266
  return valu, {}
263
267
 
268
+ def url_scheme_check(match: regex.Match):
269
+ mnfo = match.groupdict()
270
+ valu = mnfo.get('valu')
271
+ prefix = mnfo.get('prefix')
272
+
273
+ cbfo = {}
274
+ if prefix is not None:
275
+ new_valu = valu.rstrip(inverse_prefixs.get(prefix))
276
+ if new_valu != valu:
277
+ valu = new_valu
278
+ cbfo['match'] = valu
279
+
280
+ scheme = valu.split('://')[0].lower()
281
+ if scheme not in urilist:
282
+ return None, {}
283
+
284
+ return valu, cbfo
285
+
264
286
  # these must be ordered from most specific to least specific to allow first=True to work
265
287
  scrape_types = [ # type: ignore
266
288
  ('file:path', linux_path_regex, {'callback': linux_path_check, 'flags': regex.VERBOSE}),
267
289
  ('file:path', windows_path_regex, {'callback': windows_path_check, 'flags': regex.VERBOSE}),
268
290
  ('inet:url', r'(?P<prefix>[\\{<\(\[]?)(?P<valu>[a-zA-Z][a-zA-Z0-9]*://(?(?=[,.]+[ \'\"\t\n\r\f\v])|[^ \'\"\t\n\r\f\v])+)',
269
- {'callback': fqdn_prefix_check}),
291
+ {'callback': url_scheme_check}),
270
292
  ('inet:url', r'(["\'])?(?P<valu>\\[^\n]+?)(?(1)\1|\s)', {'callback': unc_path_check}),
271
293
  ('inet:email', r'(?=(?:[^a-z0-9_.+-]|^)(?P<valu>[a-z0-9_\.\-+]{1,256}@(?:[a-z0-9_-]{1,63}\.){1,10}(?:%s))(?:[^a-z0-9_.-]|[.\s]|$))' % tldcat, {}),
272
294
  ('inet:server', fr'(?P<valu>(?:(?<!\d|\d\.|[0-9a-f:]:)((?P<addr>{ipv4_match})|\[(?P<v6addr>{ipv6_match})\]):(?P<port>\d{{1,5}})(?!\d|\.\d)))',
synapse/lib/snap.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.base as s_base
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
@@ -218,7 +219,7 @@ class ProtoNode:
218
219
  return
219
220
 
220
221
  try:
221
- s_common.reqjsonsafe(valu)
222
+ s_json.reqjsonsafe(valu)
222
223
  except s_exc.MustBeJsonSafe as e:
223
224
  if self.ctx.snap.strict:
224
225
  raise e
synapse/lib/storm.lark CHANGED
@@ -437,7 +437,8 @@ _cond: notcond | "(" _condexpr ")"
437
437
  | condsubq | arraycond
438
438
  | _varvalu | _reqdollarexprs
439
439
 
440
- notcond: "not" _cond
440
+ _NOT: "not"
441
+ notcond: _NOT _cond
441
442
 
442
443
  hasrelpropcond: relprop | univprop
443
444
  relpropcond: relpropvalue _cmpr _valu
@@ -627,15 +628,15 @@ OCTNUMBER.1: /
627
628
  /x
628
629
 
629
630
  BOOL.2: /(true|false)(?=$|[\s\),\]}\|\=])/
630
- NULL.2: "null"
631
- NOT.2: "not"
631
+ NULL.2: /null(?=$|[\s\),\]}\|\=])/
632
+ NOTOP.2: /not(?=$|[\s\),\]}\|\=])/
632
633
  OR.2: "or"
633
634
  AND.2: "and"
634
635
 
635
636
  // $ expression rules in increasing order of precedence (modeled on Python's order)
636
637
  ?expror: exprand | expror OR exprand
637
638
  ?exprand: exprnot | exprand AND exprnot
638
- ?exprnot: exprcmp | NOT exprcmp
639
+ ?exprnot: exprcmp | NOTOP exprcmp
639
640
  ?exprcmp: exprsum | exprcmp (CMPR | EQSPACE | EQNOSPACE) exprsum
640
641
  ?exprsum: exprproduct | exprsum (EXPRPLUS | EXPRMINUS) exprproduct
641
642
  ?exprproduct: exprunary | exprproduct (EXPRTIMES | EXPRDIVIDE | EXPRMODULO) exprunary
synapse/lib/storm.py CHANGED
@@ -2592,7 +2592,7 @@ class Cmd:
2592
2592
 
2593
2593
  async def execStormCmd(self, runt, genr): # pragma: no cover
2594
2594
  ''' Abstract base method '''
2595
- raise s_exc.NoSuchImpl('Subclass must implement execStormCmd')
2595
+ raise s_exc.NoSuchImpl(mesg='Subclass must implement execStormCmd')
2596
2596
  for item in genr:
2597
2597
  yield item
2598
2598
 
@@ -4768,7 +4768,7 @@ class GraphCmd(Cmd):
4768
4768
  inet:fqdn | graph
4769
4769
  --degrees 2
4770
4770
  --filter { -#nope }
4771
- --pivot { -> meta:seen }
4771
+ --pivot { <(seen)- meta:source }
4772
4772
  --form-pivot inet:fqdn {<- * | limit 20}
4773
4773
  --form-pivot inet:fqdn {-> * | limit 20}
4774
4774
  --form-filter inet:fqdn {-inet:fqdn:issuffix=1}
@@ -57,7 +57,7 @@ TerminalPygMap = {
57
57
  'MODSET': p_t.Operator,
58
58
  'MODSETMULTI': p_t.Operator,
59
59
  'NONQUOTEWORD': p_t.Literal,
60
- 'NOT': p_t.Keyword,
60
+ 'NOTOP': p_t.Operator,
61
61
  'NULL': p_t.Keyword,
62
62
  'NUMBER': p_t.Literal.Number,
63
63
  'OCTNUMBER': p_t.Literal.Number,
@@ -115,6 +115,7 @@ TerminalPygMap = {
115
115
  '_LPARNOSPACE': p_t.Punctuation,
116
116
  '_MATCHHASH': p_t.Punctuation,
117
117
  '_MATCHHASHWILD': p_t.Punctuation,
118
+ '_NOT': p_t.Keyword,
118
119
  '_RETURN': p_t.Keyword,
119
120
  '_REVERSE': p_t.Keyword,
120
121
  '_RIGHTJOIN': p_t.Punctuation,
synapse/lib/stormhttp.py CHANGED
@@ -1,4 +1,3 @@
1
- import json
2
1
  import asyncio
3
2
  import logging
4
3
  import urllib.parse
@@ -12,6 +11,7 @@ import synapse.exc as s_exc
12
11
  import synapse.common as s_common
13
12
 
14
13
  import synapse.lib.base as s_base
14
+ import synapse.lib.json as s_json
15
15
  import synapse.lib.msgpack as s_msgpack
16
16
  import synapse.lib.version as s_version
17
17
  import synapse.lib.stormtypes as s_stormtypes
@@ -56,7 +56,7 @@ class WebSocket(s_base.Base, s_stormtypes.StormType):
56
56
  try:
57
57
 
58
58
  mesg = await s_stormtypes.toprim(mesg)
59
- await self.resp.send_bytes(json.dumps(mesg).encode())
59
+ await self.resp.send_bytes(s_json.dumps(mesg))
60
60
  return (True, None)
61
61
 
62
62
  except asyncio.CancelledError: # pragma: no cover
@@ -69,10 +69,8 @@ class WebSocket(s_base.Base, s_stormtypes.StormType):
69
69
 
70
70
  try:
71
71
  _type, data, extra = await s_common.wait_for(self.resp.receive(), timeout=timeout)
72
- if _type == aiohttp.WSMsgType.BINARY:
73
- return (True, json.loads(data))
74
- if _type == aiohttp.WSMsgType.TEXT:
75
- return (True, json.loads(data.encode()))
72
+ if _type in (aiohttp.WSMsgType.BINARY, aiohttp.WSMsgType.TEXT):
73
+ return (True, s_json.loads(data))
76
74
  if _type == aiohttp.WSMsgType.CLOSED: # pragma: no cover
77
75
  return (True, None)
78
76
  return (False, ('BadMesgFormat', {'mesg': f'WebSocket RX unhandled type: {_type.name}'})) # pragma: no cover
@@ -100,9 +98,9 @@ class LibHttp(s_stormtypes.Lib):
100
98
 
101
99
  For APIs that accept a proxy argument, the following values are supported::
102
100
 
103
- $lib.null: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
104
- $lib.true: Use the proxy defined by the http:proxy configuration option if set.
105
- $lib.false: Do not use the proxy defined by the http:proxy configuration option if set.
101
+ (null): Deprecated - Use the proxy defined by the http:proxy configuration option if set.
102
+ (true): Use the proxy defined by the http:proxy configuration option if set.
103
+ (false): Do not use the proxy defined by the http:proxy configuration option if set.
106
104
  <str>: A proxy URL string.
107
105
  '''
108
106
  _storm_locals = (
@@ -563,17 +561,17 @@ class HttpResp(s_stormtypes.Prim):
563
561
  errors = await s_stormtypes.tostr(errors)
564
562
 
565
563
  if encoding is None:
566
- encoding = json.detect_encoding(valu)
564
+ encoding = s_json.detect_encoding(valu)
567
565
  else:
568
566
  encoding = await s_stormtypes.tostr(encoding)
569
567
 
570
- return json.loads(valu.decode(encoding, errors))
568
+ return s_json.loads(valu.decode(encoding, errors))
571
569
 
572
570
  except UnicodeDecodeError as e:
573
571
  raise s_exc.StormRuntimeError(mesg=f'{e}: {s_common.trimText(repr(valu))}') from None
574
572
 
575
- except json.JSONDecodeError as e:
576
- mesg = f'Unable to decode HTTP response as json: {e.args[0]}'
573
+ except s_exc.BadJsonText as e:
574
+ mesg = f'Unable to decode HTTP response as json: {e.get("mesg")}'
577
575
  raise s_exc.BadJsonText(mesg=mesg)
578
576
 
579
577
  async def _httpRespMsgpack(self):
@@ -47,7 +47,7 @@ class AhaLib(s_stormtypes.Lib):
47
47
  'desc': 'An optional dictionary of filters to use when resolving the AHA service.'}
48
48
  ),
49
49
  'returns': {'type': ('null', 'dict'),
50
- 'desc': 'The AHA service information dictionary, or $lib.null.', }}},
50
+ 'desc': 'The AHA service information dictionary, or ``(null))``.', }}},
51
51
  {'name': 'list', 'desc': 'Enumerate all of the AHA services.',
52
52
  'type': {'type': 'function', '_funcname': '_methAhaList', 'args': (),
53
53
  'returns': {'name': 'Yields', 'type': 'list',
@@ -97,14 +97,14 @@ class AhaLib(s_stormtypes.Lib):
97
97
  Examples:
98
98
  Call getNexusChanges on an AHA service::
99
99
 
100
- $todo = $lib.utils.todo('getNexusChanges', (0), wait=$lib.false)
100
+ $todo = $lib.utils.todo('getNexusChanges', (0), wait=(false))
101
101
  for $info in $lib.aha.callPeerGenr(cortex..., $todo) {
102
102
  $lib.print($info)
103
103
  }
104
104
 
105
105
  Call getNexusChanges on an AHA service, skipping the invoking service::
106
106
 
107
- $todo = $lib.utils.todo('getNexusChanges', (0), wait=$lib.false)
107
+ $todo = $lib.utils.todo('getNexusChanges', (0), wait=(false))
108
108
  for $info in $lib.aha.callPeerGenr(cortex..., $todo, skiprun=$lib.cell.getCellInfo().cell.run) {
109
109
  $lib.print($info)
110
110
  }
@@ -259,7 +259,7 @@ class AhaPoolLib(s_stormtypes.Lib):
259
259
  {'name': 'name', 'type': 'str',
260
260
  'desc': 'The name of the pool to get. It is easiest to use the relative name of a pool, ending with "...".', },
261
261
  ),
262
- 'returns': {'type': ['null', 'aha:pool'], 'desc': 'The pool if it exists, or $lib.null.'}}},
262
+ 'returns': {'type': ['null', 'aha:pool'], 'desc': 'The pool if it exists, or ``(null)``.'}}},
263
263
  {'name': 'list', 'desc': 'Enumerate all of the AHA service pools.',
264
264
  'type': {'type': 'function', '_funcname': '_methPoolList',
265
265
  'returns': {'name': 'yields', 'type': 'aha:pool'}}},
@@ -684,7 +684,7 @@ class UserJson(s_stormtypes.Prim):
684
684
  {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path parts.'},
685
685
  {'name': 'prop', 'type': 'str|list', 'desc': 'A property name or list of name parts.', 'default': None},
686
686
  ),
687
- 'returns': {'type': 'prim', 'desc': 'The previously stored value or $lib.null'}}},
687
+ 'returns': {'type': 'prim', 'desc': 'The previously stored value or ``(null)``.'}}},
688
688
 
689
689
  {'name': 'set', 'desc': 'Set a JSON object or object property for the user.',
690
690
  'type': {'type': 'function', '_funcname': 'set',
@@ -24,7 +24,7 @@ class LibCache(s_stormtypes.Lib):
24
24
  to the key argument provided to .get().
25
25
 
26
26
  The callback Storm query must contain a return statement, and if it does not return a value
27
- when executed with the input, $lib.null will be set as the value.
27
+ when executed with the input, ``(null)`` will be set as the value.
28
28
 
29
29
  The fixed cache uses FIFO to evict items once the maximum size is reached.
30
30
 
@@ -114,7 +114,7 @@ class FixedCache(s_stormtypes.StormType):
114
114
  {'name': 'key', 'type': 'any', 'desc': 'The key to pop.'},
115
115
  ),
116
116
  'returns': {'type': 'any',
117
- 'desc': 'The value from the cache, or $lib.null if it does not exist', }}},
117
+ 'desc': 'The value from the cache, or ``(null)`` if it does not exist', }}},
118
118
  {'name': 'put', 'desc': 'Put an item into the cache.',
119
119
  'type': {'type': 'function', '_funcname': '_methPut',
120
120
  'args': (
@@ -1,10 +1,10 @@
1
1
  import copy
2
- import json
3
2
  import logging
4
3
 
5
4
  import synapse.exc as s_exc
6
5
  import synapse.telepath as s_telepath
7
6
 
7
+ import synapse.lib.json as s_json
8
8
  import synapse.lib.storm as s_storm
9
9
  import synapse.lib.stormtypes as s_stormtypes
10
10
  import synapse.lib.stormlib.auth as slib_auth
@@ -990,8 +990,8 @@ class HttpReq(s_stormtypes.StormType):
990
990
  @s_stormtypes.stormfunc(readonly=True)
991
991
  def _ctorJson(self, path=None):
992
992
  try:
993
- return json.loads(self.rnfo.get('body'))
994
- except (UnicodeDecodeError, json.JSONDecodeError) as e:
993
+ return s_json.loads(self.rnfo.get('body'))
994
+ except (UnicodeDecodeError, s_exc.BadJsonText) as e:
995
995
  raise s_exc.StormRuntimeError(mesg=f'Failed to decode request body as JSON: {e}') from None
996
996
 
997
997
  @s_stormtypes.stormfunc(readonly=True)
@@ -1028,7 +1028,7 @@ class HttpReq(s_stormtypes.StormType):
1028
1028
  if body is not s_stormtypes.undef:
1029
1029
  if not isinstance(body, bytes):
1030
1030
  body = await s_stormtypes.toprim(body)
1031
- body = json.dumps(body).encode('utf-8', 'surrogatepass')
1031
+ body = s_json.dumps(body)
1032
1032
  headers['Content-Type'] = 'application/json; charset=utf8"'
1033
1033
  headers['Content-Length'] = len(body)
1034
1034
 
@@ -1126,7 +1126,7 @@ class CortexHttpApi(s_stormtypes.Lib):
1126
1126
  {'name': 'path', 'type': 'string',
1127
1127
  'desc': 'Path to use to retrieve an object.'},
1128
1128
  ),
1129
- 'returns': {'type': ['http:api', 'null'], 'desc': 'The ``http:api`` object or ``$lib.null`` if there is no match.'}}},
1129
+ 'returns': {'type': ['http:api', 'null'], 'desc': 'The ``http:api`` object or ``(null)`` if there is no match.'}}},
1130
1130
  {'name': 'list', 'desc': 'Get all the Extended HTTP APIs on the Cortex',
1131
1131
  'type': {'type': 'function', '_funcname': 'listHttpApis', 'args': (),
1132
1132
  'returns': {'type': 'list', 'desc': 'A list of ``http:api`` objects'}}},
@@ -36,7 +36,7 @@ class GraphLib(s_stormtypes.Lib):
36
36
  "name": "Test Projection",
37
37
  "desc": "My test projection",
38
38
  "degrees": 2,
39
- "pivots": ["-> meta:seen"],
39
+ "pivots": [" <(seen)- meta:source "],
40
40
  "filters": ["-#nope"],
41
41
  "forms": {
42
42
  "inet:fqdn": {
@@ -193,7 +193,7 @@ class ImapServer(s_stormtypes.StormType):
193
193
  {'type': 'str', 'name': '*args',
194
194
  'desc': 'A set of search criteria to use.'},
195
195
  {'type': ['str', 'null'], 'name': 'charset', 'default': 'utf-8',
196
- 'desc': 'The CHARSET used for the search. May be set to $lib.null to disable CHARSET.'},
196
+ 'desc': 'The CHARSET used for the search. May be set to ``(null)`` to disable CHARSET.'},
197
197
  ),
198
198
  'returns': {
199
199
  'type': 'list',
@@ -1,11 +1,11 @@
1
1
  import copy
2
- import json
3
2
  import asyncio
4
3
  import logging
5
4
 
6
5
  import synapse.exc as s_exc
7
6
 
8
7
  import synapse.lib.coro as s_coro
8
+ import synapse.lib.json as s_json
9
9
  import synapse.lib.config as s_config
10
10
  import synapse.lib.stormtypes as s_stormtypes
11
11
 
@@ -93,7 +93,7 @@ class JsonLib(s_stormtypes.Lib):
93
93
  'type': {'type': 'function', '_funcname': '_jsonSave',
94
94
  'args': (
95
95
  {'name': 'item', 'type': 'any', 'desc': 'The item to be serialized as a JSON string.', },
96
- {'name': 'indent', 'type': 'int', 'desc': 'Specify a number of spaces to indent with.', 'default': None},
96
+ {'name': 'indent', 'type': 'boolean', 'desc': 'Indent serialized data with two spaces.', 'default': False},
97
97
  ),
98
98
  'returns': {'type': 'str', 'desc': 'The JSON serialized object.', }}},
99
99
  {'name': 'schema', 'desc': 'Get a JS schema validation object.',
@@ -116,24 +116,21 @@ class JsonLib(s_stormtypes.Lib):
116
116
  }
117
117
 
118
118
  @s_stormtypes.stormfunc(readonly=True)
119
- async def _jsonSave(self, item, indent=None):
120
- indent = await s_stormtypes.toint(indent, noneok=True)
119
+ async def _jsonSave(self, item, indent=False):
120
+ indent = await s_stormtypes.tobool(indent)
121
121
 
122
122
  try:
123
123
  item = await s_stormtypes.toprim(item)
124
- return json.dumps(item, indent=indent)
125
- except Exception as e:
124
+ except Exception:
126
125
  mesg = f'Argument is not JSON compatible: {item}'
127
126
  raise s_exc.MustBeJsonSafe(mesg=mesg)
128
127
 
128
+ return s_json.dumps(item, indent=indent).decode()
129
+
129
130
  @s_stormtypes.stormfunc(readonly=True)
130
131
  async def _jsonLoad(self, text):
131
132
  text = await s_stormtypes.tostr(text)
132
- try:
133
- return json.loads(text, strict=True)
134
- except Exception as e:
135
- mesg = f'Text is not valid JSON: {text}'
136
- raise s_exc.BadJsonText(mesg=mesg)
133
+ return s_json.loads(text)
137
134
 
138
135
  @s_stormtypes.stormfunc(readonly=True)
139
136
  async def _jsonSchema(self, schema, use_default=True):
@@ -200,7 +200,7 @@ class LibModelTags(s_stormtypes.Lib):
200
200
  Examples:
201
201
  Create a tag model for the ``cno.cve`` tag::
202
202
 
203
- $regx = ($lib.null, $lib.null, "[0-9]{4}", "[0-9]{5}")
203
+ $regx = ([null, null, "[0-9]{4}", "[0-9]{5}"])
204
204
  $lib.model.tags.set(cno.cve, regex, $regx)''',
205
205
  'type': {'type': 'function', '_funcname': '_setTagModel',
206
206
  'args': (
@@ -41,7 +41,7 @@ class NotifyLib(s_stormtypes.Lib):
41
41
  {
42
42
  'name': 'get',
43
43
  'desc': '''
44
- Return a notification by ID (or $lib.null).
44
+ Return a notification by ID (or ``(null)`` ).
45
45
 
46
46
  ''',
47
47
  'type': {
@@ -51,7 +51,7 @@ class NotifyLib(s_stormtypes.Lib):
51
51
  ),
52
52
  'returns': {
53
53
  'name': 'retn', 'type': 'dict',
54
- 'desc': 'The requested notification or $lib.null.'},
54
+ 'desc': 'The requested notification or ``(null)``.'},
55
55
  },
56
56
  },
57
57
  )