synapse 2.164.0__py311-none-any.whl → 2.166.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 +3 -3
- synapse/cmds/cortex.py +1 -6
- synapse/common.py +7 -1
- synapse/cortex.py +145 -192
- synapse/datamodel.py +36 -1
- synapse/lib/agenda.py +87 -97
- synapse/lib/aha.py +51 -0
- synapse/lib/ast.py +22 -23
- synapse/lib/base.py +0 -6
- synapse/lib/boss.py +3 -0
- synapse/lib/cell.py +70 -39
- synapse/lib/certdir.py +9 -0
- synapse/lib/hiveauth.py +65 -12
- synapse/lib/httpapi.py +1 -0
- synapse/lib/modelrev.py +121 -33
- synapse/lib/modules.py +1 -0
- synapse/lib/nexus.py +64 -26
- synapse/lib/parser.py +2 -0
- synapse/lib/schemas.py +14 -0
- synapse/lib/snap.py +50 -4
- synapse/lib/storm.lark +4 -3
- synapse/lib/storm.py +96 -22
- synapse/lib/storm_format.py +1 -0
- synapse/lib/stormlib/aha.py +7 -1
- synapse/lib/stormlib/auth.py +13 -5
- synapse/lib/stormlib/cache.py +202 -0
- synapse/lib/stormlib/cortex.py +147 -8
- synapse/lib/stormlib/gen.py +53 -6
- synapse/lib/stormlib/math.py +1 -1
- synapse/lib/stormlib/model.py +11 -1
- synapse/lib/stormlib/spooled.py +109 -0
- synapse/lib/stormlib/vault.py +1 -1
- synapse/lib/stormtypes.py +113 -17
- synapse/lib/trigger.py +36 -47
- synapse/lib/types.py +29 -2
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +80 -53
- synapse/models/economic.py +174 -5
- synapse/models/files.py +2 -0
- synapse/models/inet.py +77 -2
- synapse/models/infotech.py +12 -12
- synapse/models/orgs.py +72 -21
- synapse/models/person.py +40 -11
- synapse/models/risk.py +78 -24
- synapse/models/science.py +102 -0
- synapse/telepath.py +117 -35
- synapse/tests/test_cortex.py +84 -158
- synapse/tests/test_datamodel.py +22 -0
- synapse/tests/test_lib_agenda.py +52 -96
- synapse/tests/test_lib_aha.py +126 -4
- synapse/tests/test_lib_ast.py +412 -6
- synapse/tests/test_lib_cell.py +24 -8
- synapse/tests/test_lib_certdir.py +32 -0
- synapse/tests/test_lib_grammar.py +9 -1
- synapse/tests/test_lib_httpapi.py +0 -1
- synapse/tests/test_lib_jupyter.py +0 -1
- synapse/tests/test_lib_modelrev.py +41 -0
- synapse/tests/test_lib_nexus.py +38 -0
- synapse/tests/test_lib_storm.py +95 -5
- synapse/tests/test_lib_stormlib_cache.py +272 -0
- synapse/tests/test_lib_stormlib_cortex.py +71 -0
- synapse/tests/test_lib_stormlib_gen.py +37 -2
- synapse/tests/test_lib_stormlib_model.py +2 -0
- synapse/tests/test_lib_stormlib_spooled.py +190 -0
- synapse/tests/test_lib_stormlib_vault.py +12 -3
- synapse/tests/test_lib_stormsvc.py +0 -10
- synapse/tests/test_lib_stormtypes.py +60 -8
- synapse/tests/test_lib_trigger.py +20 -2
- synapse/tests/test_lib_types.py +17 -1
- synapse/tests/test_model_economic.py +114 -0
- synapse/tests/test_model_files.py +2 -0
- synapse/tests/test_model_inet.py +73 -1
- synapse/tests/test_model_infotech.py +2 -2
- synapse/tests/test_model_orgs.py +10 -1
- synapse/tests/test_model_risk.py +30 -2
- synapse/tests/test_model_science.py +59 -0
- synapse/tests/test_model_syn.py +0 -1
- synapse/tests/test_telepath.py +30 -7
- synapse/tests/test_tools_modrole.py +81 -0
- synapse/tests/test_tools_moduser.py +105 -0
- synapse/tools/modrole.py +59 -7
- synapse/tools/moduser.py +78 -10
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/METADATA +2 -2
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/RECORD +87 -83
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/WHEEL +1 -1
- synapse/lib/provenance.py +0 -111
- synapse/tests/test_lib_provenance.py +0 -37
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/LICENSE +0 -0
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import synapse.exc as s_exc
|
|
4
|
+
|
|
5
|
+
import synapse.lib.ast as s_ast
|
|
6
|
+
import synapse.lib.cache as s_cache
|
|
7
|
+
import synapse.lib.stormctrl as s_stormctrl
|
|
8
|
+
import synapse.lib.stormtypes as s_stormtypes
|
|
9
|
+
|
|
10
|
+
CACHE_SIZE_MAX = 10_000
|
|
11
|
+
CACHE_SIZE_DEFAULT = 10_000
|
|
12
|
+
|
|
13
|
+
@s_stormtypes.registry.registerLib
|
|
14
|
+
class LibCache(s_stormtypes.Lib):
|
|
15
|
+
'''
|
|
16
|
+
A Storm Library for interacting with Cache Objects.
|
|
17
|
+
'''
|
|
18
|
+
_storm_locals = (
|
|
19
|
+
{'name': 'fixed', 'desc': '''
|
|
20
|
+
Get a new Fixed Cache object.
|
|
21
|
+
|
|
22
|
+
On a cache-miss when calling .get(), the callback Storm query is executed in a sub-runtime
|
|
23
|
+
in the current execution context. A special variable, $cache_key, will be set
|
|
24
|
+
to the key argument provided to .get().
|
|
25
|
+
|
|
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.
|
|
28
|
+
|
|
29
|
+
The fixed cache uses FIFO to evict items once the maximum size is reached.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
|
|
33
|
+
// Use a callback query with a function that modifies the outer runtime,
|
|
34
|
+
// since it will run in the scope where it was defined.
|
|
35
|
+
$test = foo
|
|
36
|
+
|
|
37
|
+
function callback(key) {
|
|
38
|
+
$test = $key // this will modify $test in the outer runtime
|
|
39
|
+
return(`{$key}-val`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
$cache = $lib.cache.fixed(${ return($callback($cache_key)) })
|
|
43
|
+
$value = $cache.get(bar)
|
|
44
|
+
$lib.print($test) // this will equal "bar"
|
|
45
|
+
|
|
46
|
+
// Use a callback query that will not modify the outer runtime,
|
|
47
|
+
// except for variables accessible as references.
|
|
48
|
+
$test = foo
|
|
49
|
+
$tests = ([])
|
|
50
|
+
|
|
51
|
+
$cache = $lib.cache.fixed(${
|
|
52
|
+
$test = $cache_key // this will *not* modify $test in the outer runtime
|
|
53
|
+
$tests.append($cache_key) // this will modify $tests in the outer runtime
|
|
54
|
+
return(`{$cache_key}-val`)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
$value = $cache.get(bar)
|
|
58
|
+
$lib.print($test) // this will equal "foo"
|
|
59
|
+
$lib.print($tests) // this will equal (foo,)
|
|
60
|
+
''',
|
|
61
|
+
'type': {'type': 'function', '_funcname': '_methFixedCache',
|
|
62
|
+
'args': (
|
|
63
|
+
{'name': 'callback', 'type': ['str', 'storm:query'],
|
|
64
|
+
'desc': 'A Storm query that will return a value for $cache_key on a cache miss.', },
|
|
65
|
+
{'name': 'size', 'type': 'int', 'default': CACHE_SIZE_DEFAULT,
|
|
66
|
+
'desc': 'The maximum size of the cache.', },
|
|
67
|
+
),
|
|
68
|
+
'returns': {'type': 'cache:fixed', 'desc': 'A new ``cache:fixed`` object.'}}},
|
|
69
|
+
)
|
|
70
|
+
_storm_lib_path = ('cache',)
|
|
71
|
+
|
|
72
|
+
def getObjLocals(self):
|
|
73
|
+
return {
|
|
74
|
+
'fixed': self._methFixedCache,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
78
|
+
async def _methFixedCache(self, callback, size=CACHE_SIZE_DEFAULT):
|
|
79
|
+
size = await s_stormtypes.toint(size)
|
|
80
|
+
callback = await s_stormtypes.tostr(callback)
|
|
81
|
+
|
|
82
|
+
if size < 1 or size > CACHE_SIZE_MAX:
|
|
83
|
+
raise s_exc.BadArg(mesg=f'Cache size must be between 1-{CACHE_SIZE_MAX}')
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
query = await self.runt.getStormQuery(callback)
|
|
87
|
+
except s_exc.BadSyntax as e:
|
|
88
|
+
raise s_exc.BadArg(mesg=f'Invalid callback query: {e.errinfo.get("mesg")}')
|
|
89
|
+
|
|
90
|
+
if not query.hasAstClass(s_ast.Return):
|
|
91
|
+
raise s_exc.BadArg(mesg='Callback query must return a value')
|
|
92
|
+
|
|
93
|
+
return FixedCache(self.runt, query, size=size)
|
|
94
|
+
|
|
95
|
+
@s_stormtypes.registry.registerType
|
|
96
|
+
class FixedCache(s_stormtypes.StormType):
|
|
97
|
+
'''
|
|
98
|
+
A StormLib API instance of a Storm Fixed Cache.
|
|
99
|
+
'''
|
|
100
|
+
_storm_locals = (
|
|
101
|
+
{'name': 'query', 'desc': 'Get the callback Storm query as string.',
|
|
102
|
+
'type': {'type': 'gtor', '_gtorfunc': '_gtorQuery',
|
|
103
|
+
'returns': {'type': 'str', 'desc': 'The callback Storm query text.', }}},
|
|
104
|
+
{'name': 'get', 'desc': 'Get an item from the cache by key.',
|
|
105
|
+
'type': {'type': 'function', '_funcname': '_methGet',
|
|
106
|
+
'args': (
|
|
107
|
+
{'name': 'key', 'type': 'any', 'desc': 'The key to lookup.'},
|
|
108
|
+
),
|
|
109
|
+
'returns': {'type': 'any',
|
|
110
|
+
'desc': 'The value from the cache, or the callback query if it does not exist', }}},
|
|
111
|
+
{'name': 'pop', 'desc': 'Pop an item from the cache.',
|
|
112
|
+
'type': {'type': 'function', '_funcname': '_methPop',
|
|
113
|
+
'args': (
|
|
114
|
+
{'name': 'key', 'type': 'any', 'desc': 'The key to pop.'},
|
|
115
|
+
),
|
|
116
|
+
'returns': {'type': 'any',
|
|
117
|
+
'desc': 'The value from the cache, or $lib.null if it does not exist', }}},
|
|
118
|
+
{'name': 'put', 'desc': 'Put an item into the cache.',
|
|
119
|
+
'type': {'type': 'function', '_funcname': '_methPut',
|
|
120
|
+
'args': (
|
|
121
|
+
{'name': 'key', 'type': 'any', 'desc': 'The key put in the cache.'},
|
|
122
|
+
{'name': 'value', 'type': 'any', 'desc': 'The value to assign to the key.'},
|
|
123
|
+
),
|
|
124
|
+
'returns': {'type': 'null', }}},
|
|
125
|
+
{'name': 'clear', 'desc': 'Clear all items from the cache.',
|
|
126
|
+
'type': {'type': 'function', '_funcname': '_methClear',
|
|
127
|
+
'returns': {'type': 'null', }}},
|
|
128
|
+
)
|
|
129
|
+
_storm_typename = 'cache:fixed'
|
|
130
|
+
_ismutable = False
|
|
131
|
+
|
|
132
|
+
def __init__(self, runt, query, size=CACHE_SIZE_DEFAULT):
|
|
133
|
+
s_stormtypes.StormType.__init__(self)
|
|
134
|
+
self.runt = runt
|
|
135
|
+
self.size = size
|
|
136
|
+
self.query = query
|
|
137
|
+
self.locls.update(self.getObjLocals())
|
|
138
|
+
self.gtors.update({
|
|
139
|
+
'query': self._gtorQuery,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
self.cache = s_cache.FixedCache(self._runCallback, size=size)
|
|
143
|
+
|
|
144
|
+
def __len__(self):
|
|
145
|
+
return len(self.cache)
|
|
146
|
+
|
|
147
|
+
async def stormrepr(self):
|
|
148
|
+
if len(qtext := self.query.text) > 100:
|
|
149
|
+
qtext = qtext[:100] + '...'
|
|
150
|
+
return f'{self._storm_typename}: size={self.size} query="{qtext}"'
|
|
151
|
+
|
|
152
|
+
def getObjLocals(self):
|
|
153
|
+
return {
|
|
154
|
+
'pop': self._methPop,
|
|
155
|
+
'put': self._methPut,
|
|
156
|
+
'get': self._methGet,
|
|
157
|
+
'clear': self._methClear,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async def _gtorQuery(self):
|
|
161
|
+
return self.query.text
|
|
162
|
+
|
|
163
|
+
async def _runCallback(self, key):
|
|
164
|
+
|
|
165
|
+
varz = self.runt.getScopeVars()
|
|
166
|
+
varz['cache_key'] = key
|
|
167
|
+
|
|
168
|
+
opts = {'vars': varz}
|
|
169
|
+
async with self.runt.getCmdRuntime(self.query, opts=opts) as runt:
|
|
170
|
+
try:
|
|
171
|
+
async for _ in runt.execute():
|
|
172
|
+
await asyncio.sleep(0)
|
|
173
|
+
except s_stormctrl.StormReturn as e:
|
|
174
|
+
return await s_stormtypes.toprim(e.item)
|
|
175
|
+
except s_stormctrl.StormCtrlFlow:
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
async def _reqKey(self, key):
|
|
179
|
+
if s_stormtypes.ismutable(key):
|
|
180
|
+
mesg = 'Mutable values are not allowed as cache keys'
|
|
181
|
+
raise s_exc.BadArg(mesg=mesg, name=await s_stormtypes.torepr(key))
|
|
182
|
+
return await s_stormtypes.toprim(key)
|
|
183
|
+
|
|
184
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
185
|
+
async def _methPop(self, key):
|
|
186
|
+
key = await self._reqKey(key)
|
|
187
|
+
return self.cache.pop(key)
|
|
188
|
+
|
|
189
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
190
|
+
async def _methPut(self, key, value):
|
|
191
|
+
key = await self._reqKey(key)
|
|
192
|
+
val = await s_stormtypes.toprim(value)
|
|
193
|
+
self.cache.put(key, val)
|
|
194
|
+
|
|
195
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
196
|
+
async def _methGet(self, key):
|
|
197
|
+
key = await self._reqKey(key)
|
|
198
|
+
return await self.cache.aget(key)
|
|
199
|
+
|
|
200
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
201
|
+
async def _methClear(self):
|
|
202
|
+
self.cache.clear()
|
synapse/lib/stormlib/cortex.py
CHANGED
|
@@ -3,7 +3,9 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
import synapse.exc as s_exc
|
|
6
|
+
import synapse.telepath as s_telepath
|
|
6
7
|
|
|
8
|
+
import synapse.lib.storm as s_storm
|
|
7
9
|
import synapse.lib.stormtypes as s_stormtypes
|
|
8
10
|
import synapse.lib.stormlib.auth as slib_auth
|
|
9
11
|
|
|
@@ -85,6 +87,7 @@ stormcmds = [
|
|
|
85
87
|
$lib.print(`!View: No view found ({$err.info.iden})`)
|
|
86
88
|
}
|
|
87
89
|
$lib.print(`Readonly: {$api.readonly}`)
|
|
90
|
+
$lib.print(`Pool enabled: {$api.pool}`)
|
|
88
91
|
$lib.print(`Authenticated: {$api.authenticated}`)
|
|
89
92
|
$lib.print(`Name: {$api.name}`)
|
|
90
93
|
$lib.print(`Description: {$api.desc}`)
|
|
@@ -223,6 +226,9 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
223
226
|
''',
|
|
224
227
|
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storPath', '_gtorfunc': '_gtorPath',
|
|
225
228
|
'returns': {'type': 'str'}}},
|
|
229
|
+
{'name': 'pool', 'desc': 'Boolean value indicating if the handler responses may be executed as part of a Storm pool.',
|
|
230
|
+
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storPool', '_gtorfunc': '_gtorPool',
|
|
231
|
+
'returns': {'type': 'boolean'}}},
|
|
226
232
|
{'name': 'vars', 'desc': 'The Storm runtime variables specific for the API instance.',
|
|
227
233
|
'type': {'type': ['stor', 'ctor'], '_storfunc': '_storVars', '_ctorfunc': '_ctorVars',
|
|
228
234
|
'returns': {'type': 'http:api:vars'}}},
|
|
@@ -241,6 +247,8 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
241
247
|
{'name': 'authenticated', 'desc': 'Boolean value indicating if the Extended HTTP API requires an authenticated user or session.',
|
|
242
248
|
'type': {'type': ['stor', 'gtor'], '_storfunc': '_storAuthenticated', '_gtorfunc': '_gtorAuthenticated',
|
|
243
249
|
'returns': {'type': 'boolean'}}},
|
|
250
|
+
{'name': 'methods', 'desc': 'The dictionary containing the Storm code used to implement the HTTP methods.',
|
|
251
|
+
'type': {'type': ['ctor'], '_ctorfunc': '_ctorMethods', 'returns': {'type': 'http:api:methods'}}}
|
|
244
252
|
)
|
|
245
253
|
|
|
246
254
|
def __init__(self, runt, info):
|
|
@@ -256,6 +264,7 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
256
264
|
'name': self._storName,
|
|
257
265
|
'desc': self._storDesc,
|
|
258
266
|
'path': self._storPath,
|
|
267
|
+
'pool': self._storPool,
|
|
259
268
|
'vars': self._storVars,
|
|
260
269
|
'view': self._storView,
|
|
261
270
|
'runas': self._storRunas,
|
|
@@ -269,6 +278,7 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
269
278
|
'name': self._gtorName,
|
|
270
279
|
'desc': self._gtorDesc,
|
|
271
280
|
'path': self._gtorPath,
|
|
281
|
+
'pool': self._gtorPool,
|
|
272
282
|
'view': self._gtorView,
|
|
273
283
|
'runas': self._gtorRunas,
|
|
274
284
|
'owner': self._gtorOwner,
|
|
@@ -291,6 +301,13 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
291
301
|
'created': self.info.get('created'),
|
|
292
302
|
})
|
|
293
303
|
|
|
304
|
+
async def stormrepr(self):
|
|
305
|
+
name = await self._gtorName()
|
|
306
|
+
if not name:
|
|
307
|
+
name = '<no name>'
|
|
308
|
+
path = await self._gtorPath()
|
|
309
|
+
return f'{self._storm_typename}: {name} ({self.iden}), path={path}'
|
|
310
|
+
|
|
294
311
|
def getObjLocals(self):
|
|
295
312
|
return {
|
|
296
313
|
'pack': self._methPack,
|
|
@@ -298,7 +315,10 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
298
315
|
|
|
299
316
|
@s_stormtypes.stormfunc(readonly=True)
|
|
300
317
|
async def _methPack(self):
|
|
301
|
-
|
|
318
|
+
# TODO: Remove this when we've migrated the HTTPAPI data to set this value.
|
|
319
|
+
ret = copy.deepcopy(self.info)
|
|
320
|
+
ret.setdefault('pool', False)
|
|
321
|
+
return ret
|
|
302
322
|
|
|
303
323
|
@s_stormtypes.stormfunc(readonly=True)
|
|
304
324
|
def _ctorMethods(self, path=None):
|
|
@@ -315,6 +335,17 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
315
335
|
async def _gtorPath(self):
|
|
316
336
|
return self.info.get('path')
|
|
317
337
|
|
|
338
|
+
async def _storPool(self, pool):
|
|
339
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
340
|
+
pool = await s_stormtypes.tobool(pool)
|
|
341
|
+
adef = await self.runt.snap.core.modHttpExtApi(self.iden, 'pool', pool)
|
|
342
|
+
self.info['pool'] = pool
|
|
343
|
+
self.info['updated'] = adef.get('updated')
|
|
344
|
+
|
|
345
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
346
|
+
async def _gtorPool(self):
|
|
347
|
+
return self.info.get('pool')
|
|
348
|
+
|
|
318
349
|
async def _storName(self, name):
|
|
319
350
|
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'set'))
|
|
320
351
|
name = await s_stormtypes.tostr(name)
|
|
@@ -452,7 +483,7 @@ class HttpApi(s_stormtypes.StormType):
|
|
|
452
483
|
@s_stormtypes.registry.registerType
|
|
453
484
|
class HttpApiMethods(s_stormtypes.Prim):
|
|
454
485
|
'''
|
|
455
|
-
Accessor dictionary for getting and setting
|
|
486
|
+
Accessor dictionary for getting and setting Extended HTTP API methods.
|
|
456
487
|
|
|
457
488
|
Notes:
|
|
458
489
|
The Storm code used to run these methods will have a $request object
|
|
@@ -1054,7 +1085,7 @@ class CortexHttpApi(s_stormtypes.Lib):
|
|
|
1054
1085
|
{'name': 'readonly', 'type': 'boolean',
|
|
1055
1086
|
'desc': 'Run the Extended HTTP Storm methods in readonly mode.', 'default': False},
|
|
1056
1087
|
),
|
|
1057
|
-
'returns': {'type': 'http:api', 'desc': 'A new http:api object.'}}},
|
|
1088
|
+
'returns': {'type': 'http:api', 'desc': 'A new ``http:api`` object.'}}},
|
|
1058
1089
|
{'name': 'del', 'desc': 'Delete an Extended HTTP API endpoint.',
|
|
1059
1090
|
'type': {'type': 'function', '_funcname': 'delHttpApi',
|
|
1060
1091
|
'args': (
|
|
@@ -1062,16 +1093,29 @@ class CortexHttpApi(s_stormtypes.Lib):
|
|
|
1062
1093
|
'desc': 'The iden of the API to delete.'},
|
|
1063
1094
|
),
|
|
1064
1095
|
'returns': {'type': 'null'}}},
|
|
1065
|
-
{'name': 'get', 'desc': 'Get an Extended
|
|
1096
|
+
{'name': 'get', 'desc': 'Get an Extended ``http:api`` object.',
|
|
1066
1097
|
'type': {'type': 'function', '_funcname': 'getHttpApi',
|
|
1067
1098
|
'args': (
|
|
1068
1099
|
{'name': 'iden', 'type': 'string',
|
|
1069
|
-
'desc': 'The iden of the API to
|
|
1100
|
+
'desc': 'The iden of the API to retrieve.'},
|
|
1101
|
+
),
|
|
1102
|
+
'returns': {'type': 'http:api', 'desc': 'The ``http:api`` object.'}}},
|
|
1103
|
+
{'name': 'getByPath', 'desc': '''
|
|
1104
|
+
Get an Extended ``http:api`` object by path.
|
|
1105
|
+
|
|
1106
|
+
Notes:
|
|
1107
|
+
The path argument is evaluated as a regular expression input, and will be
|
|
1108
|
+
used to get the first HTTP API handler whose path value has a match.
|
|
1109
|
+
''',
|
|
1110
|
+
'type': {'type': 'function', '_funcname': 'getHttpApiByPath',
|
|
1111
|
+
'args': (
|
|
1112
|
+
{'name': 'path', 'type': 'string',
|
|
1113
|
+
'desc': 'Path to use to retrieve an object.'},
|
|
1070
1114
|
),
|
|
1071
|
-
'returns': {'type': 'http:api', 'desc': 'The http:api object.'}}},
|
|
1072
|
-
{'name': 'list', 'desc': 'Get all the
|
|
1115
|
+
'returns': {'type': ['http:api', 'null'], 'desc': 'The ``http:api`` object or ``$lib.null`` if there is no match.'}}},
|
|
1116
|
+
{'name': 'list', 'desc': 'Get all the Extended HTTP APIs on the Cortex',
|
|
1073
1117
|
'type': {'type': 'function', '_funcname': 'listHttpApis', 'args': (),
|
|
1074
|
-
|
|
1118
|
+
'returns': {'type': 'list', 'desc': 'A list of ``http:api`` objects'}}},
|
|
1075
1119
|
{'name': 'index', 'desc': 'Set the index for a given Extended HTTP API.',
|
|
1076
1120
|
'type': {'type': 'function', '_funcname': 'setHttpApiIndx',
|
|
1077
1121
|
'args': (
|
|
@@ -1110,6 +1154,7 @@ class CortexHttpApi(s_stormtypes.Lib):
|
|
|
1110
1154
|
'list': self.listHttpApis,
|
|
1111
1155
|
'index': self.setHttpApiIndx,
|
|
1112
1156
|
'response': self.makeHttpResponse,
|
|
1157
|
+
'getByPath': self.getHttpApiByPath,
|
|
1113
1158
|
}
|
|
1114
1159
|
|
|
1115
1160
|
@s_stormtypes.stormfunc(readonly=True)
|
|
@@ -1124,6 +1169,15 @@ class CortexHttpApi(s_stormtypes.Lib):
|
|
|
1124
1169
|
adef = await self.runt.snap.core.getHttpExtApi(iden)
|
|
1125
1170
|
return HttpApi(self.runt, adef)
|
|
1126
1171
|
|
|
1172
|
+
@s_stormtypes.stormfunc(readonly=True)
|
|
1173
|
+
async def getHttpApiByPath(self, path):
|
|
1174
|
+
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'get'))
|
|
1175
|
+
path = await s_stormtypes.tostr(path)
|
|
1176
|
+
adef, _ = await self.runt.snap.core.getHttpExtApiByPath(path)
|
|
1177
|
+
if adef is None:
|
|
1178
|
+
return None
|
|
1179
|
+
return HttpApi(self.runt, adef)
|
|
1180
|
+
|
|
1127
1181
|
@s_stormtypes.stormfunc(readonly=True)
|
|
1128
1182
|
async def listHttpApis(self):
|
|
1129
1183
|
s_stormtypes.confirm(('storm', 'lib', 'cortex', 'httpapi', 'get'))
|
|
@@ -1167,3 +1221,88 @@ class CortexHttpApi(s_stormtypes.Lib):
|
|
|
1167
1221
|
iden = await s_stormtypes.tostr(iden)
|
|
1168
1222
|
index = await s_stormtypes.toint(index)
|
|
1169
1223
|
return await self.runt.snap.view.core.setHttpApiIndx(iden, index)
|
|
1224
|
+
|
|
1225
|
+
class StormPoolSetCmd(s_storm.Cmd):
|
|
1226
|
+
'''
|
|
1227
|
+
Setup a Storm query offload mirror pool for the Cortex.
|
|
1228
|
+
'''
|
|
1229
|
+
name = 'cortex.storm.pool.set'
|
|
1230
|
+
def getArgParser(self):
|
|
1231
|
+
pars = s_storm.Cmd.getArgParser(self)
|
|
1232
|
+
pars.add_argument('--connection-timeout', type='int', default=2,
|
|
1233
|
+
help='The maximum amount of time to wait for a connection from the pool to become available.')
|
|
1234
|
+
pars.add_argument('--sync-timeout', type='int', default=2,
|
|
1235
|
+
help='The maximum amount of time to wait for the mirror to be in sync with the leader')
|
|
1236
|
+
pars.add_argument('url', type='str', required=True, help='The telepath URL for the AHA service pool.')
|
|
1237
|
+
return pars
|
|
1238
|
+
|
|
1239
|
+
async def execStormCmd(self, runt, genr):
|
|
1240
|
+
|
|
1241
|
+
if not self.runtsafe: # pragma: no cover
|
|
1242
|
+
mesg = 'cortex.storm.pool.set arguments must be runtsafe.'
|
|
1243
|
+
raise s_exc.StormRuntimeError(mesg=mesg)
|
|
1244
|
+
|
|
1245
|
+
mesg = 'cortex.storm.pool.set command requires global admin permissions.'
|
|
1246
|
+
self.runt.reqAdmin(mesg=mesg)
|
|
1247
|
+
|
|
1248
|
+
async for node, path in genr: # pragma: no cover
|
|
1249
|
+
yield node, path
|
|
1250
|
+
|
|
1251
|
+
try:
|
|
1252
|
+
s_telepath.chopurl(self.opts.url)
|
|
1253
|
+
except s_exc.BadUrl as e:
|
|
1254
|
+
raise s_exc.BadArg(mesg=f'Unable to set Storm pool URL from url={self.opts.url} : {e.get("mesg")}') from None
|
|
1255
|
+
|
|
1256
|
+
opts = {
|
|
1257
|
+
'timeout:sync': self.opts.sync_timeout,
|
|
1258
|
+
'timeout:connection': self.opts.connection_timeout,
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
await self.runt.snap.core.setStormPool(self.opts.url, opts)
|
|
1262
|
+
await self.runt.printf('Storm pool configuration set.')
|
|
1263
|
+
|
|
1264
|
+
class StormPoolDelCmd(s_storm.Cmd):
|
|
1265
|
+
'''
|
|
1266
|
+
Remove a Storm query offload mirror pool configuration.
|
|
1267
|
+
|
|
1268
|
+
Notes:
|
|
1269
|
+
This will result in tearing down any Storm queries currently being serviced by the Storm pool.
|
|
1270
|
+
This may result in this command raising an exception if it was offloaded to a pool member. That would be an expected behavior.
|
|
1271
|
+
'''
|
|
1272
|
+
name = 'cortex.storm.pool.del'
|
|
1273
|
+
|
|
1274
|
+
async def execStormCmd(self, runt, genr):
|
|
1275
|
+
|
|
1276
|
+
mesg = 'cortex.storm.pool.del command requires global admin permissions.'
|
|
1277
|
+
self.runt.reqAdmin(mesg=mesg)
|
|
1278
|
+
|
|
1279
|
+
async for node, path in genr: # pragma: no cover
|
|
1280
|
+
yield node, path
|
|
1281
|
+
|
|
1282
|
+
await self.runt.snap.core.delStormPool()
|
|
1283
|
+
await self.runt.printf('Storm pool configuration removed.')
|
|
1284
|
+
|
|
1285
|
+
class StormPoolGetCmd(s_storm.Cmd):
|
|
1286
|
+
'''
|
|
1287
|
+
Display the current Storm query offload mirror pool configuration.
|
|
1288
|
+
'''
|
|
1289
|
+
name = 'cortex.storm.pool.get'
|
|
1290
|
+
|
|
1291
|
+
async def execStormCmd(self, runt, genr):
|
|
1292
|
+
|
|
1293
|
+
mesg = 'cortex.storm.pool.get command requires global admin permissions.'
|
|
1294
|
+
self.runt.reqAdmin(mesg=mesg)
|
|
1295
|
+
|
|
1296
|
+
async for node, path in genr: # pragma: no cover
|
|
1297
|
+
yield node, path
|
|
1298
|
+
|
|
1299
|
+
item = await self.runt.snap.core.getStormPool()
|
|
1300
|
+
if item is None:
|
|
1301
|
+
await self.runt.printf('No Storm pool configuration found.')
|
|
1302
|
+
return
|
|
1303
|
+
|
|
1304
|
+
url, opts = item
|
|
1305
|
+
|
|
1306
|
+
await self.runt.printf(f'Storm Pool URL: {url}')
|
|
1307
|
+
await self.runt.printf(f'Sync Timeout (secs): {opts.get("timeout:sync")}')
|
|
1308
|
+
await self.runt.printf(f'Connection Timeout (secs): {opts.get("timeout:connection")}')
|
synapse/lib/stormlib/gen.py
CHANGED
|
@@ -46,12 +46,14 @@ class LibGen(s_stormtypes.Lib):
|
|
|
46
46
|
{'name': 'name', 'type': 'str', 'desc': 'The name of the software.'},
|
|
47
47
|
),
|
|
48
48
|
'returns': {'type': 'node', 'desc': 'An it:prod:soft node with the given name.'}}},
|
|
49
|
-
{'name': 'vulnByCve', 'desc': 'Returns risk:vuln node by CVE, adding the node if it does not exist.',
|
|
49
|
+
{'name': 'vulnByCve', 'desc': 'Returns risk:vuln node by CVE and reporter, adding the node if it does not exist.',
|
|
50
50
|
'type': {'type': 'function', '_funcname': '_storm_query',
|
|
51
51
|
'args': (
|
|
52
52
|
{'name': 'cve', 'type': 'str', 'desc': 'The CVE id.'},
|
|
53
53
|
{'name': 'try', 'type': 'boolean', 'default': False,
|
|
54
54
|
'desc': 'Type normalization will fail silently instead of raising an exception.'},
|
|
55
|
+
{'name': 'reporter', 'type': 'str', 'default': None,
|
|
56
|
+
'desc': 'The name of the organization which reported the vulnerability.'},
|
|
55
57
|
),
|
|
56
58
|
'returns': {'type': 'node', 'desc': 'A risk:vuln node with the given CVE.'}}},
|
|
57
59
|
|
|
@@ -128,6 +130,12 @@ class LibGen(s_stormtypes.Lib):
|
|
|
128
130
|
'desc': 'Type normalization will fail silently instead of raising an exception.'},
|
|
129
131
|
),
|
|
130
132
|
'returns': {'type': 'node', 'desc': 'An it:av:scan:result node.'}}},
|
|
133
|
+
{'name': 'geoPlaceByName', 'desc': 'Returns a geo:place node by name, adding the node if it does not exist.',
|
|
134
|
+
'type': {'type': 'function', '_funcname': '_storm_query',
|
|
135
|
+
'args': (
|
|
136
|
+
{'name': 'name', 'type': 'str', 'desc': 'The name of the place.'},
|
|
137
|
+
),
|
|
138
|
+
'returns': {'type': 'node', 'desc': 'A geo:place node with the given name.'}}},
|
|
131
139
|
)
|
|
132
140
|
_storm_lib_path = ('gen',)
|
|
133
141
|
|
|
@@ -222,14 +230,27 @@ class LibGen(s_stormtypes.Lib):
|
|
|
222
230
|
return($node)
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
function vulnByCve(cve, try=$lib.false) {
|
|
233
|
+
function vulnByCve(cve, try=$lib.false, reporter=$lib.null) {
|
|
226
234
|
($ok, $cve) = $__maybeCast($try, it:sec:cve, $cve)
|
|
227
235
|
if (not $ok) { return() }
|
|
228
236
|
|
|
229
237
|
risk:vuln:cve=$cve
|
|
238
|
+
if $reporter {
|
|
239
|
+
+:reporter:name=$reporter
|
|
240
|
+
{ -:reporter [ :reporter=$orgByName($reporter) ] }
|
|
241
|
+
}
|
|
230
242
|
return($node)
|
|
231
243
|
|
|
232
|
-
|
|
244
|
+
$guid = (gen, cve, $cve)
|
|
245
|
+
if $reporter {
|
|
246
|
+
$reporter = $lib.cast(ou:name, $reporter)
|
|
247
|
+
$guid.append($reporter)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[ risk:vuln=$guid :cve=$cve ]
|
|
251
|
+
if $reporter {
|
|
252
|
+
[ :reporter:name=$reporter :reporter=$orgByName($reporter) ]
|
|
253
|
+
}
|
|
233
254
|
return($node)
|
|
234
255
|
}
|
|
235
256
|
|
|
@@ -394,6 +415,16 @@ class LibGen(s_stormtypes.Lib):
|
|
|
394
415
|
|
|
395
416
|
return($node)
|
|
396
417
|
}
|
|
418
|
+
|
|
419
|
+
function geoPlaceByName(name) {
|
|
420
|
+
$geoname = $lib.cast(geo:name, $name)
|
|
421
|
+
|
|
422
|
+
geo:name=$geoname -> geo:place
|
|
423
|
+
return($node)
|
|
424
|
+
|
|
425
|
+
[ geo:place=(gen, name, $geoname) :name=$geoname ]
|
|
426
|
+
return($node)
|
|
427
|
+
}
|
|
397
428
|
'''
|
|
398
429
|
|
|
399
430
|
stormcmds = (
|
|
@@ -483,14 +514,20 @@ stormcmds = (
|
|
|
483
514
|
{
|
|
484
515
|
'name': 'gen.risk.vuln',
|
|
485
516
|
'descr': '''
|
|
486
|
-
Lift (or create) a risk:vuln node based on the CVE.
|
|
517
|
+
Lift (or create) a risk:vuln node based on the CVE and reporter name.
|
|
518
|
+
|
|
519
|
+
Examples:
|
|
520
|
+
|
|
521
|
+
// Yield a risk:vuln node for CVE-2012-0157 reported by Mandiant.
|
|
522
|
+
gen.risk.vuln CVE-2012-0157 Mandiant
|
|
487
523
|
''',
|
|
488
524
|
'cmdargs': (
|
|
489
525
|
('cve', {'help': 'The CVE identifier.'}),
|
|
526
|
+
('reporter', {'help': 'The name of the reporting organization.', 'nargs': '?'}),
|
|
490
527
|
('--try', {'help': 'Type normalization will fail silently instead of raising an exception.',
|
|
491
528
|
'action': 'store_true'}),
|
|
492
529
|
),
|
|
493
|
-
'storm': 'yield $lib.gen.vulnByCve($cmdopts.cve, try=$cmdopts.try)',
|
|
530
|
+
'storm': 'yield $lib.gen.vulnByCve($cmdopts.cve, try=$cmdopts.try, reporter=$cmdopts.reporter)',
|
|
494
531
|
},
|
|
495
532
|
{
|
|
496
533
|
'name': 'gen.ou.industry',
|
|
@@ -594,5 +631,15 @@ stormcmds = (
|
|
|
594
631
|
yield $lib.gen.itAvScanResultByTarget($cmdopts.form, $cmdopts.value, $cmdopts.signame,
|
|
595
632
|
scanner=$cmdopts.scanner_name, time=$cmdopts.time, try=$cmdopts.try)
|
|
596
633
|
''',
|
|
597
|
-
}
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
'name': 'gen.geo.place',
|
|
637
|
+
'descr': '''
|
|
638
|
+
Lift (or create) a geo:place node based on the name.
|
|
639
|
+
''',
|
|
640
|
+
'cmdargs': (
|
|
641
|
+
('name', {'help': 'The name of the place.'}),
|
|
642
|
+
),
|
|
643
|
+
'storm': 'yield $lib.gen.geoPlaceByName($cmdopts.name)',
|
|
644
|
+
},
|
|
598
645
|
)
|
synapse/lib/stormlib/math.py
CHANGED
|
@@ -10,7 +10,7 @@ class MathLib(s_stormtypes.Lib):
|
|
|
10
10
|
'desc': '''
|
|
11
11
|
Convert a value to a Storm Number object.
|
|
12
12
|
|
|
13
|
-
Storm Numbers are high precision fixed point decimals corresponding to
|
|
13
|
+
Storm Numbers are high precision fixed point decimals corresponding to
|
|
14
14
|
the hugenum storage type.
|
|
15
15
|
|
|
16
16
|
This is not to be used for converting a string to an integer.
|
synapse/lib/stormlib/model.py
CHANGED
|
@@ -329,6 +329,7 @@ class LibModel(s_stormtypes.Lib):
|
|
|
329
329
|
@s_cache.memoizemethod(size=100)
|
|
330
330
|
@s_stormtypes.stormfunc(readonly=True)
|
|
331
331
|
async def _methType(self, name):
|
|
332
|
+
name = await s_stormtypes.tostr(name)
|
|
332
333
|
type_ = self.model.type(name)
|
|
333
334
|
if type_ is not None:
|
|
334
335
|
return ModelType(type_)
|
|
@@ -336,6 +337,7 @@ class LibModel(s_stormtypes.Lib):
|
|
|
336
337
|
@s_cache.memoizemethod(size=100)
|
|
337
338
|
@s_stormtypes.stormfunc(readonly=True)
|
|
338
339
|
async def _methProp(self, name):
|
|
340
|
+
name = await s_stormtypes.tostr(name)
|
|
339
341
|
prop = self.model.prop(name)
|
|
340
342
|
if prop is not None:
|
|
341
343
|
return ModelProp(prop)
|
|
@@ -343,6 +345,7 @@ class LibModel(s_stormtypes.Lib):
|
|
|
343
345
|
@s_cache.memoizemethod(size=100)
|
|
344
346
|
@s_stormtypes.stormfunc(readonly=True)
|
|
345
347
|
async def _methForm(self, name):
|
|
348
|
+
name = await s_stormtypes.tostr(name)
|
|
346
349
|
form = self.model.form(name)
|
|
347
350
|
if form is not None:
|
|
348
351
|
return ModelForm(form)
|
|
@@ -350,6 +353,7 @@ class LibModel(s_stormtypes.Lib):
|
|
|
350
353
|
@s_cache.memoize(size=100)
|
|
351
354
|
@s_stormtypes.stormfunc(readonly=True)
|
|
352
355
|
async def _methTagProp(self, name):
|
|
356
|
+
name = await s_stormtypes.tostr(name)
|
|
353
357
|
tagprop = self.model.getTagProp(name)
|
|
354
358
|
if tagprop is not None:
|
|
355
359
|
return ModelTagProp(tagprop)
|
|
@@ -394,7 +398,8 @@ class ModelForm(s_stormtypes.Prim):
|
|
|
394
398
|
return ModelType(self.valu.type, path=path)
|
|
395
399
|
|
|
396
400
|
@s_stormtypes.stormfunc(readonly=True)
|
|
397
|
-
def _getFormProp(self, name):
|
|
401
|
+
async def _getFormProp(self, name):
|
|
402
|
+
name = await s_stormtypes.tostr(name)
|
|
398
403
|
prop = self.valu.prop(name)
|
|
399
404
|
if prop is not None:
|
|
400
405
|
return ModelProp(prop)
|
|
@@ -593,10 +598,12 @@ class LibModelEdge(s_stormtypes.Lib):
|
|
|
593
598
|
|
|
594
599
|
@s_stormtypes.stormfunc(readonly=True)
|
|
595
600
|
def _methValidKeys(self):
|
|
601
|
+
s_common.deprecated('model.edge.validkeys', curv='2.165.0')
|
|
596
602
|
return self.validedgekeys
|
|
597
603
|
|
|
598
604
|
@s_stormtypes.stormfunc(readonly=True)
|
|
599
605
|
async def _methEdgeGet(self, verb):
|
|
606
|
+
s_common.deprecated('model.edge.get', curv='2.165.0')
|
|
600
607
|
verb = await s_stormtypes.tostr(verb)
|
|
601
608
|
await self._chkEdgeVerbInView(verb)
|
|
602
609
|
|
|
@@ -604,6 +611,7 @@ class LibModelEdge(s_stormtypes.Lib):
|
|
|
604
611
|
return await self.runt.snap.core.getHiveKey(path) or {}
|
|
605
612
|
|
|
606
613
|
async def _methEdgeSet(self, verb, key, valu):
|
|
614
|
+
s_common.deprecated('model.edge.set', curv='2.165.0')
|
|
607
615
|
verb = await s_stormtypes.tostr(verb)
|
|
608
616
|
await self._chkEdgeVerbInView(verb)
|
|
609
617
|
|
|
@@ -619,6 +627,7 @@ class LibModelEdge(s_stormtypes.Lib):
|
|
|
619
627
|
await self.runt.snap.core.setHiveKey(path, kvdict)
|
|
620
628
|
|
|
621
629
|
async def _methEdgeDel(self, verb, key):
|
|
630
|
+
s_common.deprecated('model.edge.del', curv='2.165.0')
|
|
622
631
|
verb = await s_stormtypes.tostr(verb)
|
|
623
632
|
await self._chkEdgeVerbInView(verb)
|
|
624
633
|
|
|
@@ -637,6 +646,7 @@ class LibModelEdge(s_stormtypes.Lib):
|
|
|
637
646
|
|
|
638
647
|
@s_stormtypes.stormfunc(readonly=True)
|
|
639
648
|
async def _methEdgeList(self):
|
|
649
|
+
s_common.deprecated('model.edge.list', curv='2.165.0')
|
|
640
650
|
retn = []
|
|
641
651
|
async for verb in self.runt.snap.view.getEdgeVerbs():
|
|
642
652
|
path = self.hivepath + (verb, 'extprops')
|