synapse 2.152.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/axon.py +19 -16
- synapse/cortex.py +203 -15
- synapse/exc.py +0 -2
- synapse/lib/ast.py +42 -23
- synapse/lib/autodoc.py +2 -2
- synapse/lib/cache.py +16 -1
- synapse/lib/cell.py +5 -5
- synapse/lib/httpapi.py +198 -2
- synapse/lib/layer.py +5 -2
- synapse/lib/modelrev.py +36 -3
- synapse/lib/node.py +2 -5
- synapse/lib/parser.py +1 -1
- synapse/lib/schemas.py +51 -0
- synapse/lib/snap.py +10 -0
- synapse/lib/storm.lark +24 -4
- synapse/lib/storm.py +98 -19
- synapse/lib/storm_format.py +1 -1
- synapse/lib/stormhttp.py +11 -4
- synapse/lib/stormlib/auth.py +16 -2
- 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/cortex.py +1168 -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 +6 -1
- synapse/lib/stormlib/ipv6.py +1 -0
- synapse/lib/stormlib/iters.py +58 -1
- 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 +223 -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 +250 -170
- 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 +21 -6
- synapse/models/orgs.py +48 -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_axon.py +7 -2
- synapse/tests/test_cortex.py +231 -35
- synapse/tests/test_lib_ast.py +138 -43
- 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 +14 -1
- synapse/tests/test_lib_stormhttp.py +65 -6
- synapse/tests/test_lib_stormlib_auth.py +12 -3
- synapse/tests/test_lib_stormlib_cortex.py +1327 -0
- synapse/tests/test_lib_stormlib_iters.py +116 -0
- synapse/tests/test_lib_stormlib_stats.py +187 -0
- synapse/tests/test_lib_stormlib_storm.py +8 -0
- synapse/tests/test_lib_stormsvc.py +24 -1
- synapse/tests/test_lib_stormtypes.py +124 -69
- synapse/tests/test_lib_trigger.py +315 -0
- synapse/tests/test_lib_view.py +1 -2
- synapse/tests/test_model_base.py +26 -0
- 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/tests/utils.py +39 -0
- synapse/tools/autodoc.py +42 -2
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/METADATA +2 -2
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/RECORD +87 -79
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
- {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
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
|
}),
|
|
@@ -7,6 +7,29 @@ logo:
|
|
|
7
7
|
|
|
8
8
|
modules:
|
|
9
9
|
- name: testmod
|
|
10
|
+
- name: apimod
|
|
11
|
+
apidefs:
|
|
12
|
+
- name: search
|
|
13
|
+
desc: |
|
|
14
|
+
Execute a search
|
|
15
|
+
|
|
16
|
+
This API will foo the bar.
|
|
17
|
+
type:
|
|
18
|
+
type: function
|
|
19
|
+
args:
|
|
20
|
+
- { name: text, type: str, desc: "The text." }
|
|
21
|
+
- { name: mintime, type: [str, int], desc: "The mintime.", default: "-30days" }
|
|
22
|
+
returns:
|
|
23
|
+
name: yields
|
|
24
|
+
type: node
|
|
25
|
+
desc: Yields it:dev:str nodes.
|
|
26
|
+
- name: status
|
|
27
|
+
desc: Get the status of the foo.
|
|
28
|
+
type:
|
|
29
|
+
type: function
|
|
30
|
+
returns:
|
|
31
|
+
type: dict
|
|
32
|
+
desc: A status dictionary.
|
|
10
33
|
|
|
11
34
|
external_modules:
|
|
12
35
|
- name: testext
|
synapse/tests/test_axon.py
CHANGED
|
@@ -278,8 +278,14 @@ class AxonTest(s_t_utils.SynTest):
|
|
|
278
278
|
(lsize, l256) = await axon.put(linesbuf)
|
|
279
279
|
(jsize, j256) = await axon.put(jsonsbuf)
|
|
280
280
|
(bsize, b256) = await axon.put(b'\n'.join((jsonsbuf, linesbuf)))
|
|
281
|
+
(binsize, bin256) = await axon.put(bin_buf)
|
|
282
|
+
|
|
281
283
|
lines = [item async for item in axon.readlines(s_common.ehex(l256))]
|
|
282
284
|
self.eq(('asdf', '', 'qwer'), lines)
|
|
285
|
+
lines = [item async for item in axon.readlines(s_common.ehex(bin256))] # Default is errors=ignore
|
|
286
|
+
self.eq(lines, ['/$A\x00_v4\x1b'])
|
|
287
|
+
lines = [item async for item in axon.readlines(s_common.ehex(bin256), errors='replace')]
|
|
288
|
+
self.eq(lines, ['�/$�A�\x00_�v4��\x1b'])
|
|
283
289
|
jsons = [item async for item in axon.jsonlines(s_common.ehex(j256))]
|
|
284
290
|
self.eq(({'foo': 'bar'}, {'baz': 'faz'}), jsons)
|
|
285
291
|
jsons = []
|
|
@@ -288,9 +294,8 @@ class AxonTest(s_t_utils.SynTest):
|
|
|
288
294
|
jsons.append(item)
|
|
289
295
|
self.eq(({'foo': 'bar'}, {'baz': 'faz'}), jsons)
|
|
290
296
|
|
|
291
|
-
binsize, bin256 = await axon.put(bin_buf)
|
|
292
297
|
with self.raises(s_exc.BadDataValu):
|
|
293
|
-
lines = [item async for item in axon.readlines(s_common.ehex(bin256))]
|
|
298
|
+
lines = [item async for item in axon.readlines(s_common.ehex(bin256), errors=None)]
|
|
294
299
|
|
|
295
300
|
with self.raises(s_exc.NoSuchFile):
|
|
296
301
|
lines = [item async for item in axon.readlines(s_common.ehex(newphash))]
|
synapse/tests/test_cortex.py
CHANGED
|
@@ -2046,8 +2046,10 @@ class CortexTest(s_t_utils.SynTest):
|
|
|
2046
2046
|
await core.nodes('test:str +test:str@=2018')
|
|
2047
2047
|
with self.raises(s_exc.NoSuchCmpr):
|
|
2048
2048
|
await core.nodes('test:str +test:str:tick*near=newp')
|
|
2049
|
-
with self.raises(s_exc.
|
|
2049
|
+
with self.raises(s_exc.NoSuchCmpr):
|
|
2050
2050
|
await core.nodes('test:str +#test*near=newp')
|
|
2051
|
+
with self.raises(s_exc.BadTypeValu):
|
|
2052
|
+
await core.nodes('test:str +#test*in=newp')
|
|
2051
2053
|
with self.raises(s_exc.BadSyntax):
|
|
2052
2054
|
await core.nodes('test:str -> # } limit 10')
|
|
2053
2055
|
with self.raises(s_exc.BadSyntax):
|
|
@@ -6115,55 +6117,71 @@ class CortexBasicTest(s_t_utils.SynTest):
|
|
|
6115
6117
|
self.true(layr.lockmemory)
|
|
6116
6118
|
|
|
6117
6119
|
async def test_cortex_storm_lib_dmon(self):
|
|
6118
|
-
async with self.getTestCoreAndProxy() as (core, prox):
|
|
6119
|
-
nodes = await core.nodes('''
|
|
6120
6120
|
|
|
6121
|
-
|
|
6121
|
+
with self.getTestDir() as dirn:
|
|
6122
6122
|
|
|
6123
|
-
|
|
6124
|
-
|
|
6123
|
+
async with self.getTestCoreAndProxy(dirn=dirn) as (core, prox):
|
|
6124
|
+
nodes = await core.nodes('''
|
|
6125
6125
|
|
|
6126
|
-
|
|
6126
|
+
$lib.print(hi)
|
|
6127
6127
|
|
|
6128
|
-
$
|
|
6129
|
-
$
|
|
6128
|
+
$tx = $lib.queue.add(tx)
|
|
6129
|
+
$rx = $lib.queue.add(rx)
|
|
6130
6130
|
|
|
6131
|
-
$
|
|
6132
|
-
for ($offs, $ipv4) in $rx.gets(wait=1) {
|
|
6133
|
-
[ inet:ipv4=$ipv4 ]
|
|
6134
|
-
$rx.cull($offs)
|
|
6135
|
-
$tx.put($ipv4)
|
|
6136
|
-
}
|
|
6137
|
-
})
|
|
6131
|
+
$ddef = $lib.dmon.add(${
|
|
6138
6132
|
|
|
6139
|
-
|
|
6133
|
+
$rx = $lib.queue.get(tx)
|
|
6134
|
+
$tx = $lib.queue.get(rx)
|
|
6140
6135
|
|
|
6141
|
-
|
|
6136
|
+
$ipv4 = nope
|
|
6137
|
+
for ($offs, $ipv4) in $rx.gets(wait=1) {
|
|
6138
|
+
[ inet:ipv4=$ipv4 ]
|
|
6139
|
+
$rx.cull($offs)
|
|
6140
|
+
$tx.put($ipv4)
|
|
6141
|
+
}
|
|
6142
|
+
})
|
|
6142
6143
|
|
|
6143
|
-
|
|
6144
|
+
$tx.put(1.2.3.4)
|
|
6144
6145
|
|
|
6145
|
-
|
|
6146
|
+
for ($xoff, $xpv4) in $rx.gets(size=1, wait=1) { }
|
|
6146
6147
|
|
|
6147
|
-
|
|
6148
|
+
$lib.print(xed)
|
|
6148
6149
|
|
|
6149
|
-
|
|
6150
|
-
$lib.queue.del(rx)
|
|
6151
|
-
''')
|
|
6152
|
-
self.len(1, nodes)
|
|
6153
|
-
self.len(0, await prox.getStormDmons())
|
|
6150
|
+
inet:ipv4=$xpv4
|
|
6154
6151
|
|
|
6155
|
-
|
|
6156
|
-
await core.nodes('$lib.dmon.del(newp)')
|
|
6152
|
+
$lib.dmon.del($ddef.iden)
|
|
6157
6153
|
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6154
|
+
$lib.queue.del(tx)
|
|
6155
|
+
$lib.queue.del(rx)
|
|
6156
|
+
''')
|
|
6157
|
+
self.len(1, nodes)
|
|
6158
|
+
self.len(0, await prox.getStormDmons())
|
|
6161
6159
|
|
|
6162
|
-
|
|
6163
|
-
|
|
6160
|
+
with self.raises(s_exc.NoSuchIden):
|
|
6161
|
+
await core.nodes('$lib.dmon.del(newp)')
|
|
6164
6162
|
|
|
6165
|
-
|
|
6166
|
-
await core.
|
|
6163
|
+
await core.stormlist('auth.user.add user')
|
|
6164
|
+
user = await core.auth.getUserByName('user')
|
|
6165
|
+
asuser = {'user': user.iden}
|
|
6166
|
+
|
|
6167
|
+
ddef = await core.callStorm('return($lib.dmon.add(${$lib.print(foo)}))')
|
|
6168
|
+
iden = ddef.get('iden')
|
|
6169
|
+
asuser['vars'] = {'iden': iden}
|
|
6170
|
+
|
|
6171
|
+
with self.raises(s_exc.AuthDeny):
|
|
6172
|
+
await core.callStorm(f'$lib.dmon.del($iden)', opts=asuser)
|
|
6173
|
+
|
|
6174
|
+
# remove the dmon without a nexus entry to verify recover works
|
|
6175
|
+
await core._delStormDmon(iden)
|
|
6176
|
+
self.none(await core.callStorm('return($lib.dmon.get($iden))', opts=asuser))
|
|
6177
|
+
self.eq('storm:dmon:add', (await core.nexsroot.nexslog.last())[1][1])
|
|
6178
|
+
|
|
6179
|
+
async with self.getTestCoreAndProxy(dirn=dirn) as (core, prox):
|
|
6180
|
+
|
|
6181
|
+
# nexus recover() previously failed on adding to the hive
|
|
6182
|
+
# although the dmon would get successfully started
|
|
6183
|
+
self.nn(await core.callStorm('return($lib.dmon.get($iden))', opts=asuser))
|
|
6184
|
+
self.nn(core.stormdmonhive.get(iden))
|
|
6167
6185
|
|
|
6168
6186
|
async def test_cortex_storm_dmon_view(self):
|
|
6169
6187
|
|
|
@@ -7164,6 +7182,66 @@ class CortexBasicTest(s_t_utils.SynTest):
|
|
|
7164
7182
|
# Avoid races in cleanup
|
|
7165
7183
|
del genr
|
|
7166
7184
|
|
|
7185
|
+
async def test_cortex_syncnodeedits(self):
|
|
7186
|
+
|
|
7187
|
+
async with self.getTestCore() as core:
|
|
7188
|
+
|
|
7189
|
+
layr00 = core.getLayer().iden
|
|
7190
|
+
layr01 = (await core.addLayer())['iden']
|
|
7191
|
+
view01 = (await core.addView({'layers': (layr01,)}))['iden']
|
|
7192
|
+
|
|
7193
|
+
async def layrgenr(layr, startoff, endoff=None, newlayer=False):
|
|
7194
|
+
wait = endoff is None
|
|
7195
|
+
async for ioff, item, meta in layr.syncNodeEdits2(startoff, wait=wait):
|
|
7196
|
+
if endoff is not None and ioff >= endoff:
|
|
7197
|
+
break
|
|
7198
|
+
yield ioff, item, meta
|
|
7199
|
+
|
|
7200
|
+
indx = await core.getNexsIndx()
|
|
7201
|
+
|
|
7202
|
+
offsdict = {
|
|
7203
|
+
layr00: indx,
|
|
7204
|
+
layr01: indx,
|
|
7205
|
+
}
|
|
7206
|
+
|
|
7207
|
+
genr = None
|
|
7208
|
+
|
|
7209
|
+
try:
|
|
7210
|
+
|
|
7211
|
+
# test that a slow consumer can continue to stream edits
|
|
7212
|
+
# even if a layer exceeds the window maxsize
|
|
7213
|
+
|
|
7214
|
+
oldv = s_layer.WINDOW_MAXSIZE
|
|
7215
|
+
s_layer.WINDOW_MAXSIZE = 2
|
|
7216
|
+
|
|
7217
|
+
genr = core._syncNodeEdits(offsdict, layrgenr, wait=True)
|
|
7218
|
+
|
|
7219
|
+
nodes = await core.nodes('[ test:str=foo ]')
|
|
7220
|
+
item = await asyncio.wait_for(genr.__anext__(), timeout=2)
|
|
7221
|
+
self.eq(s_common.uhex(nodes[0].iden()), item[1][0][0])
|
|
7222
|
+
|
|
7223
|
+
# we should now be in live sync
|
|
7224
|
+
# and the empty layer will be pulling from the window
|
|
7225
|
+
|
|
7226
|
+
nodes = await core.nodes('[ test:str=bar ]')
|
|
7227
|
+
item = await asyncio.wait_for(genr.__anext__(), timeout=2)
|
|
7228
|
+
self.eq(s_common.uhex(nodes[0].iden()), item[1][0][0])
|
|
7229
|
+
|
|
7230
|
+
# add more nodes than the window size without consuming from the genr
|
|
7231
|
+
|
|
7232
|
+
opts = {'view': view01}
|
|
7233
|
+
nodes = await core.nodes('for $s in (baz, bam, cat, dog) { [ test:str=$s ] }', opts=opts)
|
|
7234
|
+
items = [await asyncio.wait_for(genr.__anext__(), timeout=2) for _ in range(4)]
|
|
7235
|
+
self.sorteq(
|
|
7236
|
+
[s_common.uhex(n.iden()) for n in nodes],
|
|
7237
|
+
[item[1][0][0] for item in items],
|
|
7238
|
+
)
|
|
7239
|
+
|
|
7240
|
+
finally:
|
|
7241
|
+
s_layer.WINDOW_MAXSIZE = oldv
|
|
7242
|
+
if genr is not None:
|
|
7243
|
+
del genr
|
|
7244
|
+
|
|
7167
7245
|
async def test_cortex_all_layr_read(self):
|
|
7168
7246
|
async with self.getTestCore() as core:
|
|
7169
7247
|
layr = core.getView().layers[0].iden
|
|
@@ -7613,3 +7691,121 @@ class CortexBasicTest(s_t_utils.SynTest):
|
|
|
7613
7691
|
self.isin('Set admin=True for lowuser', mesg.get('message'))
|
|
7614
7692
|
self.eq('admin', mesg.get('username'))
|
|
7615
7693
|
self.eq('lowuser', mesg.get('target_username'))
|
|
7694
|
+
|
|
7695
|
+
async def test_cortex_ext_httpapi(self):
|
|
7696
|
+
# Cortex API tests for Extended HttpAPI
|
|
7697
|
+
async with self.getTestCore() as core: # type: s_cortex.Cortex
|
|
7698
|
+
|
|
7699
|
+
newp = s_common.guid()
|
|
7700
|
+
with self.raises(s_exc.SynErr):
|
|
7701
|
+
await core.setHttpApiIndx(newp, 0)
|
|
7702
|
+
|
|
7703
|
+
unfo = await core.getUserDefByName('root')
|
|
7704
|
+
view = core.getView()
|
|
7705
|
+
info = await core.addHttpExtApi({
|
|
7706
|
+
'path': 'test/path/(hehe|haha)/(.*)',
|
|
7707
|
+
'owner': unfo.get('iden'),
|
|
7708
|
+
'view': view.iden,
|
|
7709
|
+
})
|
|
7710
|
+
|
|
7711
|
+
info2 = await core.addHttpExtApi({
|
|
7712
|
+
'path': 'something/else',
|
|
7713
|
+
'owner': unfo.get('iden'),
|
|
7714
|
+
'view': view.iden,
|
|
7715
|
+
})
|
|
7716
|
+
|
|
7717
|
+
info3 = await core.addHttpExtApi({
|
|
7718
|
+
'path': 'something/else/goes/here',
|
|
7719
|
+
'owner': unfo.get('iden'),
|
|
7720
|
+
'view': view.iden,
|
|
7721
|
+
})
|
|
7722
|
+
|
|
7723
|
+
info4 = await core.addHttpExtApi({
|
|
7724
|
+
'path': 'another/item',
|
|
7725
|
+
'owner': unfo.get('iden'),
|
|
7726
|
+
'view': view.iden,
|
|
7727
|
+
})
|
|
7728
|
+
|
|
7729
|
+
iden = info.get('iden')
|
|
7730
|
+
|
|
7731
|
+
adef = await core.getHttpExtApi(iden)
|
|
7732
|
+
self.eq(adef, info)
|
|
7733
|
+
|
|
7734
|
+
adef, args = await core.getHttpExtApiByPath('test/path/hehe/wow')
|
|
7735
|
+
self.eq(adef, info)
|
|
7736
|
+
self.eq(args, ('hehe', 'wow'))
|
|
7737
|
+
|
|
7738
|
+
adef, args = await core.getHttpExtApiByPath('test/path/hehe/wow/more/')
|
|
7739
|
+
self.eq(adef, info)
|
|
7740
|
+
self.eq(args, ('hehe', 'wow/more/'))
|
|
7741
|
+
|
|
7742
|
+
adef, args = await core.getHttpExtApiByPath('test/path/HeHe/wow')
|
|
7743
|
+
self.none(adef)
|
|
7744
|
+
self.eq(args, ())
|
|
7745
|
+
|
|
7746
|
+
async with core.getLocalProxy() as prox:
|
|
7747
|
+
adef, args = await prox.getHttpExtApiByPath('test/path/haha/words')
|
|
7748
|
+
self.eq(adef, info)
|
|
7749
|
+
self.eq(args, ('haha', 'words'))
|
|
7750
|
+
|
|
7751
|
+
self.len(4, core._exthttpapicache)
|
|
7752
|
+
|
|
7753
|
+
# Reordering / safety
|
|
7754
|
+
self.eq(1, await core.setHttpApiIndx(info4.get('iden'), 1))
|
|
7755
|
+
|
|
7756
|
+
# Cache is cleared when reloading
|
|
7757
|
+
self.len(0, core._exthttpapicache)
|
|
7758
|
+
adef, args = await core.getHttpExtApiByPath('test/path/hehe/wow')
|
|
7759
|
+
self.eq(adef, info)
|
|
7760
|
+
self.len(1, core._exthttpapicache)
|
|
7761
|
+
|
|
7762
|
+
self.eq([adef.get('iden') for adef in await core.getHttpExtApis()],
|
|
7763
|
+
[info.get('iden'), info4.get('iden'), info2.get('iden'), info3.get('iden')])
|
|
7764
|
+
|
|
7765
|
+
items = await core.getHttpExtApis()
|
|
7766
|
+
self.eq(items, (info, info4, info2, info3))
|
|
7767
|
+
|
|
7768
|
+
# Tiny sleep to ensure that updated ticks forward when modified
|
|
7769
|
+
created = adef.get('created')
|
|
7770
|
+
updated = adef.get('updated')
|
|
7771
|
+
await asyncio.sleep(0.005)
|
|
7772
|
+
adef = await core.modHttpExtApi(iden, 'name', 'wow')
|
|
7773
|
+
self.eq(adef.get('created'), created)
|
|
7774
|
+
self.gt(adef.get('updated'), updated)
|
|
7775
|
+
|
|
7776
|
+
# Sad path
|
|
7777
|
+
|
|
7778
|
+
with self.raises(s_exc.SynErr):
|
|
7779
|
+
await core.setHttpApiIndx(newp, 0)
|
|
7780
|
+
|
|
7781
|
+
with self.raises(s_exc.BadArg):
|
|
7782
|
+
await core.setHttpApiIndx(newp, -1)
|
|
7783
|
+
|
|
7784
|
+
with self.raises(s_exc.NoSuchUser):
|
|
7785
|
+
await core.modHttpExtApi(iden, 'owner', newp)
|
|
7786
|
+
|
|
7787
|
+
with self.raises(s_exc.NoSuchView):
|
|
7788
|
+
await core.modHttpExtApi(iden, 'view', newp)
|
|
7789
|
+
|
|
7790
|
+
with self.raises(s_exc.BadArg):
|
|
7791
|
+
await core.modHttpExtApi(iden, 'created', 1234)
|
|
7792
|
+
|
|
7793
|
+
with self.raises(s_exc.BadArg):
|
|
7794
|
+
await core.modHttpExtApi(iden, 'updated', 1234)
|
|
7795
|
+
|
|
7796
|
+
with self.raises(s_exc.BadArg):
|
|
7797
|
+
await core.modHttpExtApi(iden, 'creator', s_common.guid())
|
|
7798
|
+
|
|
7799
|
+
with self.raises(s_exc.BadArg):
|
|
7800
|
+
await core.modHttpExtApi(iden, 'newp', newp)
|
|
7801
|
+
|
|
7802
|
+
with self.raises(s_exc.NoSuchIden):
|
|
7803
|
+
await core.modHttpExtApi(newp, 'path', 'a/new/path/')
|
|
7804
|
+
|
|
7805
|
+
with self.raises(s_exc.NoSuchIden):
|
|
7806
|
+
await core.getHttpExtApi(newp)
|
|
7807
|
+
|
|
7808
|
+
self.none(await core.delHttpExtApi(newp))
|
|
7809
|
+
|
|
7810
|
+
with self.raises(s_exc.BadArg):
|
|
7811
|
+
await core.delHttpExtApi('notAGuid')
|