synapse 2.153.0__py311-none-any.whl → 2.154.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/cortex.py +4 -4
- synapse/lib/ast.py +10 -5
- synapse/lib/autodoc.py +2 -2
- synapse/lib/cache.py +16 -1
- synapse/lib/layer.py +2 -1
- synapse/lib/modelrev.py +36 -3
- synapse/lib/node.py +2 -5
- synapse/lib/snap.py +10 -0
- synapse/lib/storm.py +80 -0
- synapse/lib/stormhttp.py +3 -0
- synapse/lib/stormlib/backup.py +1 -0
- synapse/lib/stormlib/basex.py +2 -0
- synapse/lib/stormlib/cell.py +7 -0
- synapse/lib/stormlib/compression.py +3 -0
- synapse/lib/stormlib/ethereum.py +1 -0
- synapse/lib/stormlib/graph.py +2 -0
- synapse/lib/stormlib/hashes.py +5 -0
- synapse/lib/stormlib/hex.py +6 -0
- synapse/lib/stormlib/infosec.py +4 -0
- synapse/lib/stormlib/ipv6.py +1 -0
- synapse/lib/stormlib/iters.py +2 -0
- synapse/lib/stormlib/json.py +5 -0
- synapse/lib/stormlib/mime.py +1 -0
- synapse/lib/stormlib/model.py +19 -3
- synapse/lib/stormlib/modelext.py +1 -0
- synapse/lib/stormlib/notifications.py +2 -0
- synapse/lib/stormlib/pack.py +2 -0
- synapse/lib/stormlib/random.py +1 -0
- synapse/lib/stormlib/smtp.py +0 -7
- synapse/lib/stormlib/stats.py +4 -0
- synapse/lib/stormlib/stix.py +8 -0
- synapse/lib/stormlib/storm.py +1 -0
- synapse/lib/stormlib/version.py +3 -0
- synapse/lib/stormlib/xml.py +3 -0
- synapse/lib/stormlib/yaml.py +2 -0
- synapse/lib/stormtypes.py +201 -29
- synapse/lib/trigger.py +180 -4
- synapse/lib/types.py +1 -1
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +55 -6
- synapse/models/inet.py +16 -4
- synapse/models/orgs.py +47 -2
- synapse/models/risk.py +126 -2
- synapse/models/syn.py +6 -0
- synapse/tests/files/stormpkg/badapidef.yaml +13 -0
- synapse/tests/files/stormpkg/storm/modules/apimod +10 -0
- synapse/tests/files/stormpkg/testpkg.yaml +23 -0
- synapse/tests/test_cortex.py +50 -34
- synapse/tests/test_lib_ast.py +7 -0
- synapse/tests/test_lib_autodoc.py +1 -1
- synapse/tests/test_lib_modelrev.py +9 -0
- synapse/tests/test_lib_node.py +55 -0
- synapse/tests/test_lib_storm.py +13 -0
- synapse/tests/test_lib_stormlib_storm.py +8 -0
- synapse/tests/test_lib_stormsvc.py +24 -1
- synapse/tests/test_lib_stormtypes.py +105 -1
- synapse/tests/test_lib_trigger.py +315 -0
- synapse/tests/test_lib_view.py +1 -2
- synapse/tests/test_model_inet.py +22 -0
- synapse/tests/test_model_orgs.py +28 -0
- synapse/tests/test_model_risk.py +73 -0
- synapse/tests/test_tools_autodoc.py +25 -0
- synapse/tests/test_tools_genpkg.py +9 -3
- synapse/tools/autodoc.py +42 -2
- {synapse-2.153.0.dist-info → synapse-2.154.0.dist-info}/METADATA +1 -1
- {synapse-2.153.0.dist-info → synapse-2.154.0.dist-info}/RECORD +69 -67
- {synapse-2.153.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
- {synapse-2.153.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
- {synapse-2.153.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
synapse/lib/trigger.py
CHANGED
|
@@ -21,6 +21,8 @@ Conditions = set((
|
|
|
21
21
|
'node:add',
|
|
22
22
|
'node:del',
|
|
23
23
|
'prop:set',
|
|
24
|
+
'edge:add',
|
|
25
|
+
'edge:del'
|
|
24
26
|
))
|
|
25
27
|
|
|
26
28
|
RecursionDepth = contextvars.ContextVar('RecursionDepth', default=0)
|
|
@@ -37,11 +39,13 @@ TrigSchema = {
|
|
|
37
39
|
'user': {'type': 'string', 'pattern': s_config.re_iden},
|
|
38
40
|
'view': {'type': 'string', 'pattern': s_config.re_iden},
|
|
39
41
|
'form': {'type': 'string', 'pattern': _formre},
|
|
42
|
+
'n2form': {'type': 'string', 'pattern': _formre},
|
|
40
43
|
'tag': {'type': 'string', 'pattern': _tagre},
|
|
41
44
|
'prop': {'type': 'string', 'pattern': _propre},
|
|
45
|
+
'verb': {'type': 'string', },
|
|
42
46
|
'name': {'type': 'string', },
|
|
43
47
|
'doc': {'type': 'string', },
|
|
44
|
-
'cond': {'enum': ['node:add', 'node:del', 'tag:add', 'tag:del', 'prop:set']},
|
|
48
|
+
'cond': {'enum': ['node:add', 'node:del', 'tag:add', 'tag:del', 'prop:set', 'edge:add', 'edge:del']},
|
|
45
49
|
'storm': {'type': 'string'},
|
|
46
50
|
'async': {'type': 'boolean'},
|
|
47
51
|
'enabled': {'type': 'boolean'},
|
|
@@ -69,6 +73,14 @@ TrigSchema = {
|
|
|
69
73
|
'if': {'properties': {'cond': {'const': 'prop:set'}}},
|
|
70
74
|
'then': {'required': ['prop']},
|
|
71
75
|
},
|
|
76
|
+
{
|
|
77
|
+
'if': {'properties': {'cond': {'const': 'edge:add'}}},
|
|
78
|
+
'then': {'required': ['verb']},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
'if': {'properties': {'cond': {'const': 'edge:del'}}},
|
|
82
|
+
'then': {'required': ['verb']},
|
|
83
|
+
},
|
|
72
84
|
],
|
|
73
85
|
}
|
|
74
86
|
TrigSchemaValidator = s_config.getJsValidator(TrigSchema)
|
|
@@ -89,9 +101,9 @@ class Triggers:
|
|
|
89
101
|
self.view = view
|
|
90
102
|
self.triggers = {}
|
|
91
103
|
|
|
92
|
-
self.tagadd = collections.defaultdict(list) # (form, tag): [
|
|
93
|
-
self.tagset = collections.defaultdict(list) # (form, tag): [
|
|
94
|
-
self.tagdel = collections.defaultdict(list) # (form, tag): [
|
|
104
|
+
self.tagadd = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
|
|
105
|
+
self.tagset = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
|
|
106
|
+
self.tagdel = collections.defaultdict(list) # (form, tag): [ Trigger ... ]
|
|
95
107
|
|
|
96
108
|
self.tagaddglobs = collections.defaultdict(s_cache.TagGlobs) # form: TagGlobs
|
|
97
109
|
self.tagsetglobs = collections.defaultdict(s_cache.TagGlobs) # form: TagGlobs
|
|
@@ -101,6 +113,15 @@ class Triggers:
|
|
|
101
113
|
self.nodedel = collections.defaultdict(list) # form: [ Trigger ... ]
|
|
102
114
|
self.propset = collections.defaultdict(list) # prop: [ Trigger ... ]
|
|
103
115
|
|
|
116
|
+
self.edgeadd = collections.defaultdict(list) # (n1form, verb, n2form: [ Trigger ... ]
|
|
117
|
+
self.edgedel = collections.defaultdict(list) # (n1form, verb, n2form: [ Trigger ... ]
|
|
118
|
+
|
|
119
|
+
self.edgeaddglobs = collections.defaultdict(s_cache.EdgeGlobs) # (n1form, n2form: [ EdgeGlobs ... ]
|
|
120
|
+
self.edgedelglobs = collections.defaultdict(s_cache.EdgeGlobs) # (n1form, n2form: [ EdgeGlobs ... ]
|
|
121
|
+
|
|
122
|
+
self.edgeaddcache = s_cache.LruDict()
|
|
123
|
+
self.edgedelcache = s_cache.LruDict()
|
|
124
|
+
|
|
104
125
|
@contextlib.contextmanager
|
|
105
126
|
def _recursion_check(self):
|
|
106
127
|
|
|
@@ -182,6 +203,106 @@ class Triggers:
|
|
|
182
203
|
for _, trig in globs.get(tag):
|
|
183
204
|
await trig.execute(node, vars=vars, view=view)
|
|
184
205
|
|
|
206
|
+
async def runEdgeAdd(self, n1, verb, n2, view=None):
|
|
207
|
+
n1form = n1.form.name if n1 else None
|
|
208
|
+
n2form = n2.form.name if n2 else None
|
|
209
|
+
n2iden = n2.iden() if n2 else None
|
|
210
|
+
varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
|
|
211
|
+
with self._recursion_check():
|
|
212
|
+
cachekey = (n1form, verb, n2form)
|
|
213
|
+
cached = self.edgeaddcache.get(cachekey)
|
|
214
|
+
if cached is None:
|
|
215
|
+
cached = []
|
|
216
|
+
for trig in self.edgeadd.get((None, verb, None), ()):
|
|
217
|
+
cached.append(trig)
|
|
218
|
+
|
|
219
|
+
globs = self.edgeaddglobs.get((None, None))
|
|
220
|
+
if globs:
|
|
221
|
+
for _, trig in globs.get(verb):
|
|
222
|
+
cached.append(trig)
|
|
223
|
+
|
|
224
|
+
if n1:
|
|
225
|
+
for trig in self.edgeadd.get((n1form, verb, None), ()):
|
|
226
|
+
cached.append(trig)
|
|
227
|
+
|
|
228
|
+
globs = self.edgeaddglobs.get((n1form, None))
|
|
229
|
+
if globs:
|
|
230
|
+
for _, trig in globs.get(verb):
|
|
231
|
+
cached.append(trig)
|
|
232
|
+
|
|
233
|
+
if n2:
|
|
234
|
+
for trig in self.edgeadd.get((None, verb, n2form), ()):
|
|
235
|
+
cached.append(trig)
|
|
236
|
+
|
|
237
|
+
globs = self.edgeaddglobs.get((None, n2form))
|
|
238
|
+
if globs:
|
|
239
|
+
for _, trig in globs.get(verb):
|
|
240
|
+
cached.append(trig)
|
|
241
|
+
|
|
242
|
+
if n1 and n2:
|
|
243
|
+
for trig in self.edgeadd.get((n1form, verb, n2form), ()):
|
|
244
|
+
cached.append(trig)
|
|
245
|
+
|
|
246
|
+
globs = self.edgeaddglobs.get((n1form, n2form))
|
|
247
|
+
if globs:
|
|
248
|
+
for _, trig in globs.get(verb):
|
|
249
|
+
cached.append(trig)
|
|
250
|
+
|
|
251
|
+
self.edgeaddcache[cachekey] = cached
|
|
252
|
+
|
|
253
|
+
for trig in cached:
|
|
254
|
+
await trig.execute(n1, vars=varz, view=view)
|
|
255
|
+
|
|
256
|
+
async def runEdgeDel(self, n1, verb, n2, view=None):
|
|
257
|
+
n1form = n1.form.name if n1 else None
|
|
258
|
+
n2form = n2.form.name if n2 else None
|
|
259
|
+
n2iden = n2.iden() if n2 else None
|
|
260
|
+
varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
|
|
261
|
+
with self._recursion_check():
|
|
262
|
+
cachekey = (n1form, verb, n2form)
|
|
263
|
+
cached = self.edgedelcache.get(cachekey)
|
|
264
|
+
if cached is None:
|
|
265
|
+
cached = []
|
|
266
|
+
for trig in self.edgedel.get((None, verb, None), ()):
|
|
267
|
+
cached.append(trig)
|
|
268
|
+
|
|
269
|
+
globs = self.edgedelglobs.get((None, None))
|
|
270
|
+
if globs:
|
|
271
|
+
for _, trig in globs.get(verb):
|
|
272
|
+
cached.append(trig)
|
|
273
|
+
|
|
274
|
+
if n1:
|
|
275
|
+
for trig in self.edgedel.get((n1form, verb, None), ()):
|
|
276
|
+
cached.append(trig)
|
|
277
|
+
|
|
278
|
+
globs = self.edgedelglobs.get((n1form, None))
|
|
279
|
+
if globs:
|
|
280
|
+
for _, trig in globs.get(verb):
|
|
281
|
+
cached.append(trig)
|
|
282
|
+
|
|
283
|
+
if n2:
|
|
284
|
+
for trig in self.edgedel.get((None, verb, n2form), ()):
|
|
285
|
+
cached.append(trig)
|
|
286
|
+
|
|
287
|
+
globs = self.edgedelglobs.get((None, n2form))
|
|
288
|
+
if globs:
|
|
289
|
+
for _, trig in globs.get(verb):
|
|
290
|
+
cached.append(trig)
|
|
291
|
+
|
|
292
|
+
if n1 and n2:
|
|
293
|
+
for trig in self.edgedel.get((n1form, verb, n2form), ()):
|
|
294
|
+
cached.append(trig)
|
|
295
|
+
|
|
296
|
+
globs = self.edgedelglobs.get((n1form, n2form))
|
|
297
|
+
if globs:
|
|
298
|
+
for _, trig in globs.get(verb):
|
|
299
|
+
cached.append(trig)
|
|
300
|
+
|
|
301
|
+
self.edgedelcache[cachekey] = cached
|
|
302
|
+
|
|
303
|
+
for trig in cached:
|
|
304
|
+
await trig.execute(n1, vars=varz, view=view)
|
|
305
|
+
|
|
185
306
|
async def load(self, tdef):
|
|
186
307
|
|
|
187
308
|
trig = Trigger(self.view, tdef)
|
|
@@ -194,6 +315,8 @@ class Triggers:
|
|
|
194
315
|
tag = trig.tdef.get('tag')
|
|
195
316
|
form = trig.tdef.get('form')
|
|
196
317
|
prop = trig.tdef.get('prop')
|
|
318
|
+
verb = trig.tdef.get('verb')
|
|
319
|
+
n2form = trig.tdef.get('n2form')
|
|
197
320
|
|
|
198
321
|
if cond not in Conditions:
|
|
199
322
|
raise s_exc.NoSuchCond(name=cond)
|
|
@@ -206,6 +329,9 @@ class Triggers:
|
|
|
206
329
|
if tag is None:
|
|
207
330
|
raise s_exc.BadOptValu(mesg='missing tag')
|
|
208
331
|
s_chop.validateTagMatch(tag)
|
|
332
|
+
if cond in ('edge:add', 'edge:del') and verb is None:
|
|
333
|
+
raise s_exc.BadOptValu(mesg='verb must be present for edge:add or edge:del')
|
|
334
|
+
|
|
209
335
|
if prop is not None and cond != 'prop:set':
|
|
210
336
|
raise s_exc.BadOptValu(mesg='prop parameter invalid')
|
|
211
337
|
|
|
@@ -237,6 +363,20 @@ class Triggers:
|
|
|
237
363
|
else:
|
|
238
364
|
self.tagdelglobs[form].add(tag, trig)
|
|
239
365
|
|
|
366
|
+
elif cond == 'edge:add':
|
|
367
|
+
self.edgeaddcache.clear()
|
|
368
|
+
if '*' not in verb:
|
|
369
|
+
self.edgeadd[(form, verb, n2form)].append(trig)
|
|
370
|
+
else:
|
|
371
|
+
self.edgeaddglobs[(form, n2form)].add(verb, trig)
|
|
372
|
+
|
|
373
|
+
elif cond == 'edge:del':
|
|
374
|
+
self.edgedelcache.clear()
|
|
375
|
+
if '*' not in verb:
|
|
376
|
+
self.edgedel[(form, verb, n2form)].append(trig)
|
|
377
|
+
else:
|
|
378
|
+
self.edgedelglobs[(form, n2form)].add(verb, trig)
|
|
379
|
+
|
|
240
380
|
self.triggers[trig.iden] = trig
|
|
241
381
|
return trig
|
|
242
382
|
|
|
@@ -292,6 +432,34 @@ class Triggers:
|
|
|
292
432
|
globs.rem(tag, trig)
|
|
293
433
|
return trig
|
|
294
434
|
|
|
435
|
+
if cond == 'edge:add':
|
|
436
|
+
verb = trig.tdef['verb']
|
|
437
|
+
form = trig.tdef.get('form')
|
|
438
|
+
n2form = trig.tdef.get('n2form')
|
|
439
|
+
|
|
440
|
+
self.edgeaddcache.clear()
|
|
441
|
+
if '*' not in verb:
|
|
442
|
+
self.edgeadd[(form, verb, n2form)].remove(trig)
|
|
443
|
+
return trig
|
|
444
|
+
|
|
445
|
+
globs = self.edgeaddglobs.get((form, n2form))
|
|
446
|
+
globs.rem(verb, trig)
|
|
447
|
+
return trig
|
|
448
|
+
|
|
449
|
+
if cond == 'edge:del':
|
|
450
|
+
verb = trig.tdef['verb']
|
|
451
|
+
form = trig.tdef.get('form')
|
|
452
|
+
n2form = trig.tdef.get('n2form')
|
|
453
|
+
|
|
454
|
+
self.edgedelcache.clear()
|
|
455
|
+
if '*' not in verb:
|
|
456
|
+
self.edgedel[(form, verb, n2form)].remove(trig)
|
|
457
|
+
return trig
|
|
458
|
+
|
|
459
|
+
globs = self.edgedelglobs.get((form, n2form))
|
|
460
|
+
globs.rem(verb, trig)
|
|
461
|
+
return trig
|
|
462
|
+
|
|
295
463
|
raise AssertionError('trigger has invalid condition')
|
|
296
464
|
|
|
297
465
|
def get(self, iden):
|
|
@@ -440,6 +608,14 @@ class Trigger:
|
|
|
440
608
|
if prop is not None:
|
|
441
609
|
props['prop'] = prop
|
|
442
610
|
|
|
611
|
+
verb = self.tdef.get('verb')
|
|
612
|
+
if verb is not None:
|
|
613
|
+
props['verb'] = verb
|
|
614
|
+
|
|
615
|
+
n2form = self.tdef.get('n2form')
|
|
616
|
+
if n2form is not None:
|
|
617
|
+
props['n2form'] = n2form
|
|
618
|
+
|
|
443
619
|
pnorms = {}
|
|
444
620
|
for prop, valu in props.items():
|
|
445
621
|
formprop = form.props.get(prop)
|
synapse/lib/types.py
CHANGED
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, 154, 0)
|
|
227
227
|
verstring = '.'.join([str(x) for x in version])
|
|
228
|
-
commit = '
|
|
228
|
+
commit = '6c9600992a0ca2563239bc86b6631bd599707e3f'
|
synapse/lib/view.py
CHANGED
|
@@ -746,6 +746,7 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
746
746
|
|
|
747
747
|
await self.info.set('layers', [lyr.iden for lyr in self.layers])
|
|
748
748
|
await self.core.feedBeholder('view:addlayer', {'iden': self.iden, 'layer': layriden, 'indx': indx}, gates=[self.iden, layriden])
|
|
749
|
+
self.core._calcViewsByLayer()
|
|
749
750
|
|
|
750
751
|
@s_nexus.Pusher.onPushAuto('view:setlayers')
|
|
751
752
|
async def setLayers(self, layers):
|
|
@@ -753,14 +754,11 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
753
754
|
Set the view layers from a list of idens.
|
|
754
755
|
NOTE: view layers are stored "top down" (the write layer is self.layers[0])
|
|
755
756
|
'''
|
|
756
|
-
|
|
757
|
-
if view.parent is self:
|
|
758
|
-
raise s_exc.ReadOnlyLayer(mesg='May not change layers that have been forked from')
|
|
757
|
+
layrs = []
|
|
759
758
|
|
|
760
759
|
if self.parent is not None:
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
layrs = []
|
|
760
|
+
mesg = 'You cannot set the layers of a forked view.'
|
|
761
|
+
raise s_exc.BadArg(mesg=mesg)
|
|
764
762
|
|
|
765
763
|
for iden in layers:
|
|
766
764
|
layr = self.core.layers.get(iden)
|
|
@@ -777,6 +775,39 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
777
775
|
await self.info.set('layers', layers)
|
|
778
776
|
await self.core.feedBeholder('view:setlayers', {'iden': self.iden, 'layers': layers}, gates=[self.iden, layers[0]])
|
|
779
777
|
|
|
778
|
+
await self._calcChildViews()
|
|
779
|
+
self.core._calcViewsByLayer()
|
|
780
|
+
|
|
781
|
+
async def _calcChildViews(self):
|
|
782
|
+
|
|
783
|
+
todo = collections.deque([self])
|
|
784
|
+
|
|
785
|
+
byparent = collections.defaultdict(list)
|
|
786
|
+
for view in self.core.views.values():
|
|
787
|
+
if view.parent is None:
|
|
788
|
+
continue
|
|
789
|
+
|
|
790
|
+
byparent[view.parent].append(view)
|
|
791
|
+
|
|
792
|
+
while todo:
|
|
793
|
+
|
|
794
|
+
view = todo.pop()
|
|
795
|
+
|
|
796
|
+
for child in byparent.get(view, ()):
|
|
797
|
+
|
|
798
|
+
layers = [child.layers[0]]
|
|
799
|
+
layers.extend(view.layers)
|
|
800
|
+
|
|
801
|
+
child.layers = layers
|
|
802
|
+
|
|
803
|
+
# convert layers to a list of idens...
|
|
804
|
+
lids = [layr.iden for layr in layers]
|
|
805
|
+
await child.info.set('layers', lids)
|
|
806
|
+
|
|
807
|
+
await self.core.feedBeholder('view:setlayers', {'iden': child.iden, 'layers': lids}, gates=[child.iden, lids[0]])
|
|
808
|
+
|
|
809
|
+
todo.append(child)
|
|
810
|
+
|
|
780
811
|
async def fork(self, ldef=None, vdef=None):
|
|
781
812
|
'''
|
|
782
813
|
Make a new view inheriting from this view with the same layers and a new write layer on top
|
|
@@ -961,6 +992,24 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
961
992
|
|
|
962
993
|
await self.triggers.runPropSet(node, prop, oldv, view=view)
|
|
963
994
|
|
|
995
|
+
async def runEdgeAdd(self, n1, edge, n2, view=None):
|
|
996
|
+
if not n1.snap.trigson:
|
|
997
|
+
return
|
|
998
|
+
|
|
999
|
+
if view is None:
|
|
1000
|
+
view = self.iden
|
|
1001
|
+
|
|
1002
|
+
await self.triggers.runEdgeAdd(n1, edge, n2, view=view)
|
|
1003
|
+
|
|
1004
|
+
async def runEdgeDel(self, n1, edge, n2, view=None):
|
|
1005
|
+
if not n1.snap.trigson:
|
|
1006
|
+
return
|
|
1007
|
+
|
|
1008
|
+
if view is None:
|
|
1009
|
+
view = self.iden
|
|
1010
|
+
|
|
1011
|
+
await self.triggers.runEdgeDel(n1, edge, n2, view=view)
|
|
1012
|
+
|
|
964
1013
|
async def addTrigger(self, tdef):
|
|
965
1014
|
'''
|
|
966
1015
|
Adds a trigger to the view.
|
synapse/models/inet.py
CHANGED
|
@@ -28,6 +28,8 @@ udots = regex.compile(r'[\u3002\uff0e\uff61]')
|
|
|
28
28
|
cidrmasks = [((0xffffffff - (2 ** (32 - i) - 1)), (2 ** (32 - i))) for i in range(33)]
|
|
29
29
|
ipv4max = 2 ** 32 - 1
|
|
30
30
|
|
|
31
|
+
rfc6598 = ipaddress.IPv4Network('100.64.0.0/10')
|
|
32
|
+
|
|
31
33
|
def getAddrType(ip):
|
|
32
34
|
|
|
33
35
|
if ip.is_multicast:
|
|
@@ -45,6 +47,9 @@ def getAddrType(ip):
|
|
|
45
47
|
if ip.is_reserved:
|
|
46
48
|
return 'reserved'
|
|
47
49
|
|
|
50
|
+
if ip in rfc6598:
|
|
51
|
+
return 'shared'
|
|
52
|
+
|
|
48
53
|
return 'unicast'
|
|
49
54
|
|
|
50
55
|
class Addr(s_types.Str):
|
|
@@ -924,6 +929,11 @@ class Url(s_types.Str):
|
|
|
924
929
|
except Exception:
|
|
925
930
|
pass
|
|
926
931
|
|
|
932
|
+
# allow MSFT specific wild card syntax
|
|
933
|
+
# https://learn.microsoft.com/en-us/windows/win32/http/urlprefix-strings
|
|
934
|
+
if host is None and part == '+':
|
|
935
|
+
host = '+'
|
|
936
|
+
|
|
927
937
|
if host and local:
|
|
928
938
|
raise s_exc.BadTypeValu(valu=orig, name=self.name,
|
|
929
939
|
mesg='Host specified on local-only file URI') from None
|
|
@@ -950,10 +960,12 @@ class Url(s_types.Str):
|
|
|
950
960
|
if port is not None:
|
|
951
961
|
hostparts = f'{hostparts}:{port}'
|
|
952
962
|
|
|
953
|
-
if proto != 'file':
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
963
|
+
if proto != 'file' and host is None:
|
|
964
|
+
raise s_exc.BadTypeValu(valu=orig, name=self.name, mesg='Missing address/url')
|
|
965
|
+
|
|
966
|
+
if not hostparts and not pathpart:
|
|
967
|
+
raise s_exc.BadTypeValu(valu=orig, name=self.name,
|
|
968
|
+
mesg='Missing address/url') from None
|
|
957
969
|
|
|
958
970
|
base = f'{proto}://{hostparts}{pathpart}'
|
|
959
971
|
subs['base'] = base
|
synapse/models/orgs.py
CHANGED
|
@@ -202,6 +202,8 @@ class OuModule(s_module.CoreModule):
|
|
|
202
202
|
('ou:jobtitle', ('str', {'lower': True, 'onespace': True}), {
|
|
203
203
|
'doc': 'A title for a position within an org.',
|
|
204
204
|
}),
|
|
205
|
+
('ou:requirement', ('guid', {}), {
|
|
206
|
+
'doc': 'A specific requirement.'}),
|
|
205
207
|
),
|
|
206
208
|
'edges': (
|
|
207
209
|
(('ou:campaign', 'uses', 'ou:technique'), {
|
|
@@ -219,6 +221,8 @@ class OuModule(s_module.CoreModule):
|
|
|
219
221
|
'doc': 'The campaign made use of the target node.'}),
|
|
220
222
|
(('ou:contribution', 'includes', None), {
|
|
221
223
|
'doc': 'The contribution includes the specific node.'}),
|
|
224
|
+
((None, 'meets', 'ou:requirement'), {
|
|
225
|
+
'doc': 'The requirement is met by the source node.'}),
|
|
222
226
|
),
|
|
223
227
|
'forms': (
|
|
224
228
|
('ou:jobtype', {}, ()),
|
|
@@ -634,8 +638,10 @@ class OuModule(s_module.CoreModule):
|
|
|
634
638
|
'doc': 'Deprecated for scalability. Please use -(uses)> ou:technique.',
|
|
635
639
|
}),
|
|
636
640
|
('goals', ('array', {'type': 'ou:goal', 'sorted': True, 'uniq': True}), {
|
|
637
|
-
'doc': 'The assessed goals of the organization.'
|
|
638
|
-
|
|
641
|
+
'doc': 'The assessed goals of the organization.'}),
|
|
642
|
+
|
|
643
|
+
('tag', ('syn:tag', {}), {
|
|
644
|
+
'doc': 'A base tag used to encode assessments made by the organization.'}),
|
|
639
645
|
)),
|
|
640
646
|
('ou:team', {}, (
|
|
641
647
|
('org', ('ou:org', {}), {}),
|
|
@@ -1123,6 +1129,45 @@ class OuModule(s_module.CoreModule):
|
|
|
1123
1129
|
}),
|
|
1124
1130
|
# TODO duration ('duration'
|
|
1125
1131
|
)),
|
|
1132
|
+
('ou:requirement', {}, (
|
|
1133
|
+
|
|
1134
|
+
('name', ('str', {'lower': True, 'onespace': True}), {
|
|
1135
|
+
'doc': 'A name for the requirement.'}),
|
|
1136
|
+
|
|
1137
|
+
('text', ('str', {}), {
|
|
1138
|
+
'disp': {'hint': 'text'},
|
|
1139
|
+
'doc': 'The text of the stated requirement.'}),
|
|
1140
|
+
|
|
1141
|
+
('optional', ('bool', {}), {
|
|
1142
|
+
'doc': 'Set to true if the requirement is optional.'}),
|
|
1143
|
+
|
|
1144
|
+
('priority', ('meta:priority', {}), {
|
|
1145
|
+
'doc': 'The priority of the requirement.'}),
|
|
1146
|
+
|
|
1147
|
+
('goal', ('ou:goal', {}), {
|
|
1148
|
+
'doc': 'The goal that the requirement is designed to achieve.'}),
|
|
1149
|
+
|
|
1150
|
+
('active', ('bool', {}), {
|
|
1151
|
+
'doc': 'Set to true if the requirement is currently active.'}),
|
|
1152
|
+
|
|
1153
|
+
('issued', ('time', {}), {
|
|
1154
|
+
'doc': 'The time that the requirement was first issued.'}),
|
|
1155
|
+
|
|
1156
|
+
('period', ('ival', {}), {
|
|
1157
|
+
'doc': 'The time window where the goal must be met. Can be ongoing.'}),
|
|
1158
|
+
|
|
1159
|
+
('issuer', ('ps:contact', {}), {
|
|
1160
|
+
'doc': 'The contact information of the entity which issued the requirement.'}),
|
|
1161
|
+
|
|
1162
|
+
('assignee', ('ps:contact', {}), {
|
|
1163
|
+
'doc': 'The contact information of the entity which is assigned to meet the requirement.'}),
|
|
1164
|
+
|
|
1165
|
+
('deps', ('array', {'type': 'ou:requirement', 'sorted': True, 'uniq': True}), {
|
|
1166
|
+
'doc': 'A list of sub-requirements which must be met to complete the requirement.'}),
|
|
1167
|
+
|
|
1168
|
+
('deps:min', ('int', {'min': 0}), {
|
|
1169
|
+
'doc': 'The minimum number dependant requirements which must be met. If unset, assume all must be met.'}),
|
|
1170
|
+
)),
|
|
1126
1171
|
)
|
|
1127
1172
|
}
|
|
1128
1173
|
|
synapse/models/risk.py
CHANGED
|
@@ -99,9 +99,22 @@ class RiskModule(s_module.CoreModule):
|
|
|
99
99
|
}),
|
|
100
100
|
|
|
101
101
|
('risk:threat:type:taxonomy', ('taxonomy', {}), {
|
|
102
|
-
'doc': 'A taxonomy of threat types.',
|
|
103
102
|
'interfaces': ('taxonomy',),
|
|
104
|
-
|
|
103
|
+
'doc': 'A taxonomy of threat types.'}),
|
|
104
|
+
|
|
105
|
+
('risk:leak', ('guid', {}), {
|
|
106
|
+
'doc': 'An event where information was disclosed without permission.'}),
|
|
107
|
+
|
|
108
|
+
('risk:leak:type:taxonomy', ('taxonomy', {}), {
|
|
109
|
+
'interfaces': ('taxonomy',),
|
|
110
|
+
'doc': 'A taxonomy of leak event types.'}),
|
|
111
|
+
|
|
112
|
+
('risk:extortion', ('guid', {}), {
|
|
113
|
+
'doc': 'An event where an attacker attempted to extort a victim.'}),
|
|
114
|
+
|
|
115
|
+
('risk:extortion:type:taxonomy', ('taxonomy', {}), {
|
|
116
|
+
'interfaces': ('taxonomy',),
|
|
117
|
+
'doc': 'A taxonomy of extortion event types.'}),
|
|
105
118
|
),
|
|
106
119
|
'edges': (
|
|
107
120
|
# some explicit examples...
|
|
@@ -141,6 +154,12 @@ class RiskModule(s_module.CoreModule):
|
|
|
141
154
|
'doc': 'The target node was stolen or copied as a result of the compromise.'}),
|
|
142
155
|
(('risk:mitigation', 'addresses', 'ou:technique'), {
|
|
143
156
|
'doc': 'The mitigation addresses the technique.'}),
|
|
157
|
+
|
|
158
|
+
(('risk:leak', 'leaked', None), {
|
|
159
|
+
'doc': 'The leak included the disclosure of the target node.'}),
|
|
160
|
+
|
|
161
|
+
(('risk:extortion', 'leveraged', None), {
|
|
162
|
+
'doc': 'The extortion event was based on attacker access to the target node.'}),
|
|
144
163
|
),
|
|
145
164
|
'forms': (
|
|
146
165
|
|
|
@@ -596,6 +615,9 @@ class RiskModule(s_module.CoreModule):
|
|
|
596
615
|
|
|
597
616
|
('ext:id', ('str', {}), {
|
|
598
617
|
'doc': 'An external identifier for the alert.'}),
|
|
618
|
+
|
|
619
|
+
('host', ('it:host', {}), {
|
|
620
|
+
'doc': 'The host which generated the alert.'}),
|
|
599
621
|
)),
|
|
600
622
|
('risk:compromisetype', {}, ()),
|
|
601
623
|
('risk:compromise', {}, (
|
|
@@ -612,6 +634,12 @@ class RiskModule(s_module.CoreModule):
|
|
|
612
634
|
('reporter:name', ('ou:name', {}), {
|
|
613
635
|
'doc': 'The name of the organization reporting on the compromise.'}),
|
|
614
636
|
|
|
637
|
+
('ext:id', ('str', {}), {
|
|
638
|
+
'doc': 'An external unique ID for the compromise.'}),
|
|
639
|
+
|
|
640
|
+
('url', ('inet:url', {}), {
|
|
641
|
+
'doc': 'A URL which documents the compromise.'}),
|
|
642
|
+
|
|
615
643
|
('type', ('risk:compromisetype', {}), {
|
|
616
644
|
'ex': 'cno.breach',
|
|
617
645
|
'doc': 'A type for the compromise, as a taxonomy entry.'}),
|
|
@@ -815,6 +843,102 @@ class RiskModule(s_module.CoreModule):
|
|
|
815
843
|
'doc': 'An external unique ID for the attack.'}),
|
|
816
844
|
|
|
817
845
|
)),
|
|
846
|
+
|
|
847
|
+
('risk:leak:type:taxonomy', {}, ()),
|
|
848
|
+
('risk:leak', {}, (
|
|
849
|
+
|
|
850
|
+
('name', ('str', {'lower': True, 'onespace': True}), {
|
|
851
|
+
'doc': 'A simple name for the leak event.'}),
|
|
852
|
+
|
|
853
|
+
('desc', ('str', {}), {
|
|
854
|
+
'disp': {'hint': 'text'},
|
|
855
|
+
'doc': 'A description of the leak event.'}),
|
|
856
|
+
|
|
857
|
+
('reporter', ('ou:org', {}), {
|
|
858
|
+
'doc': 'The organization reporting on the leak event.'}),
|
|
859
|
+
|
|
860
|
+
('reporter:name', ('ou:name', {}), {
|
|
861
|
+
'doc': 'The name of the organization reporting on the leak event.'}),
|
|
862
|
+
|
|
863
|
+
('disclosed', ('time', {}), {
|
|
864
|
+
'doc': 'The time the leaked information was disclosed.'}),
|
|
865
|
+
|
|
866
|
+
('owner', ('ps:contact', {}), {
|
|
867
|
+
'doc': 'The owner of the leaked information.'}),
|
|
868
|
+
|
|
869
|
+
('leaker', ('ps:contact', {}), {
|
|
870
|
+
'doc': 'The identity which leaked the information.'}),
|
|
871
|
+
|
|
872
|
+
('type', ('risk:leak:type:taxonomy', {}), {
|
|
873
|
+
'doc': 'A type taxonomy for the leak.'}),
|
|
874
|
+
|
|
875
|
+
('goal', ('ou:goal', {}), {
|
|
876
|
+
'doc': 'The goal of the leaker in disclosing the information.'}),
|
|
877
|
+
|
|
878
|
+
('compromise', ('risk:compromise', {}), {
|
|
879
|
+
'doc': 'The compromise which allowed the leaker access to the information.'}),
|
|
880
|
+
|
|
881
|
+
('public', ('bool', {}), {
|
|
882
|
+
'doc': 'Set to true if the leaked information was made publicly available.'}),
|
|
883
|
+
|
|
884
|
+
('public:url', ('inet:url', {}), {
|
|
885
|
+
'doc': 'The URL where the leaked information was made publicly available.'}),
|
|
886
|
+
|
|
887
|
+
)),
|
|
888
|
+
|
|
889
|
+
('risk:extortion:type:taxonomy', {}, ()),
|
|
890
|
+
('risk:extortion', {}, (
|
|
891
|
+
|
|
892
|
+
('name', ('str', {'lower': True, 'onespace': True}), {
|
|
893
|
+
'doc': 'A name for the extortion event.'}),
|
|
894
|
+
|
|
895
|
+
('desc', ('str', {}), {
|
|
896
|
+
'disp': {'hint': 'text'},
|
|
897
|
+
'doc': 'A description of the extortion event.'}),
|
|
898
|
+
|
|
899
|
+
('reporter', ('ou:org', {}), {
|
|
900
|
+
'doc': 'The organization reporting on the extortion event.'}),
|
|
901
|
+
|
|
902
|
+
('reporter:name', ('ou:name', {}), {
|
|
903
|
+
'doc': 'The name of the organization reporting on the extortion event.'}),
|
|
904
|
+
|
|
905
|
+
('demanded', ('time', {}), {
|
|
906
|
+
'doc': 'The time that the attacker made their demands.'}),
|
|
907
|
+
|
|
908
|
+
('goal', ('ou:goal', {}), {
|
|
909
|
+
'doc': 'The goal of the attacker in extorting the victim.'}),
|
|
910
|
+
|
|
911
|
+
('type', ('risk:extortion:type:taxonomy', {}), {
|
|
912
|
+
'doc': 'A type taxonomy for the extortion event.'}),
|
|
913
|
+
|
|
914
|
+
('attacker', ('ps:contact', {}), {
|
|
915
|
+
'doc': 'The extortion attacker identity.'}),
|
|
916
|
+
|
|
917
|
+
('target', ('ps:contact', {}), {
|
|
918
|
+
'doc': 'The extortion target identity.'}),
|
|
919
|
+
|
|
920
|
+
('success', ('bool', {}), {
|
|
921
|
+
'doc': 'Set to true if the victim met the attackers demands.'}),
|
|
922
|
+
|
|
923
|
+
('enacted', ('bool', {}), {
|
|
924
|
+
'doc': 'Set to true if attacker carried out the threat.'}),
|
|
925
|
+
|
|
926
|
+
('public', ('bool', {}), {
|
|
927
|
+
'doc': 'Set to true if the attacker publicly announced the extortion.'}),
|
|
928
|
+
|
|
929
|
+
('public:url', ('inet:url', {}), {
|
|
930
|
+
'doc': 'The URL where the attacker publicly announced the extortion.'}),
|
|
931
|
+
|
|
932
|
+
('compromise', ('risk:compromise', {}), {
|
|
933
|
+
'doc': 'The compromise which allowed the attacker to extort the target.'}),
|
|
934
|
+
|
|
935
|
+
('demanded:payment:price', ('econ:price', {}), {
|
|
936
|
+
'doc': 'The payment price which was demanded.'}),
|
|
937
|
+
|
|
938
|
+
('demanded:payment:currency', ('econ:currency', {}), {
|
|
939
|
+
'doc': 'The currency in which payment was demanded.'}),
|
|
940
|
+
|
|
941
|
+
)),
|
|
818
942
|
),
|
|
819
943
|
}
|
|
820
944
|
name = 'risk'
|
synapse/models/syn.py
CHANGED
|
@@ -236,6 +236,12 @@ class SynModule(s_module.CoreModule):
|
|
|
236
236
|
('form', ('str', {'lower': True, 'strip': True}), {
|
|
237
237
|
'doc': 'Form the trigger is watching for.'
|
|
238
238
|
}),
|
|
239
|
+
('verb', ('str', {'lower': True, 'strip': True}), {
|
|
240
|
+
'doc': 'Edge verb the trigger is watching for.'
|
|
241
|
+
}),
|
|
242
|
+
('n2form', ('str', {'lower': True, 'strip': True}), {
|
|
243
|
+
'doc': 'N2 form the trigger is watching for.'
|
|
244
|
+
}),
|
|
239
245
|
('prop', ('str', {'lower': True, 'strip': True}), {
|
|
240
246
|
'doc': 'Property the trigger is watching for.'
|
|
241
247
|
}),
|