synapse 2.202.0__py311-none-any.whl → 2.204.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/axon.py +4 -4
- synapse/cmds/cortex.py +4 -6
- synapse/cmds/hive.py +10 -10
- synapse/common.py +17 -58
- synapse/cortex.py +6 -6
- synapse/data/__init__.py +3 -2
- synapse/data/iana.uris.mpk +1 -0
- synapse/lib/auth.py +3 -0
- synapse/lib/autodoc.py +3 -3
- synapse/lib/cell.py +21 -1
- synapse/lib/cli.py +2 -2
- synapse/lib/config.py +2 -2
- synapse/lib/encoding.py +4 -3
- synapse/lib/httpapi.py +7 -11
- synapse/lib/json.py +224 -0
- synapse/lib/lmdbslab.py +1 -1
- synapse/lib/oauth.py +176 -54
- synapse/lib/rstorm.py +18 -14
- synapse/lib/schemas.py +87 -1
- synapse/lib/scrape.py +35 -13
- synapse/lib/snap.py +2 -1
- synapse/lib/storm.lark +2 -2
- synapse/lib/storm.py +11 -6
- synapse/lib/stormhttp.py +11 -13
- synapse/lib/stormlib/aha.py +4 -4
- synapse/lib/stormlib/auth.py +1 -1
- synapse/lib/stormlib/cache.py +2 -2
- synapse/lib/stormlib/cortex.py +5 -5
- synapse/lib/stormlib/graph.py +1 -1
- synapse/lib/stormlib/imap.py +1 -1
- synapse/lib/stormlib/json.py +8 -11
- synapse/lib/stormlib/model.py +1 -1
- synapse/lib/stormlib/notifications.py +2 -2
- synapse/lib/stormlib/oauth.py +105 -2
- synapse/lib/stormlib/stats.py +4 -0
- synapse/lib/stormlib/stix.py +3 -4
- synapse/lib/stormlib/vault.py +6 -6
- synapse/lib/stormlib/xml.py +2 -2
- synapse/lib/stormtypes.py +19 -28
- synapse/lib/structlog.py +3 -3
- synapse/lib/types.py +2 -1
- synapse/lib/urlhelp.py +28 -4
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +7 -3
- synapse/models/base.py +51 -2
- synapse/models/person.py +5 -2
- synapse/telepath.py +5 -3
- synapse/tests/files/__init__.py +0 -1
- synapse/tests/test_axon.py +1 -1
- synapse/tests/test_cmds_cortex.py +3 -2
- synapse/tests/test_cmds_hive.py +4 -4
- synapse/tests/test_common.py +29 -19
- synapse/tests/test_cortex.py +5 -5
- synapse/tests/test_lib_ast.py +3 -3
- synapse/tests/test_lib_autodoc.py +5 -5
- synapse/tests/test_lib_base.py +1 -1
- synapse/tests/test_lib_cell.py +16 -10
- synapse/tests/test_lib_config.py +2 -2
- synapse/tests/test_lib_encoding.py +2 -2
- synapse/tests/test_lib_grammar.py +86 -64
- synapse/tests/test_lib_httpapi.py +56 -13
- synapse/tests/test_lib_json.py +219 -0
- synapse/tests/test_lib_multislabseqn.py +2 -1
- synapse/tests/test_lib_node.py +2 -2
- synapse/tests/test_lib_scrape.py +50 -0
- synapse/tests/test_lib_storm.py +36 -8
- synapse/tests/test_lib_stormhttp.py +4 -4
- synapse/tests/test_lib_stormlib_auth.py +3 -2
- synapse/tests/test_lib_stormlib_cortex.py +10 -12
- synapse/tests/test_lib_stormlib_infosec.py +2 -3
- synapse/tests/test_lib_stormlib_json.py +18 -21
- synapse/tests/test_lib_stormlib_log.py +1 -1
- synapse/tests/test_lib_stormlib_oauth.py +603 -1
- synapse/tests/test_lib_stormlib_stats.py +13 -3
- synapse/tests/test_lib_stormlib_stix.py +5 -5
- synapse/tests/test_lib_stormtypes.py +4 -4
- synapse/tests/test_lib_structlog.py +5 -6
- synapse/tests/test_lib_urlhelp.py +7 -0
- synapse/tests/test_lib_view.py +8 -0
- synapse/tests/test_model_base.py +32 -0
- synapse/tests/test_model_infotech.py +2 -2
- synapse/tests/test_model_person.py +2 -0
- synapse/tests/test_telepath.py +0 -1
- synapse/tests/test_tools_cryo_cat.py +4 -3
- synapse/tests/test_tools_docker_validate.py +4 -2
- synapse/tests/test_tools_feed.py +30 -2
- synapse/tests/test_tools_genpkg.py +1 -1
- synapse/tests/test_tools_healthcheck.py +8 -7
- synapse/tests/test_utils.py +2 -2
- synapse/tests/utils.py +3 -3
- synapse/tools/autodoc.py +3 -3
- synapse/tools/changelog.py +2 -2
- synapse/tools/cryo/cat.py +3 -3
- synapse/tools/csvtool.py +2 -3
- synapse/tools/docker/validate.py +5 -5
- synapse/tools/feed.py +2 -1
- synapse/tools/genpkg.py +3 -2
- synapse/tools/healthcheck.py +2 -3
- synapse/tools/json2mpk.py +2 -2
- synapse/utils/getrefs.py +6 -6
- synapse/vendor/cpython/lib/json.py +35 -0
- synapse/vendor/cpython/lib/test/test_json.py +22 -0
- {synapse-2.202.0.dist-info → synapse-2.204.0.dist-info}/METADATA +4 -2
- {synapse-2.202.0.dist-info → synapse-2.204.0.dist-info}/RECORD +107 -102
- {synapse-2.202.0.dist-info → synapse-2.204.0.dist-info}/WHEEL +1 -1
- {synapse-2.202.0.dist-info → synapse-2.204.0.dist-info/licenses}/LICENSE +0 -0
- {synapse-2.202.0.dist-info → synapse-2.204.0.dist-info}/top_level.txt +0 -0
synapse/lib/stormlib/oauth.py
CHANGED
|
@@ -145,15 +145,118 @@ class OAuthV2Lib(s_stormtypes.Lib):
|
|
|
145
145
|
})
|
|
146
146
|
|
|
147
147
|
// Optionally enable PKCE
|
|
148
|
-
$conf.extensions = ({"pkce":
|
|
148
|
+
$conf.extensions = ({"pkce": true})
|
|
149
149
|
|
|
150
150
|
// Optionally disable SSL verification
|
|
151
|
-
$conf.ssl_verify =
|
|
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': {
|
synapse/lib/stormlib/stats.py
CHANGED
|
@@ -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:
|
synapse/lib/stormlib/stix.py
CHANGED
|
@@ -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 =
|
|
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
|
|
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 =
|
|
1374
|
+
ndef = s_msgpack.deepcopy(node.ndef, use_list=True)
|
|
1376
1375
|
retn = {
|
|
1377
1376
|
'id': stixid,
|
|
1378
1377
|
'type': stixtype,
|
synapse/lib/stormlib/vault.py
CHANGED
|
@@ -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
|
|
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
|
|
362
|
+
'desc': 'The scope for the specified type. If ``(null)``, then getByType will search.'},
|
|
363
363
|
),
|
|
364
|
-
'returns': {'type': 'vault', 'desc': 'Vault or
|
|
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.
|
|
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': '
|
|
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': '
|
|
659
|
+
'returns': {'type': 'boolean', 'desc': '``(true)`` if the vault was deleted, ``(false)`` otherwise.', }}},
|
|
660
660
|
)
|
|
661
661
|
_storm_typename = 'vault'
|
|
662
662
|
_ismutable = False
|
synapse/lib/stormlib/xml.py
CHANGED
|
@@ -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
|
|
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
|
|
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': '
|
|
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': '
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
4988
|
+
encoding = s_json.detect_encoding(valu)
|
|
4994
4989
|
else:
|
|
4995
4990
|
encoding = await tostr(encoding)
|
|
4996
4991
|
|
|
4997
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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/urlhelp.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import urllib.parse
|
|
2
2
|
|
|
3
|
-
import regex
|
|
4
3
|
import synapse.exc as s_exc
|
|
5
4
|
|
|
5
|
+
import synapse.lib.cache as s_cache
|
|
6
|
+
|
|
6
7
|
def chopurl(url):
|
|
7
8
|
'''
|
|
8
9
|
A sane "stand alone" url parser.
|
|
@@ -70,8 +71,7 @@ def chopurl(url):
|
|
|
70
71
|
ret['path'] = pathrem
|
|
71
72
|
return ret
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
@s_cache.memoize(size=1024)
|
|
75
75
|
def sanitizeUrl(url):
|
|
76
76
|
'''
|
|
77
77
|
Returns a URL with the password (if present) replaced with ``****``
|
|
@@ -85,4 +85,28 @@ def sanitizeUrl(url):
|
|
|
85
85
|
Note: this depends on this being a reasonably-well formatted URI that starts with a scheme (e.g. http) and '//:'
|
|
86
86
|
Failure of this condition yields the original string.
|
|
87
87
|
'''
|
|
88
|
-
|
|
88
|
+
try:
|
|
89
|
+
info = chopurl(url)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return url
|
|
92
|
+
else:
|
|
93
|
+
if info.get('passwd'):
|
|
94
|
+
# Rebuild the URL from info
|
|
95
|
+
valu = f"{info.get('scheme')}://{info.get('user')}:****@"
|
|
96
|
+
host = info.get('host')
|
|
97
|
+
port = info.get('port')
|
|
98
|
+
if ':' in host and port:
|
|
99
|
+
valu = f"{valu}[{host}]:{port}"
|
|
100
|
+
elif port:
|
|
101
|
+
valu = f"{valu}{host}:{port}"
|
|
102
|
+
else:
|
|
103
|
+
valu = f"{valu}{host}"
|
|
104
|
+
if path := info.get('path'):
|
|
105
|
+
valu = f"{valu}{path}"
|
|
106
|
+
if query := info.get('query'):
|
|
107
|
+
parts = []
|
|
108
|
+
for k, v in query.items():
|
|
109
|
+
parts.append(f'{k}={v}')
|
|
110
|
+
valu = f"{valu}?{'&'.join(parts)}"
|
|
111
|
+
return valu
|
|
112
|
+
return url
|
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, 204, 0)
|
|
227
227
|
verstring = '.'.join([str(x) for x in version])
|
|
228
|
-
commit = '
|
|
228
|
+
commit = '6b616fcd2d70a036d9853355fcb6fd491e84f015'
|
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
|
|
synapse/models/person.py
CHANGED
|
@@ -446,8 +446,11 @@ class PsModule(s_module.CoreModule):
|
|
|
446
446
|
'doc': 'The fax number for this contact.',
|
|
447
447
|
}),
|
|
448
448
|
('phone:work', ('tel:phone', {}), {
|
|
449
|
-
'doc': 'The work phone number for this contact.',
|
|
450
|
-
|
|
449
|
+
'doc': 'The work phone number for this contact.'}),
|
|
450
|
+
|
|
451
|
+
('id', ('str', {'strip': True}), {
|
|
452
|
+
'doc': 'A type or source specific unique ID for the contact.'}),
|
|
453
|
+
|
|
451
454
|
('id:number', ('ou:id:number', {}), {
|
|
452
455
|
'alts': ('id:numbers',),
|
|
453
456
|
'doc': 'An ID number issued by an org and associated with this contact.',
|