synapse 2.167.0__py311-none-any.whl → 2.169.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 +35 -19
- synapse/lib/agenda.py +3 -0
- synapse/lib/aha.py +2 -1
- synapse/lib/cell.py +6 -14
- synapse/lib/httpapi.py +3 -0
- synapse/lib/layer.py +2 -0
- synapse/lib/modelrev.py +6 -0
- synapse/lib/modules.py +1 -0
- synapse/lib/snap.py +3 -1
- synapse/lib/stormlib/aha.py +21 -6
- synapse/lib/stormlib/easyperm.py +8 -0
- synapse/lib/stormlib/model.py +120 -0
- synapse/lib/stormtypes.py +17 -2
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +8 -10
- synapse/models/planning.py +161 -0
- synapse/tests/test_cortex.py +1 -1
- synapse/tests/test_lib_agenda.py +17 -3
- synapse/tests/test_lib_aha.py +7 -2
- synapse/tests/test_lib_cell.py +21 -52
- synapse/tests/test_lib_stormlib_aha.py +1 -1
- synapse/tests/test_lib_stormlib_cortex.py +12 -0
- synapse/tests/test_lib_stormlib_model.py +105 -0
- synapse/tests/test_lib_stormtypes.py +26 -0
- synapse/tests/test_lib_trigger.py +3 -3
- synapse/tests/test_model_planning.py +126 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/METADATA +1 -1
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/RECORD +31 -29
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/LICENSE +0 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/WHEEL +0 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import synapse.lib.module as s_module
|
|
2
|
+
|
|
3
|
+
class PlanModule(s_module.CoreModule):
|
|
4
|
+
|
|
5
|
+
def getModelDefs(self):
|
|
6
|
+
return (('plan', {
|
|
7
|
+
'types': (
|
|
8
|
+
('plan:system', ('guid', {}), {
|
|
9
|
+
'doc': 'A planning or behavioral analysis system that defines phases and procedures.'}),
|
|
10
|
+
|
|
11
|
+
('plan:phase', ('guid', {}), {
|
|
12
|
+
'doc': 'A phase within a planning system which may be used to group steps within a procedure.'}),
|
|
13
|
+
|
|
14
|
+
('plan:procedure', ('guid', {}), {
|
|
15
|
+
'doc': 'A procedure consisting of steps.'}),
|
|
16
|
+
|
|
17
|
+
('plan:procedure:type:taxonomy', ('taxonomy', {}), {
|
|
18
|
+
'interfaces': ('meta:taxonomy',),
|
|
19
|
+
'doc': 'A taxonomy of procedure types.'}),
|
|
20
|
+
|
|
21
|
+
('plan:procedure:variable', ('guid', {}), {
|
|
22
|
+
'doc': 'A variable used by a procedure.'}),
|
|
23
|
+
|
|
24
|
+
('plan:procedure:step', ('guid', {}), {
|
|
25
|
+
'doc': 'A step within a procedure.'}),
|
|
26
|
+
|
|
27
|
+
('plan:procedure:link', ('guid', {}), {
|
|
28
|
+
'doc': 'A link between steps in a procedure.'}),
|
|
29
|
+
),
|
|
30
|
+
|
|
31
|
+
'forms': (
|
|
32
|
+
('plan:system', {}, (
|
|
33
|
+
|
|
34
|
+
('name', ('str', {'lower': True, 'onespace': True}), {
|
|
35
|
+
'ex': 'mitre att&ck flow',
|
|
36
|
+
'doc': 'The name of the planning system.'}),
|
|
37
|
+
|
|
38
|
+
('summary', ('str', {}), {
|
|
39
|
+
'disp': {'hint': 'text'},
|
|
40
|
+
'doc': 'A summary of the purpose and use case for the planning system.'}),
|
|
41
|
+
|
|
42
|
+
('author', ('ps:contact', {}), {
|
|
43
|
+
'doc': 'The contact of the person or organization which authored the system.'}),
|
|
44
|
+
|
|
45
|
+
('created', ('time', {}), {
|
|
46
|
+
'doc': 'The time the planning system was first created.'}),
|
|
47
|
+
|
|
48
|
+
('updated', ('time', {}), {
|
|
49
|
+
'doc': 'The time the planning system was last updated.'}),
|
|
50
|
+
|
|
51
|
+
('version', ('it:semver', {}), {
|
|
52
|
+
'doc': 'The version of the planning system.'}),
|
|
53
|
+
|
|
54
|
+
('url', ('inet:url', {}), {
|
|
55
|
+
'doc': 'The primary URL which documents the planning system.'}),
|
|
56
|
+
)),
|
|
57
|
+
('plan:phase', {}, (
|
|
58
|
+
('title', ('str', {}), {
|
|
59
|
+
'ex': 'Reconnaissance Phase',
|
|
60
|
+
'doc': 'The title of the phase.'}),
|
|
61
|
+
|
|
62
|
+
('summary', ('str', {}), {
|
|
63
|
+
'disp': {'hint': 'text'},
|
|
64
|
+
'doc': 'A summary of the definition of the phase.'}),
|
|
65
|
+
|
|
66
|
+
('index', ('int', {}), {
|
|
67
|
+
'doc': 'The index of this phase within the phases of the system.'}),
|
|
68
|
+
|
|
69
|
+
('url', ('inet:url', {}), {
|
|
70
|
+
'doc': 'A URL which links to the full documentation about the phase.'}),
|
|
71
|
+
|
|
72
|
+
('system', ('plan:system', {}), {
|
|
73
|
+
'doc': 'The planning system which defines this phase.'}),
|
|
74
|
+
)),
|
|
75
|
+
('plan:procedure:type:taxonomy', {}, ()),
|
|
76
|
+
('plan:procedure', {}, (
|
|
77
|
+
|
|
78
|
+
('title', ('str', {}), {
|
|
79
|
+
'ex': 'Network Reconnaissance Procedure',
|
|
80
|
+
'doc': 'The name of the procedure.'}),
|
|
81
|
+
|
|
82
|
+
('summary', ('str', {}), {
|
|
83
|
+
'disp': {'hint': 'text'},
|
|
84
|
+
'doc': 'A summary of the purpose and use cases for the procedure.'}),
|
|
85
|
+
|
|
86
|
+
('author', ('ps:contact', {}), {
|
|
87
|
+
'doc': 'The contact of the person or organization which authored the procedure.'}),
|
|
88
|
+
|
|
89
|
+
('created', ('time', {}), {
|
|
90
|
+
'doc': 'The time the procedure was created.'}),
|
|
91
|
+
|
|
92
|
+
('updated', ('time', {}), {
|
|
93
|
+
'doc': 'The time the procedure was last updated.'}),
|
|
94
|
+
|
|
95
|
+
('version', ('it:semver', {}), {
|
|
96
|
+
'doc': 'The version of the procedure.'}),
|
|
97
|
+
|
|
98
|
+
('system', ('plan:system', {}), {
|
|
99
|
+
'doc': 'The planning system which defines this procedure.'}),
|
|
100
|
+
|
|
101
|
+
('type', ('plan:procedure:type:taxonomy', {}), {
|
|
102
|
+
'doc': 'A type classification for the procedure.'}),
|
|
103
|
+
|
|
104
|
+
('inputs', ('array', {'type': 'plan:procedure:variable', 'uniq': True, 'sorted': True}), {
|
|
105
|
+
'doc': 'An array of inputs required to execute the procedure.'}),
|
|
106
|
+
|
|
107
|
+
('firststep', ('plan:procedure:step', {}), {
|
|
108
|
+
'doc': 'The first step in the procedure.'}),
|
|
109
|
+
)),
|
|
110
|
+
('plan:procedure:variable', {}, (
|
|
111
|
+
|
|
112
|
+
('name', ('str', {}), {
|
|
113
|
+
'doc': 'The name of the variable.'}),
|
|
114
|
+
|
|
115
|
+
('type', ('str', {}), {
|
|
116
|
+
'doc': 'The type for the input. Types are specific to the planning system.'}),
|
|
117
|
+
|
|
118
|
+
('default', ('data', {}), {
|
|
119
|
+
'doc': 'The optional default value if the procedure is invoked without the input.'}),
|
|
120
|
+
|
|
121
|
+
('procedure', ('plan:procedure', {}), {
|
|
122
|
+
'doc': 'The procedure which defines the variable.'}),
|
|
123
|
+
)),
|
|
124
|
+
('plan:procedure:step', {}, (
|
|
125
|
+
|
|
126
|
+
('phase', ('plan:phase', {}), {
|
|
127
|
+
'doc': 'The phase that the step belongs within.'}),
|
|
128
|
+
|
|
129
|
+
('procedure', ('plan:procedure', {}), {
|
|
130
|
+
'doc': 'The procedure which defines the step.'}),
|
|
131
|
+
|
|
132
|
+
('title', ('str', {}), {
|
|
133
|
+
'ex': 'Scan the IPv4 address range for open ports',
|
|
134
|
+
'doc': 'The title of the step.'}),
|
|
135
|
+
|
|
136
|
+
('summary', ('str', {}), {
|
|
137
|
+
'doc': 'A summary of the tasks executed within the step.'}),
|
|
138
|
+
|
|
139
|
+
('outputs', ('array', {'type': 'plan:procedure:variable', 'uniq': True, 'sorted': True}), {
|
|
140
|
+
'doc': 'An array of variables defined in this step.'}),
|
|
141
|
+
|
|
142
|
+
('techniques', ('array', {'type': 'ou:technique', 'uniq': True, 'sorted': True}), {
|
|
143
|
+
'doc': 'An array of techniques used when executing this step.'}),
|
|
144
|
+
|
|
145
|
+
('links', ('array', {'type': 'plan:procedure:link', 'uniq': True}), {
|
|
146
|
+
'doc': 'An array of links to subsequent steps.'}),
|
|
147
|
+
|
|
148
|
+
)),
|
|
149
|
+
('plan:procedure:link', {}, (
|
|
150
|
+
|
|
151
|
+
('condition', ('bool', {}), {
|
|
152
|
+
'doc': 'Set to true/false if this link is conditional based on a decision step.'}),
|
|
153
|
+
|
|
154
|
+
('next', ('plan:procedure:step', {}), {
|
|
155
|
+
'doc': 'The next step in the plan.'}),
|
|
156
|
+
|
|
157
|
+
('procedure', ('plan:procedure', {}), {
|
|
158
|
+
'doc': 'The procedure which defines the link.'}),
|
|
159
|
+
)),
|
|
160
|
+
),
|
|
161
|
+
}),)
|
synapse/tests/test_cortex.py
CHANGED
|
@@ -7958,7 +7958,7 @@ class CortexBasicTest(s_t_utils.SynTest):
|
|
|
7958
7958
|
waiter = core01.stormpool.waiter(1, 'svc:del')
|
|
7959
7959
|
msgs = await core01.stormlist('aha.pool.svc.del pool00... 01.core...', opts={'mirror': False})
|
|
7960
7960
|
self.stormHasNoWarnErr(msgs)
|
|
7961
|
-
self.stormIsInPrint('AHA service (01.core
|
|
7961
|
+
self.stormIsInPrint('AHA service (01.core.loop.vertex.link) removed from service pool (pool00.loop.vertex.link)', msgs)
|
|
7962
7962
|
|
|
7963
7963
|
# TODO: this wait should not return None
|
|
7964
7964
|
await waiter.wait(timeout=3)
|
synapse/tests/test_lib_agenda.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import asyncio
|
|
2
3
|
import hashlib
|
|
3
4
|
import datetime
|
|
@@ -156,7 +157,7 @@ class AgendaTest(s_t_utils.SynTest):
|
|
|
156
157
|
newts = ar.nexttime(now)
|
|
157
158
|
self.eq(newts, datetime.datetime(year=2018, month=12, day=5, hour=7, minute=2, tzinfo=tz.utc).timestamp())
|
|
158
159
|
|
|
159
|
-
async def
|
|
160
|
+
async def test_agenda_base(self):
|
|
160
161
|
MONO_DELT = 1543827303.0
|
|
161
162
|
unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp()
|
|
162
163
|
|
|
@@ -382,8 +383,21 @@ class AgendaTest(s_t_utils.SynTest):
|
|
|
382
383
|
self.true(appt.enabled)
|
|
383
384
|
self.eq(0, appt.startcount)
|
|
384
385
|
|
|
385
|
-
|
|
386
|
-
|
|
386
|
+
# Ensure structured logging captures the cron iden value
|
|
387
|
+
core.stormlog = True
|
|
388
|
+
with self.getStructuredAsyncLoggerStream('synapse.storm') as stream:
|
|
389
|
+
unixtime = datetime.datetime(year=2019, month=2, day=13, hour=10, minute=16,
|
|
390
|
+
tzinfo=tz.utc).timestamp()
|
|
391
|
+
self.eq((12, 'bar'), await asyncio.wait_for(core.callStorm('return($lib.queue.gen(visi).pop(wait=$lib.true))'), timeout=5))
|
|
392
|
+
core.stormlog = False
|
|
393
|
+
|
|
394
|
+
data = stream.getvalue()
|
|
395
|
+
raw_mesgs = [m for m in data.split('\n') if m]
|
|
396
|
+
msgs = [json.loads(m) for m in raw_mesgs]
|
|
397
|
+
msgs = [m for m in msgs if m['text'] == '$lib.queue.gen(visi).put(bar)']
|
|
398
|
+
self.gt(len(msgs), 0)
|
|
399
|
+
for m in msgs:
|
|
400
|
+
self.eq(m.get('cron'), appt.iden)
|
|
387
401
|
|
|
388
402
|
self.eq(1, appt.startcount)
|
|
389
403
|
|
synapse/tests/test_lib_aha.py
CHANGED
|
@@ -1126,7 +1126,7 @@ class AhaTest(s_test.SynTest):
|
|
|
1126
1126
|
ready = svcinfo.get('ready')
|
|
1127
1127
|
online = svcinfo.get('online')
|
|
1128
1128
|
self.none(online)
|
|
1129
|
-
self.
|
|
1129
|
+
self.false(ready) # Ready is cleared upon restart / setting service down.
|
|
1130
1130
|
|
|
1131
1131
|
n = 3
|
|
1132
1132
|
if len(stack._exit_callbacks) > 0:
|
|
@@ -1245,7 +1245,12 @@ class AhaTest(s_test.SynTest):
|
|
|
1245
1245
|
|
|
1246
1246
|
msgs = await core00.stormlist('aha.pool.svc.del pool00... 00...')
|
|
1247
1247
|
self.stormHasNoWarnErr(msgs)
|
|
1248
|
-
self.stormIsInPrint('AHA service (00
|
|
1248
|
+
self.stormIsInPrint('AHA service (00.loop.vertex.link) removed from service pool (pool00.loop.vertex.link)',
|
|
1249
|
+
msgs)
|
|
1250
|
+
|
|
1251
|
+
msgs = await core00.stormlist('aha.pool.svc.del pool00... 00...')
|
|
1252
|
+
self.stormHasNoWarnErr(msgs)
|
|
1253
|
+
self.stormIsInPrint('Did not remove (00...) from the service pool.', msgs)
|
|
1249
1254
|
|
|
1250
1255
|
await waiter.wait(timeout=3)
|
|
1251
1256
|
run00 = await (await pool.proxy(timeout=3)).getCellRunId()
|
synapse/tests/test_lib_cell.py
CHANGED
|
@@ -496,52 +496,6 @@ class CellTest(s_t_utils.SynTest):
|
|
|
496
496
|
with self.raises(s_exc.AuthDeny):
|
|
497
497
|
await prox.setCellUser(visiiden)
|
|
498
498
|
|
|
499
|
-
async def test_cell_hiveboot(self):
|
|
500
|
-
|
|
501
|
-
with self.getTestDir() as dirn:
|
|
502
|
-
|
|
503
|
-
tree = {
|
|
504
|
-
'kids': {
|
|
505
|
-
'hehe': {'value': 'haha'},
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
bootpath = os.path.join(dirn, 'hiveboot.yaml')
|
|
510
|
-
|
|
511
|
-
s_common.yamlsave(tree, bootpath)
|
|
512
|
-
|
|
513
|
-
with warnings.catch_warnings(record=True) as warns:
|
|
514
|
-
async with self.getTestCell(s_cell.Cell, dirn=dirn) as cell:
|
|
515
|
-
self.eq('haha', await cell.hive.get(('hehe',)))
|
|
516
|
-
|
|
517
|
-
self.isin('Initial hive config from hiveboot.yaml', str(warns[0].message))
|
|
518
|
-
|
|
519
|
-
# test that the file does not load again
|
|
520
|
-
tree['kids']['redballoons'] = {'value': 99}
|
|
521
|
-
s_common.yamlsave(tree, bootpath)
|
|
522
|
-
|
|
523
|
-
async with self.getTestCell(s_cell.Cell, dirn=dirn) as cell:
|
|
524
|
-
self.none(await cell.hive.get(('redballoons',)))
|
|
525
|
-
|
|
526
|
-
# Do a full hive dump/load
|
|
527
|
-
with self.getTestDir() as dirn:
|
|
528
|
-
dir0 = s_common.genpath(dirn, 'cell00')
|
|
529
|
-
dir1 = s_common.genpath(dirn, 'cell01')
|
|
530
|
-
async with self.getTestCell(s_cell.Cell, dirn=dir0, conf={'auth:passwd': 'root'}) as cell00:
|
|
531
|
-
await cell00.hive.set(('beeps',), [1, 2, 'three'])
|
|
532
|
-
|
|
533
|
-
tree = await cell00.saveHiveTree()
|
|
534
|
-
s_common.yamlsave(tree, dir1, 'hiveboot.yaml')
|
|
535
|
-
with s_common.genfile(dir1, 'cell.guid') as fd:
|
|
536
|
-
_ = fd.write(cell00.iden.encode())
|
|
537
|
-
|
|
538
|
-
async with self.getTestCell(s_cell.Cell, dirn=dir1) as cell01:
|
|
539
|
-
resp = await cell01.hive.get(('beeps',))
|
|
540
|
-
self.isinstance(resp, tuple)
|
|
541
|
-
self.eq(resp, (1, 2, 'three'))
|
|
542
|
-
|
|
543
|
-
self.eq(cell00.iden, cell01.iden)
|
|
544
|
-
|
|
545
499
|
async def test_cell_getinfo(self):
|
|
546
500
|
async with self.getTestCore() as cell:
|
|
547
501
|
cell.COMMIT = 'mycommit'
|
|
@@ -2243,8 +2197,8 @@ class CellTest(s_t_utils.SynTest):
|
|
|
2243
2197
|
async with self.getTestCore(dirn=dirn, conf=conf) as core:
|
|
2244
2198
|
pass
|
|
2245
2199
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2200
|
+
stream.seek(0)
|
|
2201
|
+
self.isin('onboot optimization complete!', stream.read())
|
|
2248
2202
|
|
|
2249
2203
|
stat01 = os.stat(lmdbfile)
|
|
2250
2204
|
self.ne(stat00.st_ino, stat01.st_ino)
|
|
@@ -2266,10 +2220,25 @@ class CellTest(s_t_utils.SynTest):
|
|
|
2266
2220
|
async with self.getTestCore(dirn=dirn, conf=conf) as core:
|
|
2267
2221
|
pass
|
|
2268
2222
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2223
|
+
stream.seek(0)
|
|
2224
|
+
buf = stream.read()
|
|
2225
|
+
self.notin('onboot optimization complete!', buf)
|
|
2226
|
+
self.isin('not on the same volume', buf)
|
|
2227
|
+
|
|
2228
|
+
# Local backup files are skipped
|
|
2229
|
+
async with self.getTestCore(dirn=dirn) as core:
|
|
2230
|
+
await core.runBackup()
|
|
2231
|
+
|
|
2232
|
+
with self.getAsyncLoggerStream('synapse.lib.cell') as stream:
|
|
2233
|
+
|
|
2234
|
+
conf = {'onboot:optimize': True}
|
|
2235
|
+
async with self.getTestCore(dirn=dirn, conf=conf) as core:
|
|
2236
|
+
pass
|
|
2237
|
+
|
|
2238
|
+
stream.seek(0)
|
|
2239
|
+
buf = stream.read()
|
|
2240
|
+
self.isin('Skipping backup file', buf)
|
|
2241
|
+
self.isin('onboot optimization complete!', buf)
|
|
2273
2242
|
|
|
2274
2243
|
async def test_cell_gc(self):
|
|
2275
2244
|
async with self.getTestCore() as core:
|
|
@@ -139,7 +139,7 @@ Member: 00.cell.loop.vertex.link'''
|
|
|
139
139
|
self.len(nevents, await waiter.wait(timeout=12))
|
|
140
140
|
|
|
141
141
|
msgs = await core00.stormlist('aha.svc.list')
|
|
142
|
-
self.stormIsInPrint('01.cell.loop.vertex.link false false
|
|
142
|
+
self.stormIsInPrint('01.cell.loop.vertex.link false false false', msgs)
|
|
143
143
|
|
|
144
144
|
# Fake a record
|
|
145
145
|
await aha.addAhaSvc('00.newp', info={'urlinfo': {'scheme': 'tcp', 'host': '0.0.0.0', 'port': '3030'}},
|
|
@@ -281,6 +281,18 @@ $request.reply(206, headers=$headers, body=({"no":"body"}))
|
|
|
281
281
|
self.eq(data.get('json'), 'err')
|
|
282
282
|
self.eq(data.get('path'), 'echo/words/wOw')
|
|
283
283
|
|
|
284
|
+
# Storm query logging includes the httpapi iden in structlog data
|
|
285
|
+
core.stormlog = True
|
|
286
|
+
with self.getStructuredAsyncLoggerStream('synapse.storm', 'Executing storm query') as stream:
|
|
287
|
+
resp = await sess.get(url)
|
|
288
|
+
self.eq(resp.status, 200)
|
|
289
|
+
self.true(await stream.wait(timeout=12))
|
|
290
|
+
data = stream.getvalue()
|
|
291
|
+
raw_mesgs = [m for m in data.split('\n') if m]
|
|
292
|
+
msgs = [json.loads(m) for m in raw_mesgs]
|
|
293
|
+
self.eq(msgs[0].get('httpapi'), echoiden)
|
|
294
|
+
core.stormlog = False
|
|
295
|
+
|
|
284
296
|
# Sad paths on the $request methods
|
|
285
297
|
q = '''$api = $lib.cortex.httpapi.add(testpath02)
|
|
286
298
|
$api.methods.get = ${ $request.sendcode(200) $request.sendheaders('beep beep') }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import synapse.exc as s_exc
|
|
2
|
+
import synapse.lib.time as s_time
|
|
2
3
|
import synapse.lib.layer as s_layer
|
|
3
4
|
|
|
4
5
|
import synapse.tests.utils as s_test
|
|
@@ -295,3 +296,107 @@ class StormlibModelTest(s_test.SynTest):
|
|
|
295
296
|
|
|
296
297
|
self.stormIsInWarn('.pdep is not yet locked', mesgs)
|
|
297
298
|
self.stormNotInWarn('test:dep:easy.pdep is not yet locked', mesgs)
|
|
299
|
+
|
|
300
|
+
async def test_stormlib_model_migration(self):
|
|
301
|
+
|
|
302
|
+
async with self.getTestCore() as core:
|
|
303
|
+
|
|
304
|
+
nodes = await core.nodes('[ test:str=src test:str=dst test:str=deny test:str=other ]')
|
|
305
|
+
otheriden = nodes[3].iden()
|
|
306
|
+
|
|
307
|
+
lowuser = await core.auth.addUser('lowuser')
|
|
308
|
+
aslow = {'user': lowuser.iden}
|
|
309
|
+
|
|
310
|
+
# copy node data
|
|
311
|
+
|
|
312
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=src $lib.model.migration.copyData($node, newp)'))
|
|
313
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=dst $lib.model.migration.copyData(newp, $node)'))
|
|
314
|
+
|
|
315
|
+
nodes = await core.nodes('''
|
|
316
|
+
test:str=src
|
|
317
|
+
$node.data.set(a, a-src)
|
|
318
|
+
$node.data.set(b, b-src)
|
|
319
|
+
$n=$node -> {
|
|
320
|
+
test:str=dst
|
|
321
|
+
$node.data.set(a, a-dst)
|
|
322
|
+
$lib.model.migration.copyData($n, $node)
|
|
323
|
+
}
|
|
324
|
+
''')
|
|
325
|
+
self.len(1, nodes)
|
|
326
|
+
self.sorteq(
|
|
327
|
+
[('a', 'a-dst'), ('b', 'b-src')],
|
|
328
|
+
[data async for data in nodes[0].iterData()]
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
nodes = await core.nodes('''
|
|
332
|
+
test:str=src $n=$node -> {
|
|
333
|
+
test:str=dst
|
|
334
|
+
$lib.model.migration.copyData($n, $node, overwrite=$lib.true)
|
|
335
|
+
}
|
|
336
|
+
''')
|
|
337
|
+
self.len(1, nodes)
|
|
338
|
+
self.sorteq(
|
|
339
|
+
[('a', 'a-src'), ('b', 'b-src')],
|
|
340
|
+
[data async for data in nodes[0].iterData()]
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
q = 'test:str=src $n=$node -> { test:str=deny $lib.model.migration.copyData($n, $node) }'
|
|
344
|
+
await self.asyncraises(s_exc.AuthDeny, core.nodes(q, opts=aslow))
|
|
345
|
+
|
|
346
|
+
# copy edges
|
|
347
|
+
|
|
348
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=src $lib.model.migration.copyEdges($node, newp)'))
|
|
349
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=dst $lib.model.migration.copyEdges(newp, $node)'))
|
|
350
|
+
|
|
351
|
+
nodes = await core.nodes('''
|
|
352
|
+
test:str=src
|
|
353
|
+
[ <(foo)+ { test:str=other } +(bar)> { test:str=other } ]
|
|
354
|
+
$n=$node -> {
|
|
355
|
+
test:str=dst
|
|
356
|
+
$lib.model.migration.copyEdges($n, $node)
|
|
357
|
+
}
|
|
358
|
+
''')
|
|
359
|
+
self.len(1, nodes)
|
|
360
|
+
self.eq([('bar', otheriden)], [edge async for edge in nodes[0].iterEdgesN1()])
|
|
361
|
+
self.eq([('foo', otheriden)], [edge async for edge in nodes[0].iterEdgesN2()])
|
|
362
|
+
|
|
363
|
+
q = 'test:str=src $n=$node -> { test:str=deny $lib.model.migration.copyEdges($n, $node) }'
|
|
364
|
+
await self.asyncraises(s_exc.AuthDeny, core.nodes(q, opts=aslow))
|
|
365
|
+
|
|
366
|
+
# copy tags
|
|
367
|
+
|
|
368
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=src $lib.model.migration.copyTags($node, newp)'))
|
|
369
|
+
await self.asyncraises(s_exc.BadArg, core.nodes('test:str=dst $lib.model.migration.copyTags(newp, $node)'))
|
|
370
|
+
|
|
371
|
+
await core.nodes('$lib.model.ext.addTagProp(test, (str, ({})), ({}))')
|
|
372
|
+
|
|
373
|
+
nodes = await core.nodes('''
|
|
374
|
+
test:str=src
|
|
375
|
+
[ +#foo=(2010, 2012) +#foo.bar +#baz:test=src ]
|
|
376
|
+
$n=$node -> {
|
|
377
|
+
test:str=dst
|
|
378
|
+
[ +#foo=(2010, 2011) +#baz:test=dst ]
|
|
379
|
+
$lib.model.migration.copyTags($n, $node)
|
|
380
|
+
}
|
|
381
|
+
''')
|
|
382
|
+
self.len(1, nodes)
|
|
383
|
+
self.sorteq([
|
|
384
|
+
('baz', (None, None)),
|
|
385
|
+
('foo', (s_time.parse('2010'), s_time.parse('2012'))),
|
|
386
|
+
('foo.bar', (None, None))
|
|
387
|
+
], nodes[0].getTags())
|
|
388
|
+
self.eq([], nodes[0].getTagProps('foo'))
|
|
389
|
+
self.eq([], nodes[0].getTagProps('foo.bar'))
|
|
390
|
+
self.eq([('test', 'dst')], [(k, nodes[0].getTagProp('baz', k)) for k in nodes[0].getTagProps('baz')])
|
|
391
|
+
|
|
392
|
+
nodes = await core.nodes('''
|
|
393
|
+
test:str=src $n=$node -> {
|
|
394
|
+
test:str=dst
|
|
395
|
+
$lib.model.migration.copyTags($n, $node, overwrite=$lib.true)
|
|
396
|
+
}
|
|
397
|
+
''')
|
|
398
|
+
self.len(1, nodes)
|
|
399
|
+
self.eq([('test', 'src')], [(k, nodes[0].getTagProp('baz', k)) for k in nodes[0].getTagProps('baz')])
|
|
400
|
+
|
|
401
|
+
q = 'test:str=src $n=$node -> { test:str=deny $lib.model.migration.copyTags($n, $node) }'
|
|
402
|
+
await self.asyncraises(s_exc.AuthDeny, core.nodes(q, opts=aslow))
|
|
@@ -575,6 +575,32 @@ class StormTypesTest(s_test.SynTest):
|
|
|
575
575
|
self.len(1, nodes)
|
|
576
576
|
self.eq(30, nodes[0].ndef[1])
|
|
577
577
|
|
|
578
|
+
# $lib.min / $lib.max behavior with 1 item
|
|
579
|
+
ret = await core.callStorm('$x = ([(1234)]) return ( $lib.min($x) )')
|
|
580
|
+
self.eq(ret, 1234)
|
|
581
|
+
|
|
582
|
+
ret = await core.callStorm('return ( $lib.min(1234) )')
|
|
583
|
+
self.eq(ret, 1234)
|
|
584
|
+
|
|
585
|
+
ret = await core.callStorm('$x = ([(1234)]) return ( $lib.max($x) )')
|
|
586
|
+
self.eq(ret, 1234)
|
|
587
|
+
|
|
588
|
+
ret = await core.callStorm('return ( $lib.max(1234) )')
|
|
589
|
+
self.eq(ret, 1234)
|
|
590
|
+
|
|
591
|
+
# $lib.min / $lib.max behavior with 0 items
|
|
592
|
+
with self.raises(s_exc.StormRuntimeError):
|
|
593
|
+
await core.callStorm('$lib.max()')
|
|
594
|
+
|
|
595
|
+
with self.raises(s_exc.StormRuntimeError):
|
|
596
|
+
await core.callStorm('$l=() $lib.max($l)')
|
|
597
|
+
|
|
598
|
+
with self.raises(s_exc.StormRuntimeError):
|
|
599
|
+
await core.callStorm('$lib.min()')
|
|
600
|
+
|
|
601
|
+
with self.raises(s_exc.StormRuntimeError):
|
|
602
|
+
await core.callStorm('$l=() $lib.min($l)')
|
|
603
|
+
|
|
578
604
|
nodes = await core.nodes('[ inet:asn=$lib.len(asdf) ]')
|
|
579
605
|
self.len(1, nodes)
|
|
580
606
|
self.eq(4, nodes[0].ndef[1])
|
|
@@ -369,7 +369,7 @@ class TrigTest(s_t_utils.SynTest):
|
|
|
369
369
|
|
|
370
370
|
# coverage for migration mode
|
|
371
371
|
await core.nodes('[inet:fqdn=vertex.link +#foo]') # for additional migration mode trigger tests below
|
|
372
|
-
with core.enterMigrationMode():
|
|
372
|
+
async with core.enterMigrationMode():
|
|
373
373
|
await core.nodes('inet:fqdn=vertex.link [ +#bar -#foo ]')
|
|
374
374
|
|
|
375
375
|
async def test_trigger_delete(self):
|
|
@@ -774,7 +774,7 @@ class TrigTest(s_t_utils.SynTest):
|
|
|
774
774
|
await core.nodes('trigger.add edge:add --verb r* --n2form test:int --query { [ +#n2 ] }')
|
|
775
775
|
await core.nodes('trigger.add edge:add --verb no** --form test:int --n2form test:str --query { [ +#both ] }')
|
|
776
776
|
|
|
777
|
-
with core.enterMigrationMode():
|
|
777
|
+
async with core.enterMigrationMode():
|
|
778
778
|
nodes = await core.nodes('[test:int=123 +(foo:beep:boop)> { [test:str=neato] }]')
|
|
779
779
|
self.len(1, nodes)
|
|
780
780
|
self.notin('foo', nodes[0].tags)
|
|
@@ -834,7 +834,7 @@ class TrigTest(s_t_utils.SynTest):
|
|
|
834
834
|
await core.nodes('trigger.add edge:del --verb r* --n2form test:int --query { [ +#del.two ] }')
|
|
835
835
|
await core.nodes('trigger.add edge:del --verb no** --form test:int --n2form test:str --query { [ +#del.all ] }')
|
|
836
836
|
|
|
837
|
-
with core.enterMigrationMode():
|
|
837
|
+
async with core.enterMigrationMode():
|
|
838
838
|
nodes = await core.nodes('test:int=123 | [ -(foo:beep:boop)> { test:str=neato } ]')
|
|
839
839
|
self.len(1, nodes)
|
|
840
840
|
self.notin('del.none', nodes[0].tags)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import synapse.exc as s_exc
|
|
2
|
+
import synapse.common as s_common
|
|
3
|
+
import synapse.tests.utils as s_t_utils
|
|
4
|
+
|
|
5
|
+
class PlanModelTest(s_t_utils.SynTest):
|
|
6
|
+
|
|
7
|
+
async def test_model_planning(self):
|
|
8
|
+
|
|
9
|
+
async with self.getTestCore() as core:
|
|
10
|
+
nodes = await core.nodes('''
|
|
11
|
+
[ plan:system=*
|
|
12
|
+
:name="Woot CNO Planner"
|
|
13
|
+
:author={[ ps:contact=* :name=visi ]}
|
|
14
|
+
:created=20240202
|
|
15
|
+
:updated=20240203
|
|
16
|
+
:version=1.0.0
|
|
17
|
+
:url=https://vertex.link
|
|
18
|
+
]
|
|
19
|
+
''')
|
|
20
|
+
self.len(1, nodes)
|
|
21
|
+
self.eq('woot cno planner', nodes[0].get('name'))
|
|
22
|
+
self.eq(1706832000000, nodes[0].get('created'))
|
|
23
|
+
self.eq(1706918400000, nodes[0].get('updated'))
|
|
24
|
+
self.eq(1099511627776, nodes[0].get('version'))
|
|
25
|
+
self.eq('https://vertex.link', nodes[0].get('url'))
|
|
26
|
+
|
|
27
|
+
self.len(1, await core.nodes('plan:system :author -> ps:contact +:name=visi'))
|
|
28
|
+
|
|
29
|
+
nodes = await core.nodes('''
|
|
30
|
+
[ plan:phase=*
|
|
31
|
+
:system={ plan:system:name="Woot CNO Planner"}
|
|
32
|
+
:title="Recon"
|
|
33
|
+
:summary="Do some recon."
|
|
34
|
+
:index=17
|
|
35
|
+
:url=https://vertex.link/recon
|
|
36
|
+
]
|
|
37
|
+
''')
|
|
38
|
+
|
|
39
|
+
self.len(1, nodes)
|
|
40
|
+
self.eq('Recon', nodes[0].get('title'))
|
|
41
|
+
self.eq('Do some recon.', nodes[0].get('summary'))
|
|
42
|
+
self.eq(17, nodes[0].get('index'))
|
|
43
|
+
self.eq('https://vertex.link/recon', nodes[0].get('url'))
|
|
44
|
+
|
|
45
|
+
self.len(1, await core.nodes('plan:phase :system -> plan:system +:name="Woot CNO Planner"'))
|
|
46
|
+
|
|
47
|
+
nodes = await core.nodes('''
|
|
48
|
+
[ plan:procedure=*
|
|
49
|
+
:system={ plan:system:name="Woot CNO Planner"}
|
|
50
|
+
:title="Pwn Some Boxes"
|
|
51
|
+
:summary="Yoink."
|
|
52
|
+
:author={ ps:contact:name=visi }
|
|
53
|
+
:created=20240202
|
|
54
|
+
:updated=20240203
|
|
55
|
+
:version=1.0.0
|
|
56
|
+
:type=cno.offense
|
|
57
|
+
:system={ plan:system:name="Woot CNO Planner" }
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
$guid = $node.value()
|
|
61
|
+
|
|
62
|
+
[
|
|
63
|
+
:inputs={[ plan:procedure:variable=*
|
|
64
|
+
:name=network
|
|
65
|
+
:type=cidr
|
|
66
|
+
:default=127.0.0.0/24
|
|
67
|
+
:procedure=$guid
|
|
68
|
+
]}
|
|
69
|
+
|
|
70
|
+
:firststep={[ plan:procedure:step=*
|
|
71
|
+
:title="Are there vulnerable services?"
|
|
72
|
+
:summary="Scan the target network and identify available services."
|
|
73
|
+
:procedure=$guid
|
|
74
|
+
:phase={ plan:phase:title=Recon }
|
|
75
|
+
:outputs={[ plan:procedure:variable=* :name=services ]}
|
|
76
|
+
:techniques={[ ou:technique=* :name=netscan ]}
|
|
77
|
+
|
|
78
|
+
:links={[ plan:procedure:link=*
|
|
79
|
+
:condition=(true)
|
|
80
|
+
:procedure=$guid
|
|
81
|
+
:next={[ plan:procedure:step=*
|
|
82
|
+
:title="Exploit Services"
|
|
83
|
+
:summary="Gank that stuff."
|
|
84
|
+
:procedure=$guid
|
|
85
|
+
:outputs={[ plan:procedure:variable=* :name=shellz ]}
|
|
86
|
+
]}
|
|
87
|
+
|
|
88
|
+
]}
|
|
89
|
+
]}
|
|
90
|
+
]
|
|
91
|
+
''')
|
|
92
|
+
|
|
93
|
+
self.len(1, nodes)
|
|
94
|
+
self.eq('Pwn Some Boxes', nodes[0].get('title'))
|
|
95
|
+
self.eq('Yoink.', nodes[0].get('summary'))
|
|
96
|
+
self.nn(nodes[0].get('author'))
|
|
97
|
+
self.eq(1706832000000, nodes[0].get('created'))
|
|
98
|
+
self.eq(1706918400000, nodes[0].get('updated'))
|
|
99
|
+
self.eq(1099511627776, nodes[0].get('version'))
|
|
100
|
+
|
|
101
|
+
self.len(1, await core.nodes('plan:procedure :type -> plan:procedure:type:taxonomy'))
|
|
102
|
+
self.len(1, await core.nodes('plan:procedure :system -> plan:system +:name="Woot CNO Planner"'))
|
|
103
|
+
self.len(1, await core.nodes('plan:procedure :firststep -> plan:procedure:step -> plan:procedure:link'))
|
|
104
|
+
|
|
105
|
+
nodes = await core.nodes('plan:procedure :inputs -> plan:procedure:variable')
|
|
106
|
+
self.len(1, nodes)
|
|
107
|
+
self.eq('network', nodes[0].get('name'))
|
|
108
|
+
self.eq('cidr', nodes[0].get('type'))
|
|
109
|
+
self.eq('127.0.0.0/24', nodes[0].get('default'))
|
|
110
|
+
self.nn(nodes[0].get('procedure'))
|
|
111
|
+
|
|
112
|
+
nodes = await core.nodes('plan:procedure :firststep -> plan:procedure:step')
|
|
113
|
+
self.len(1, nodes)
|
|
114
|
+
self.eq('Are there vulnerable services?', nodes[0].get('title'))
|
|
115
|
+
self.eq('Scan the target network and identify available services.', nodes[0].get('summary'))
|
|
116
|
+
self.nn(nodes[0].get('procedure'))
|
|
117
|
+
|
|
118
|
+
self.len(1, await core.nodes('plan:procedure :firststep -> plan:procedure:step -> plan:phase'))
|
|
119
|
+
self.len(1, await core.nodes('plan:procedure :firststep -> plan:procedure:step :techniques -> ou:technique'))
|
|
120
|
+
self.len(1, await core.nodes('plan:procedure :firststep -> plan:procedure:step :outputs -> plan:procedure:variable'))
|
|
121
|
+
|
|
122
|
+
nodes = await core.nodes('plan:procedure :firststep -> plan:procedure:step -> plan:procedure:link')
|
|
123
|
+
self.len(1, nodes)
|
|
124
|
+
self.eq(True, nodes[0].get('condition'))
|
|
125
|
+
self.nn(nodes[0].get('next'))
|
|
126
|
+
self.nn(nodes[0].get('procedure'))
|