prelude-sdk-beta 1398__tar.gz → 1440__tar.gz
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 prelude-sdk-beta might be problematic. Click here for more details.
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/PKG-INFO +1 -1
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/build_controller.py +8 -2
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/detect_controller.py +17 -4
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/http_controller.py +20 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/iam_controller.py +11 -4
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/jobs_controller.py +13 -2
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/partner_controller.py +1 -13
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/scm_controller.py +177 -16
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/models/account.py +6 -1
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/models/codes.py +112 -36
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/PKG-INFO +1 -1
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/setup.cfg +1 -1
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_build.py +3 -3
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_iam.py +1 -1
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_partner.py +1 -30
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_scm.py +34 -168
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_scm_build.py +3 -3
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/LICENSE +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/README.md +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/__init__.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/__init__.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/export_controller.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/generate_controller.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/probe_controller.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/models/__init__.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/SOURCES.txt +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/dependency_links.txt +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/requires.txt +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/top_level.txt +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/pyproject.toml +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_detect.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_generate.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/test_probe.py +0 -0
- {prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/tests/testutils.py +0 -0
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/build_controller.py
RENAMED
|
@@ -271,7 +271,10 @@ class BuildController(HttpController):
|
|
|
271
271
|
headers=self.account.headers,
|
|
272
272
|
timeout=10,
|
|
273
273
|
)
|
|
274
|
-
|
|
274
|
+
threat_hunt = res.json()
|
|
275
|
+
if self.account.resolve_enums:
|
|
276
|
+
self.resolve_enums(threat_hunt, [(Control, "control")])
|
|
277
|
+
return threat_hunt
|
|
275
278
|
|
|
276
279
|
@verify_credentials
|
|
277
280
|
def update_threat_hunt(
|
|
@@ -296,7 +299,10 @@ class BuildController(HttpController):
|
|
|
296
299
|
headers=self.account.headers,
|
|
297
300
|
timeout=10,
|
|
298
301
|
)
|
|
299
|
-
|
|
302
|
+
threat_hunt = res.json()
|
|
303
|
+
if self.account.resolve_enums:
|
|
304
|
+
self.resolve_enums(threat_hunt, [(Control, "control")])
|
|
305
|
+
return threat_hunt
|
|
300
306
|
|
|
301
307
|
@verify_credentials
|
|
302
308
|
def delete_threat_hunt(self, threat_hunt_id: str):
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/detect_controller.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from prelude_sdk_beta.controllers.http_controller import HttpController
|
|
2
2
|
from prelude_sdk_beta.models.account import verify_credentials
|
|
3
|
+
from prelude_sdk_beta.models.codes import Control, RunCode
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class DetectController(HttpController):
|
|
@@ -59,7 +60,10 @@ class DetectController(HttpController):
|
|
|
59
60
|
params=params,
|
|
60
61
|
timeout=10,
|
|
61
62
|
)
|
|
62
|
-
|
|
63
|
+
endpoints = res.json()
|
|
64
|
+
if self.account.resolve_enums:
|
|
65
|
+
self.resolve_enums(endpoints, [(Control, "control")])
|
|
66
|
+
return endpoints
|
|
63
67
|
|
|
64
68
|
@verify_credentials
|
|
65
69
|
def describe_activity(self, filters: dict, view: str = "protected"):
|
|
@@ -169,7 +173,10 @@ class DetectController(HttpController):
|
|
|
169
173
|
params=filters if filters else {},
|
|
170
174
|
timeout=10,
|
|
171
175
|
)
|
|
172
|
-
|
|
176
|
+
threat_hunts = res.json()
|
|
177
|
+
if self.account.resolve_enums:
|
|
178
|
+
self.resolve_enums(threat_hunts, [(Control, "control")])
|
|
179
|
+
return threat_hunts
|
|
173
180
|
|
|
174
181
|
@verify_credentials
|
|
175
182
|
def get_threat_hunt(self, threat_hunt_id):
|
|
@@ -179,7 +186,10 @@ class DetectController(HttpController):
|
|
|
179
186
|
headers=self.account.headers,
|
|
180
187
|
timeout=10,
|
|
181
188
|
)
|
|
182
|
-
|
|
189
|
+
threat_hunt = res.json()
|
|
190
|
+
if self.account.resolve_enums:
|
|
191
|
+
self.resolve_enums(threat_hunt, [(Control, "control")])
|
|
192
|
+
return threat_hunt
|
|
183
193
|
|
|
184
194
|
@verify_credentials
|
|
185
195
|
def do_threat_hunt(self, threat_hunt_id):
|
|
@@ -214,7 +224,10 @@ class DetectController(HttpController):
|
|
|
214
224
|
json=dict(items=items),
|
|
215
225
|
timeout=10,
|
|
216
226
|
)
|
|
217
|
-
|
|
227
|
+
schedule = res.json()
|
|
228
|
+
if self.account.resolve_enums:
|
|
229
|
+
self.resolve_enums(schedule, [(RunCode, "run_code")])
|
|
230
|
+
return schedule
|
|
218
231
|
|
|
219
232
|
@verify_credentials
|
|
220
233
|
def unschedule(self, items: list):
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/http_controller.py
RENAMED
|
@@ -22,6 +22,26 @@ class HttpController(object):
|
|
|
22
22
|
self._session.mount("http://", HTTPAdapter(max_retries=retry))
|
|
23
23
|
self._session.mount("https://", HTTPAdapter(max_retries=retry))
|
|
24
24
|
|
|
25
|
+
def resolve_enums(self, data, enum_params: list[tuple]):
|
|
26
|
+
for [enum_class, key] in enum_params:
|
|
27
|
+
self._resolve_enum(data, enum_class, key)
|
|
28
|
+
|
|
29
|
+
def _resolve_enum(self, data, enum_class, key):
|
|
30
|
+
if isinstance(data, list):
|
|
31
|
+
for item in data:
|
|
32
|
+
if isinstance(item, dict):
|
|
33
|
+
self._resolve_enum(item, enum_class, key)
|
|
34
|
+
elif isinstance(data, dict):
|
|
35
|
+
for k, v in data.items():
|
|
36
|
+
if k == key:
|
|
37
|
+
if isinstance(v, list):
|
|
38
|
+
for i, item in enumerate(v):
|
|
39
|
+
v[i] = enum_class[item].name
|
|
40
|
+
elif v is not None:
|
|
41
|
+
data[k] = enum_class[v].name
|
|
42
|
+
elif isinstance(v, dict) or isinstance(v, list):
|
|
43
|
+
self._resolve_enum(v, enum_class, key)
|
|
44
|
+
|
|
25
45
|
def get(self, url, retry=True, **kwargs):
|
|
26
46
|
res = self._session.get(url, **kwargs)
|
|
27
47
|
if res.status_code == 200:
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/iam_controller.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from prelude_sdk_beta.controllers.http_controller import HttpController
|
|
2
2
|
from prelude_sdk_beta.models.account import verify_credentials
|
|
3
|
-
from prelude_sdk_beta.models.codes import Mode, Permission
|
|
3
|
+
from prelude_sdk_beta.models.codes import Control, Mode, Permission
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class IAMAccountController(HttpController):
|
|
@@ -14,7 +14,12 @@ class IAMAccountController(HttpController):
|
|
|
14
14
|
res = self.get(
|
|
15
15
|
f"{self.account.hq}/iam/account", headers=self.account.headers, timeout=10
|
|
16
16
|
)
|
|
17
|
-
|
|
17
|
+
account = res.json()
|
|
18
|
+
if self.account.resolve_enums:
|
|
19
|
+
self.resolve_enums(
|
|
20
|
+
account, [(Mode, "mode"), (Permission, "permission"), (Control, "id")]
|
|
21
|
+
)
|
|
22
|
+
return account
|
|
18
23
|
|
|
19
24
|
@verify_credentials
|
|
20
25
|
def purge_account(self):
|
|
@@ -100,7 +105,10 @@ class IAMAccountController(HttpController):
|
|
|
100
105
|
headers=self.account.headers,
|
|
101
106
|
timeout=10,
|
|
102
107
|
)
|
|
103
|
-
|
|
108
|
+
user = res.json()
|
|
109
|
+
if self.account.resolve_enums:
|
|
110
|
+
self.resolve_enums(user, [(Permission, "permission")])
|
|
111
|
+
return user
|
|
104
112
|
|
|
105
113
|
@verify_credentials
|
|
106
114
|
def create_service_user(self, name: str):
|
|
@@ -173,7 +181,6 @@ class IAMAccountController(HttpController):
|
|
|
173
181
|
)
|
|
174
182
|
return res.json()
|
|
175
183
|
|
|
176
|
-
|
|
177
184
|
def sign_up(self, company, email, name):
|
|
178
185
|
"""(NOT AVAIABLE IN PRODUCTION) Create a new user and account"""
|
|
179
186
|
body = dict(company=company, email=email, name=name)
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/jobs_controller.py
RENAMED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from itertools import chain
|
|
2
|
+
|
|
1
3
|
from prelude_sdk_beta.controllers.http_controller import HttpController
|
|
2
4
|
from prelude_sdk_beta.models.account import verify_credentials
|
|
5
|
+
from prelude_sdk_beta.models.codes import BackgroundJobTypes, Control
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
class JobsController(HttpController):
|
|
@@ -13,7 +16,10 @@ class JobsController(HttpController):
|
|
|
13
16
|
res = self.get(
|
|
14
17
|
f"{self.account.hq}/jobs/statuses", headers=self.account.headers, timeout=30
|
|
15
18
|
)
|
|
16
|
-
|
|
19
|
+
jobs = res.json()
|
|
20
|
+
if self.account.resolve_enums:
|
|
21
|
+
self.resolve_enums(jobs, [(Control, "control")])
|
|
22
|
+
return jobs
|
|
17
23
|
|
|
18
24
|
@verify_credentials
|
|
19
25
|
def job_status(self, job_id: str):
|
|
@@ -23,4 +29,9 @@ class JobsController(HttpController):
|
|
|
23
29
|
headers=self.account.headers,
|
|
24
30
|
timeout=30,
|
|
25
31
|
)
|
|
26
|
-
|
|
32
|
+
job = res.json()
|
|
33
|
+
if self.account.resolve_enums:
|
|
34
|
+
self.resolve_enums(
|
|
35
|
+
job, [(Control, "control"), (BackgroundJobTypes, "job_type")]
|
|
36
|
+
)
|
|
37
|
+
return job
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/partner_controller.py
RENAMED
|
@@ -45,7 +45,7 @@ class PartnerController(HttpController):
|
|
|
45
45
|
res = self.delete(
|
|
46
46
|
f"{self.account.hq}/partner/{partner.name}/{instance_id}",
|
|
47
47
|
headers=self.account.headers,
|
|
48
|
-
timeout=
|
|
48
|
+
timeout=30,
|
|
49
49
|
)
|
|
50
50
|
return res.json()
|
|
51
51
|
|
|
@@ -104,18 +104,6 @@ class PartnerController(HttpController):
|
|
|
104
104
|
)
|
|
105
105
|
return res.json()
|
|
106
106
|
|
|
107
|
-
@verify_credentials
|
|
108
|
-
def ioa_stats(self, test_id: str | None = None):
|
|
109
|
-
"""Get IOA stats"""
|
|
110
|
-
params = dict(test_id=test_id) if test_id else dict()
|
|
111
|
-
res = self.get(
|
|
112
|
-
f"{self.account.hq}/partner/ioa_stats",
|
|
113
|
-
headers=self.account.headers,
|
|
114
|
-
json=params,
|
|
115
|
-
timeout=30,
|
|
116
|
-
)
|
|
117
|
-
return res.json()
|
|
118
|
-
|
|
119
107
|
@verify_credentials
|
|
120
108
|
def observed_detected(self, test_id: str | None = None, hours: int | None = None):
|
|
121
109
|
"""Get observed_detected stats"""
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/scm_controller.py
RENAMED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
from prelude_sdk_beta.controllers.http_controller import HttpController
|
|
2
2
|
from prelude_sdk_beta.models.account import verify_credentials
|
|
3
|
-
from prelude_sdk_beta.models.codes import
|
|
3
|
+
from prelude_sdk_beta.models.codes import (
|
|
4
|
+
Control,
|
|
5
|
+
ControlCategory,
|
|
6
|
+
PartnerEvents,
|
|
7
|
+
PolicyType,
|
|
8
|
+
NotationType,
|
|
9
|
+
RunCode,
|
|
10
|
+
SCMCategory,
|
|
11
|
+
)
|
|
4
12
|
|
|
5
13
|
|
|
6
14
|
class ScmController(HttpController):
|
|
7
|
-
default = -1
|
|
15
|
+
default = "-1"
|
|
8
16
|
|
|
9
17
|
def __init__(self, account):
|
|
10
18
|
super().__init__(account)
|
|
@@ -19,7 +27,18 @@ class ScmController(HttpController):
|
|
|
19
27
|
params=params,
|
|
20
28
|
timeout=30,
|
|
21
29
|
)
|
|
22
|
-
|
|
30
|
+
data = res.json()
|
|
31
|
+
if self.account.resolve_enums:
|
|
32
|
+
self.resolve_enums(
|
|
33
|
+
data,
|
|
34
|
+
[
|
|
35
|
+
(Control, "controls"),
|
|
36
|
+
(Control, "control"),
|
|
37
|
+
(ControlCategory, "category"),
|
|
38
|
+
(PartnerEvents, "event"),
|
|
39
|
+
],
|
|
40
|
+
)
|
|
41
|
+
return data
|
|
23
42
|
|
|
24
43
|
@verify_credentials
|
|
25
44
|
def inboxes(self, filter: str = None, orderby: str = None, top: int = None):
|
|
@@ -31,7 +50,41 @@ class ScmController(HttpController):
|
|
|
31
50
|
params=params,
|
|
32
51
|
timeout=30,
|
|
33
52
|
)
|
|
34
|
-
|
|
53
|
+
data = res.json()
|
|
54
|
+
if self.account.resolve_enums:
|
|
55
|
+
self.resolve_enums(
|
|
56
|
+
data,
|
|
57
|
+
[
|
|
58
|
+
(Control, "controls"),
|
|
59
|
+
(Control, "control"),
|
|
60
|
+
(ControlCategory, "category"),
|
|
61
|
+
(PartnerEvents, "event"),
|
|
62
|
+
],
|
|
63
|
+
)
|
|
64
|
+
return data
|
|
65
|
+
|
|
66
|
+
@verify_credentials
|
|
67
|
+
def network_devices(self, filter: str = None, orderby: str = None, top: int = None):
|
|
68
|
+
"""List network_devices with SCM analysis"""
|
|
69
|
+
params = {"$filter": filter, "$orderby": orderby, "$top": top}
|
|
70
|
+
res = self.get(
|
|
71
|
+
f"{self.account.hq}/scm/network_devices",
|
|
72
|
+
headers=self.account.headers,
|
|
73
|
+
params=params,
|
|
74
|
+
timeout=30,
|
|
75
|
+
)
|
|
76
|
+
data = res.json()
|
|
77
|
+
if self.account.resolve_enums:
|
|
78
|
+
self.resolve_enums(
|
|
79
|
+
data,
|
|
80
|
+
[
|
|
81
|
+
(Control, "controls"),
|
|
82
|
+
(Control, "control"),
|
|
83
|
+
(ControlCategory, "category"),
|
|
84
|
+
(PartnerEvents, "event"),
|
|
85
|
+
],
|
|
86
|
+
)
|
|
87
|
+
return data
|
|
35
88
|
|
|
36
89
|
@verify_credentials
|
|
37
90
|
def users(self, filter: str = None, orderby: str = None, top: int = None):
|
|
@@ -43,7 +96,18 @@ class ScmController(HttpController):
|
|
|
43
96
|
params=params,
|
|
44
97
|
timeout=30,
|
|
45
98
|
)
|
|
46
|
-
|
|
99
|
+
data = res.json()
|
|
100
|
+
if self.account.resolve_enums:
|
|
101
|
+
self.resolve_enums(
|
|
102
|
+
data,
|
|
103
|
+
[
|
|
104
|
+
(Control, "controls"),
|
|
105
|
+
(Control, "control"),
|
|
106
|
+
(ControlCategory, "category"),
|
|
107
|
+
(PartnerEvents, "event"),
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
return data
|
|
47
111
|
|
|
48
112
|
@verify_credentials
|
|
49
113
|
def technique_summary(self, techniques: str):
|
|
@@ -78,7 +142,12 @@ class ScmController(HttpController):
|
|
|
78
142
|
headers=self.account.headers,
|
|
79
143
|
timeout=30,
|
|
80
144
|
)
|
|
81
|
-
|
|
145
|
+
data = res.json()
|
|
146
|
+
if self.account.resolve_enums:
|
|
147
|
+
self.resolve_enums(
|
|
148
|
+
data, [(Control, "control"), (ControlCategory, "category")]
|
|
149
|
+
)
|
|
150
|
+
return data
|
|
82
151
|
|
|
83
152
|
@verify_credentials
|
|
84
153
|
def evaluation(
|
|
@@ -86,10 +155,13 @@ class ScmController(HttpController):
|
|
|
86
155
|
partner: Control,
|
|
87
156
|
instance_id: str,
|
|
88
157
|
filter: str = None,
|
|
158
|
+
policy_types: str = None,
|
|
89
159
|
techniques: str = None,
|
|
90
160
|
):
|
|
91
161
|
"""Get policy evaluations for given partner"""
|
|
92
162
|
params = {"$filter": filter}
|
|
163
|
+
if policy_types:
|
|
164
|
+
params["policy_types"] = policy_types
|
|
93
165
|
if techniques:
|
|
94
166
|
params["techniques"] = techniques
|
|
95
167
|
res = self.get(
|
|
@@ -98,7 +170,10 @@ class ScmController(HttpController):
|
|
|
98
170
|
headers=self.account.headers,
|
|
99
171
|
timeout=30,
|
|
100
172
|
)
|
|
101
|
-
|
|
173
|
+
data = res.json()
|
|
174
|
+
if self.account.resolve_enums:
|
|
175
|
+
self.resolve_enums(data, [(PolicyType, "policy_type")])
|
|
176
|
+
return data
|
|
102
177
|
|
|
103
178
|
@verify_credentials
|
|
104
179
|
def update_evaluation(self, partner: Control, instance_id: str):
|
|
@@ -120,7 +195,10 @@ class ScmController(HttpController):
|
|
|
120
195
|
params=params,
|
|
121
196
|
timeout=10,
|
|
122
197
|
)
|
|
123
|
-
|
|
198
|
+
groups = res.json()
|
|
199
|
+
if self.account.resolve_enums:
|
|
200
|
+
self.resolve_enums(groups, [(Control, "control")])
|
|
201
|
+
return groups
|
|
124
202
|
|
|
125
203
|
@verify_credentials
|
|
126
204
|
def update_partner_groups(
|
|
@@ -144,7 +222,10 @@ class ScmController(HttpController):
|
|
|
144
222
|
headers=self.account.headers,
|
|
145
223
|
timeout=10,
|
|
146
224
|
)
|
|
147
|
-
|
|
225
|
+
exceptions = res.json()
|
|
226
|
+
if self.account.resolve_enums:
|
|
227
|
+
self.resolve_enums(exceptions, [(ControlCategory, "category")])
|
|
228
|
+
return exceptions
|
|
148
229
|
|
|
149
230
|
@verify_credentials
|
|
150
231
|
def create_object_exception(
|
|
@@ -202,13 +283,18 @@ class ScmController(HttpController):
|
|
|
202
283
|
headers=self.account.headers,
|
|
203
284
|
timeout=10,
|
|
204
285
|
)
|
|
205
|
-
|
|
286
|
+
exceptions = res.json()
|
|
287
|
+
if self.account.resolve_enums:
|
|
288
|
+
self.resolve_enums(
|
|
289
|
+
exceptions, [(Control, "control"), (ControlCategory, "category")]
|
|
290
|
+
)
|
|
291
|
+
return exceptions
|
|
206
292
|
|
|
207
293
|
@verify_credentials
|
|
208
|
-
def
|
|
209
|
-
self, partner: Control,
|
|
294
|
+
def create_policy_exception(
|
|
295
|
+
self, partner: Control, instance_id: str, policy_id, setting_names, expires=None
|
|
210
296
|
):
|
|
211
|
-
"""
|
|
297
|
+
"""Create policy exceptions"""
|
|
212
298
|
body = dict(
|
|
213
299
|
control=partner.name,
|
|
214
300
|
expires=expires,
|
|
@@ -216,6 +302,29 @@ class ScmController(HttpController):
|
|
|
216
302
|
policy_id=policy_id,
|
|
217
303
|
setting_names=setting_names,
|
|
218
304
|
)
|
|
305
|
+
res = self.post(
|
|
306
|
+
f"{self.account.hq}/scm/exceptions/policies",
|
|
307
|
+
json=body,
|
|
308
|
+
headers=self.account.headers,
|
|
309
|
+
timeout=10,
|
|
310
|
+
)
|
|
311
|
+
return res.json()
|
|
312
|
+
|
|
313
|
+
@verify_credentials
|
|
314
|
+
def update_policy_exception(
|
|
315
|
+
self,
|
|
316
|
+
partner: Control,
|
|
317
|
+
instance_id: str,
|
|
318
|
+
policy_id,
|
|
319
|
+
expires=default,
|
|
320
|
+
setting_names=None,
|
|
321
|
+
):
|
|
322
|
+
"""Update policy exceptions"""
|
|
323
|
+
body = dict(control=partner.name, instance_id=instance_id, policy_id=policy_id)
|
|
324
|
+
if expires != self.default:
|
|
325
|
+
body["expires"] = expires
|
|
326
|
+
if setting_names:
|
|
327
|
+
body["setting_names"] = setting_names
|
|
219
328
|
res = self.put(
|
|
220
329
|
f"{self.account.hq}/scm/exceptions/policies",
|
|
221
330
|
json=body,
|
|
@@ -224,6 +333,18 @@ class ScmController(HttpController):
|
|
|
224
333
|
)
|
|
225
334
|
return res.json()
|
|
226
335
|
|
|
336
|
+
@verify_credentials
|
|
337
|
+
def delete_policy_exception(self, instance_id: str, policy_id: str):
|
|
338
|
+
"""Delete policy exceptions"""
|
|
339
|
+
body = dict(instance_id=instance_id, policy_id=policy_id)
|
|
340
|
+
res = self.delete(
|
|
341
|
+
f"{self.account.hq}/scm/exceptions/policies",
|
|
342
|
+
json=body,
|
|
343
|
+
headers=self.account.headers,
|
|
344
|
+
timeout=10,
|
|
345
|
+
)
|
|
346
|
+
return res.json()
|
|
347
|
+
|
|
227
348
|
@verify_credentials
|
|
228
349
|
def list_views(self):
|
|
229
350
|
"""List views"""
|
|
@@ -232,7 +353,10 @@ class ScmController(HttpController):
|
|
|
232
353
|
headers=self.account.headers,
|
|
233
354
|
timeout=10,
|
|
234
355
|
)
|
|
235
|
-
|
|
356
|
+
views = res.json()
|
|
357
|
+
if self.account.resolve_enums:
|
|
358
|
+
self.resolve_enums(views, [(ControlCategory, "category")])
|
|
359
|
+
return views
|
|
236
360
|
|
|
237
361
|
@verify_credentials
|
|
238
362
|
def create_view(self, category: ControlCategory, filter: str, name: str):
|
|
@@ -371,7 +495,17 @@ class ScmController(HttpController):
|
|
|
371
495
|
headers=self.account.headers,
|
|
372
496
|
timeout=10,
|
|
373
497
|
)
|
|
374
|
-
|
|
498
|
+
notifications = res.json()
|
|
499
|
+
if self.account.resolve_enums:
|
|
500
|
+
self.resolve_enums(
|
|
501
|
+
notifications,
|
|
502
|
+
[
|
|
503
|
+
(ControlCategory, "control_category"),
|
|
504
|
+
(PartnerEvents, "event"),
|
|
505
|
+
(RunCode, "run_code"),
|
|
506
|
+
],
|
|
507
|
+
)
|
|
508
|
+
return notifications
|
|
375
509
|
|
|
376
510
|
@verify_credentials
|
|
377
511
|
def delete_notification(self, notification_id: str):
|
|
@@ -431,4 +565,31 @@ class ScmController(HttpController):
|
|
|
431
565
|
headers=self.account.headers,
|
|
432
566
|
timeout=10,
|
|
433
567
|
)
|
|
434
|
-
|
|
568
|
+
notations = res.json()
|
|
569
|
+
if self.account.resolve_enums:
|
|
570
|
+
self.resolve_enums(notations, [(NotationType, "event")])
|
|
571
|
+
return notations
|
|
572
|
+
|
|
573
|
+
@verify_credentials
|
|
574
|
+
def list_history(
|
|
575
|
+
self, start_date: str = None, end_date: str = None, filter: str = None
|
|
576
|
+
):
|
|
577
|
+
"""List history"""
|
|
578
|
+
params = {"start_date": start_date, "end_date": end_date, "$filter": filter}
|
|
579
|
+
res = self.get(
|
|
580
|
+
f"{self.account.hq}/scm/history",
|
|
581
|
+
headers=self.account.headers,
|
|
582
|
+
params=params,
|
|
583
|
+
timeout=10,
|
|
584
|
+
)
|
|
585
|
+
history = res.json()
|
|
586
|
+
if self.account.resolve_enums:
|
|
587
|
+
self.resolve_enums(
|
|
588
|
+
history,
|
|
589
|
+
[
|
|
590
|
+
(Control, "control"),
|
|
591
|
+
(PartnerEvents, "event"),
|
|
592
|
+
(SCMCategory, "category"),
|
|
593
|
+
],
|
|
594
|
+
)
|
|
595
|
+
return history
|
|
@@ -83,7 +83,7 @@ def exchange_token(
|
|
|
83
83
|
class Account:
|
|
84
84
|
|
|
85
85
|
@staticmethod
|
|
86
|
-
def from_keychain(profile: str = "default"):
|
|
86
|
+
def from_keychain(profile: str = "default", resolve_enums: bool = False):
|
|
87
87
|
"""
|
|
88
88
|
Create an account object from a pre-configured profile in your keychain file
|
|
89
89
|
"""
|
|
@@ -100,6 +100,7 @@ class Account:
|
|
|
100
100
|
oidc=profile_items.get("oidc"),
|
|
101
101
|
profile=profile,
|
|
102
102
|
slug=profile_items.get("slug"),
|
|
103
|
+
resolve_enums=resolve_enums,
|
|
103
104
|
)
|
|
104
105
|
|
|
105
106
|
@staticmethod
|
|
@@ -111,6 +112,7 @@ class Account:
|
|
|
111
112
|
hq: str = "https://api.us1.preludesecurity.com",
|
|
112
113
|
oidc: str | None = None,
|
|
113
114
|
slug: str | None = None,
|
|
115
|
+
resolve_enums: bool = False,
|
|
114
116
|
):
|
|
115
117
|
"""
|
|
116
118
|
Create an account object from an access token or a refresh token
|
|
@@ -131,6 +133,7 @@ class Account:
|
|
|
131
133
|
slug=slug,
|
|
132
134
|
token=token,
|
|
133
135
|
token_location=None,
|
|
136
|
+
resolve_enums=resolve_enums,
|
|
134
137
|
)
|
|
135
138
|
|
|
136
139
|
|
|
@@ -151,6 +154,7 @@ class _Account:
|
|
|
151
154
|
token_location: str | None = os.path.join(
|
|
152
155
|
Path.home(), ".prelude", "tokens.json"
|
|
153
156
|
),
|
|
157
|
+
resolve_enums: bool = False,
|
|
154
158
|
):
|
|
155
159
|
if token is None and token_location is None:
|
|
156
160
|
raise ValueError(
|
|
@@ -168,6 +172,7 @@ class _Account:
|
|
|
168
172
|
self.slug = slug
|
|
169
173
|
self.token = token
|
|
170
174
|
self.token_location = token_location
|
|
175
|
+
self.resolve_enums = resolve_enums
|
|
171
176
|
if self.token_location and not os.path.exists(self.token_location):
|
|
172
177
|
head, _ = os.path.split(Path(self.token_location))
|
|
173
178
|
Path(head).mkdir(parents=True, exist_ok=True)
|
|
@@ -184,6 +184,9 @@ class Control(Enum, metaclass=MissingItem):
|
|
|
184
184
|
QUALYS_DISCOVERY = 24
|
|
185
185
|
RAPID7 = 25
|
|
186
186
|
RAPID7_DISCOVERY = 26
|
|
187
|
+
INTEL_INTUNE = 28
|
|
188
|
+
CISCO_MERAKI = 29
|
|
189
|
+
CISCO_MERAKI_IDENTITY = 30
|
|
187
190
|
|
|
188
191
|
@classmethod
|
|
189
192
|
def _missing_(cls, value):
|
|
@@ -203,6 +206,36 @@ class Control(Enum, metaclass=MissingItem):
|
|
|
203
206
|
return k
|
|
204
207
|
return SCMCategory.NONE
|
|
205
208
|
|
|
209
|
+
@property
|
|
210
|
+
def parent(self):
|
|
211
|
+
match self:
|
|
212
|
+
case Control.CISCO_MERAKI_IDENTITY:
|
|
213
|
+
return Control.CISCO_MERAKI
|
|
214
|
+
case Control.DEFENDER_DISCOVERY:
|
|
215
|
+
return Control.DEFENDER
|
|
216
|
+
case Control.QUALYS_DISCOVERY:
|
|
217
|
+
return Control.QUALYS
|
|
218
|
+
case Control.RAPID7_DISCOVERY:
|
|
219
|
+
return Control.RAPID7
|
|
220
|
+
case Control.TENABLE_DISCOVERY:
|
|
221
|
+
return Control.TENABLE
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def children(self):
|
|
225
|
+
match self:
|
|
226
|
+
case Control.CISCO_MERAKI:
|
|
227
|
+
return [Control.CISCO_MERAKI_IDENTITY]
|
|
228
|
+
case Control.DEFENDER:
|
|
229
|
+
return [Control.DEFENDER_DISCOVERY]
|
|
230
|
+
case Control.QUALYS:
|
|
231
|
+
return [Control.QUALYS_DISCOVERY]
|
|
232
|
+
case Control.RAPID7:
|
|
233
|
+
return [Control.RAPID7_DISCOVERY]
|
|
234
|
+
case Control.TENABLE:
|
|
235
|
+
return [Control.TENABLE_DISCOVERY]
|
|
236
|
+
case _:
|
|
237
|
+
return []
|
|
238
|
+
|
|
206
239
|
|
|
207
240
|
class ControlCategory(Enum, metaclass=MissingItem):
|
|
208
241
|
INVALID = -1
|
|
@@ -217,6 +250,7 @@ class ControlCategory(Enum, metaclass=MissingItem):
|
|
|
217
250
|
VULN_MANAGER = 8
|
|
218
251
|
SIEM = 9
|
|
219
252
|
PRIVATE_REPO = 10
|
|
253
|
+
HARDWARE = 11
|
|
220
254
|
|
|
221
255
|
@classmethod
|
|
222
256
|
def _missing_(cls, value):
|
|
@@ -244,12 +278,16 @@ class ControlCategory(Enum, metaclass=MissingItem):
|
|
|
244
278
|
Control.GMAIL,
|
|
245
279
|
Control.M365,
|
|
246
280
|
],
|
|
281
|
+
ControlCategory.HARDWARE: [
|
|
282
|
+
Control.INTEL_INTUNE,
|
|
283
|
+
],
|
|
247
284
|
ControlCategory.IDENTITY: [
|
|
285
|
+
Control.CISCO_MERAKI_IDENTITY,
|
|
248
286
|
Control.ENTRA,
|
|
249
287
|
Control.GOOGLE_IDENTITY,
|
|
250
288
|
Control.OKTA,
|
|
251
289
|
],
|
|
252
|
-
ControlCategory.NETWORK: [],
|
|
290
|
+
ControlCategory.NETWORK: [Control.CISCO_MERAKI],
|
|
253
291
|
ControlCategory.PRIVATE_REPO: [
|
|
254
292
|
Control.GITHUB,
|
|
255
293
|
],
|
|
@@ -277,6 +315,7 @@ class SCMCategory(Enum, metaclass=MissingItem):
|
|
|
277
315
|
ENDPOINT = 1
|
|
278
316
|
INBOX = 2
|
|
279
317
|
USER = 3
|
|
318
|
+
NETWORK_DEVICE = 4
|
|
280
319
|
|
|
281
320
|
@classmethod
|
|
282
321
|
def _missing_(cls, value):
|
|
@@ -292,6 +331,7 @@ class SCMCategory(Enum, metaclass=MissingItem):
|
|
|
292
331
|
Control.DEFENDER,
|
|
293
332
|
Control.DEFENDER_DISCOVERY,
|
|
294
333
|
Control.EC2,
|
|
334
|
+
Control.INTEL_INTUNE,
|
|
295
335
|
Control.INTUNE,
|
|
296
336
|
Control.JAMF,
|
|
297
337
|
Control.QUALYS,
|
|
@@ -303,15 +343,19 @@ class SCMCategory(Enum, metaclass=MissingItem):
|
|
|
303
343
|
Control.TENABLE,
|
|
304
344
|
Control.TENABLE_DISCOVERY,
|
|
305
345
|
],
|
|
346
|
+
SCMCategory.INBOX: [
|
|
347
|
+
Control.GMAIL,
|
|
348
|
+
Control.M365,
|
|
349
|
+
],
|
|
350
|
+
SCMCategory.NETWORK_DEVICE: [
|
|
351
|
+
Control.CISCO_MERAKI,
|
|
352
|
+
],
|
|
306
353
|
SCMCategory.USER: [
|
|
354
|
+
Control.CISCO_MERAKI_IDENTITY,
|
|
307
355
|
Control.ENTRA,
|
|
308
356
|
Control.GOOGLE_IDENTITY,
|
|
309
357
|
Control.OKTA,
|
|
310
358
|
],
|
|
311
|
-
SCMCategory.INBOX: [
|
|
312
|
-
Control.GMAIL,
|
|
313
|
-
Control.M365,
|
|
314
|
-
],
|
|
315
359
|
}
|
|
316
360
|
|
|
317
361
|
@classmethod
|
|
@@ -320,11 +364,13 @@ class SCMCategory(Enum, metaclass=MissingItem):
|
|
|
320
364
|
SCMCategory.ENDPOINT: [
|
|
321
365
|
ControlCategory.ASSET_MANAGER,
|
|
322
366
|
ControlCategory.DISCOVERED_DEVICES,
|
|
367
|
+
ControlCategory.HARDWARE,
|
|
323
368
|
ControlCategory.VULN_MANAGER,
|
|
324
369
|
ControlCategory.XDR,
|
|
325
370
|
],
|
|
326
|
-
SCMCategory.USER: [ControlCategory.IDENTITY],
|
|
327
371
|
SCMCategory.INBOX: [ControlCategory.EMAIL],
|
|
372
|
+
SCMCategory.NETWORK_DEVICE: [ControlCategory.NETWORK],
|
|
373
|
+
SCMCategory.USER: [ControlCategory.IDENTITY],
|
|
328
374
|
}
|
|
329
375
|
|
|
330
376
|
|
|
@@ -356,19 +402,27 @@ class EDRResponse(Enum, metaclass=MissingItem):
|
|
|
356
402
|
class PartnerEvents(Enum, metaclass=MissingItem):
|
|
357
403
|
INVALID = -1
|
|
358
404
|
REDUCED_FUNCTIONALITY_MODE = 1
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
405
|
+
MISSING_EDR = 2
|
|
406
|
+
NO_EDR_POLICY = 3
|
|
407
|
+
NO_AV_POLICY = 4
|
|
362
408
|
MISSING_MFA = 5
|
|
363
|
-
|
|
409
|
+
MISSING_ASSET_MANAGER = 6
|
|
364
410
|
MISCONFIGURED_POLICY_SETTING = 7
|
|
365
|
-
|
|
411
|
+
MISSING_VULN_SCAN = 8
|
|
366
412
|
OUT_OF_DATE_SCAN = 9
|
|
367
|
-
|
|
413
|
+
MISSING_VULN_MANAGER = 10
|
|
368
414
|
USER_MISSING_ASSET_MANAGER = 11
|
|
369
415
|
USER_MISSING_EDR = 12
|
|
370
416
|
USER_MISSING_VULN_MANAGER = 13
|
|
371
|
-
|
|
417
|
+
MISSING_SERVER_MANAGER = 14
|
|
418
|
+
NO_HOST_FIREWALL_POLICY = 16
|
|
419
|
+
OUT_OF_DATE_FIRMWARE = 18
|
|
420
|
+
NO_DISK_ENCRYPTION_POLICY = 19
|
|
421
|
+
NO_DISK_ENCRYPTION = 20
|
|
422
|
+
NO_REGISTERED_DEVICES = 21
|
|
423
|
+
NO_DEVICE_COMPLIANCE_POLICY = 22
|
|
424
|
+
NONCOMPLIANT = 23
|
|
425
|
+
NO_ASR_POLICY = 24
|
|
372
426
|
|
|
373
427
|
@classmethod
|
|
374
428
|
def _missing_(cls, value):
|
|
@@ -377,46 +431,61 @@ class PartnerEvents(Enum, metaclass=MissingItem):
|
|
|
377
431
|
@classmethod
|
|
378
432
|
def control_category_mapping(cls):
|
|
379
433
|
return {
|
|
380
|
-
PartnerEvents.REDUCED_FUNCTIONALITY_MODE: [ControlCategory.XDR],
|
|
381
|
-
PartnerEvents.NO_EDR: [
|
|
382
|
-
ControlCategory.XDR,
|
|
383
|
-
],
|
|
384
|
-
PartnerEvents.MISSING_EDR_POLICY: [ControlCategory.XDR],
|
|
385
|
-
PartnerEvents.MISSING_AV_POLICY: [ControlCategory.XDR],
|
|
386
|
-
PartnerEvents.MISSING_MFA: [ControlCategory.IDENTITY],
|
|
387
|
-
PartnerEvents.NO_ASSET_MANAGER: [ControlCategory.ASSET_MANAGER],
|
|
388
434
|
PartnerEvents.MISCONFIGURED_POLICY_SETTING: [
|
|
389
|
-
ControlCategory.
|
|
435
|
+
ControlCategory.ASSET_MANAGER,
|
|
390
436
|
ControlCategory.EMAIL,
|
|
391
437
|
ControlCategory.IDENTITY,
|
|
438
|
+
ControlCategory.XDR,
|
|
392
439
|
],
|
|
393
|
-
PartnerEvents.
|
|
440
|
+
PartnerEvents.MISSING_ASSET_MANAGER: [ControlCategory.ASSET_MANAGER],
|
|
441
|
+
PartnerEvents.MISSING_EDR: [ControlCategory.XDR],
|
|
442
|
+
PartnerEvents.MISSING_MFA: [ControlCategory.IDENTITY],
|
|
443
|
+
PartnerEvents.MISSING_SERVER_MANAGER: [ControlCategory.ASSET_MANAGER],
|
|
444
|
+
PartnerEvents.MISSING_VULN_MANAGER: [ControlCategory.VULN_MANAGER],
|
|
445
|
+
PartnerEvents.MISSING_VULN_SCAN: [ControlCategory.VULN_MANAGER],
|
|
446
|
+
PartnerEvents.NO_ASR_POLICY: [ControlCategory.ASSET_MANAGER],
|
|
447
|
+
PartnerEvents.NO_AV_POLICY: [ControlCategory.XDR],
|
|
448
|
+
PartnerEvents.NO_DEVICE_COMPLIANCE_POLICY: [ControlCategory.ASSET_MANAGER],
|
|
449
|
+
PartnerEvents.NO_DISK_ENCRYPTION: [ControlCategory.ASSET_MANAGER],
|
|
450
|
+
PartnerEvents.NO_DISK_ENCRYPTION_POLICY: [ControlCategory.ASSET_MANAGER],
|
|
451
|
+
PartnerEvents.NO_EDR_POLICY: [ControlCategory.XDR],
|
|
452
|
+
PartnerEvents.NO_HOST_FIREWALL_POLICY: [ControlCategory.ASSET_MANAGER],
|
|
453
|
+
PartnerEvents.NO_REGISTERED_DEVICES: [ControlCategory.IDENTITY],
|
|
454
|
+
PartnerEvents.NONCOMPLIANT: [ControlCategory.ASSET_MANAGER],
|
|
455
|
+
PartnerEvents.OUT_OF_DATE_FIRMWARE: [ControlCategory.NETWORK],
|
|
394
456
|
PartnerEvents.OUT_OF_DATE_SCAN: [ControlCategory.VULN_MANAGER],
|
|
395
|
-
PartnerEvents.
|
|
457
|
+
PartnerEvents.REDUCED_FUNCTIONALITY_MODE: [ControlCategory.XDR],
|
|
396
458
|
PartnerEvents.USER_MISSING_ASSET_MANAGER: [ControlCategory.IDENTITY],
|
|
397
459
|
PartnerEvents.USER_MISSING_EDR: [ControlCategory.IDENTITY],
|
|
398
460
|
PartnerEvents.USER_MISSING_VULN_MANAGER: [ControlCategory.IDENTITY],
|
|
399
|
-
PartnerEvents.NO_SERVER_MANAGER: [ControlCategory.ASSET_MANAGER],
|
|
400
461
|
}
|
|
401
462
|
|
|
402
463
|
|
|
403
464
|
class AlertTypes(Enum, metaclass=MissingItem):
|
|
404
465
|
INVALID = -1
|
|
405
466
|
NEW_REDUCED_FUNCTIONALITY_MODE_ENDPOINTS = 1
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
467
|
+
NEW_MISSING_EDR_ENDPOINTS = 2
|
|
468
|
+
NEW_NO_EDR_POLICY_ENDPOINTS = 3
|
|
469
|
+
NEW_NO_AV_POLICY_ENDPOINTS = 4
|
|
409
470
|
NEW_MISSING_MFA_USERS = 5
|
|
410
|
-
|
|
471
|
+
NEW_MISSING_ASSET_MANAGER_ENDPOINTS = 6
|
|
411
472
|
NEW_POLICY_SETTING_FAILURE = 7
|
|
412
473
|
NEW_POLICY_SETTING_PASS = 8
|
|
413
|
-
|
|
414
|
-
|
|
474
|
+
NEW_MISSING_VULN_SCAN_ENDPOINTS = 9
|
|
475
|
+
NEW_MISSING_VULN_MANAGER_ENDPOINTS = 10
|
|
415
476
|
NEW_OUT_OF_DATE_SCAN_ENDPOINTS = 11
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
477
|
+
NEW_MISSING_ASSET_MANAGER_USERS = 12
|
|
478
|
+
NEW_MISSING_EDR_USERS = 13
|
|
479
|
+
NEW_MISSING_VULN_MANAGER_USERS = 14
|
|
480
|
+
NEW_MISSING_SERVER_MANAGER_ENDPOINTS = 15
|
|
481
|
+
NEW_NO_HOST_FIREWALL_POLICY_ENDPOINTS = 17
|
|
482
|
+
NEW_OUT_OF_DATE_FIRMWARE_NETWORK_DEVICES = 19
|
|
483
|
+
NEW_NO_DISK_ENCRYPTION_POLICY_ENDPOINTS = 20
|
|
484
|
+
NEW_NO_DISK_ENCRYPTION_ENDPOINTS = 21
|
|
485
|
+
NEW_NO_REGISTERED_DEVICES_USERS = 22
|
|
486
|
+
NEW_NO_DEVICE_COMPLIANCE_POLICY_ENDPOINTS = 23
|
|
487
|
+
NEW_NONCOMPLIANT_ENDPOINTS = 24
|
|
488
|
+
NEW_NO_ASR_POLICY_ENDPOINTS = 25
|
|
420
489
|
|
|
421
490
|
@classmethod
|
|
422
491
|
def _missing_(cls, value):
|
|
@@ -437,12 +506,19 @@ class PolicyType(Enum, metaclass=MissingItem):
|
|
|
437
506
|
EMAIL_DKIM = 10
|
|
438
507
|
DEVICE_COMPLIANCE = 11
|
|
439
508
|
IDENTITY_MFA = 12
|
|
509
|
+
HOST_FIREWALL = 13
|
|
510
|
+
NETWORK_FIREWALL = 15
|
|
511
|
+
INTEL_BELOW_OS = 16
|
|
512
|
+
INTEL_OS = 17
|
|
513
|
+
INTEL_TDT = 18
|
|
514
|
+
INTEL_CHIP = 19
|
|
515
|
+
DISK_ENCRYPTION = 20
|
|
516
|
+
ASR = 21
|
|
440
517
|
|
|
441
518
|
@classmethod
|
|
442
519
|
def _missing_(cls, value):
|
|
443
520
|
return PolicyType.INVALID
|
|
444
521
|
|
|
445
|
-
|
|
446
522
|
class Platform(Enum, metaclass=MissingItem):
|
|
447
523
|
INVALID = 0
|
|
448
524
|
WINDOWS = 1
|
|
@@ -365,7 +365,7 @@ class TestThreatHunt:
|
|
|
365
365
|
control=Control.CROWDSTRIKE.value,
|
|
366
366
|
id=pytest.crwd_threat_hunt_id,
|
|
367
367
|
name="test CRWD threat hunt",
|
|
368
|
-
query="
|
|
368
|
+
query="#repo=base_sensor | ContextImageFileName = /prelude_dropper.exe/",
|
|
369
369
|
test_id=pytest.test_id,
|
|
370
370
|
)
|
|
371
371
|
|
|
@@ -394,13 +394,13 @@ class TestThreatHunt:
|
|
|
394
394
|
pytest.expected_threat_hunt = unwrap(self.build.update_threat_hunt)(
|
|
395
395
|
self.build,
|
|
396
396
|
name="updated threat hunt",
|
|
397
|
-
query=
|
|
397
|
+
query="#repo=base_sensor | FilePath = /Prelude Security/ | groupBy([@timestamp, ParentBaseFileName, ImageFileName, aid], limit=20)| sort(@timestamp, limit=20)",
|
|
398
398
|
threat_hunt_id=pytest.crwd_threat_hunt_id,
|
|
399
399
|
)
|
|
400
400
|
assert pytest.expected_threat_hunt["name"] == "updated threat hunt"
|
|
401
401
|
assert (
|
|
402
402
|
pytest.expected_threat_hunt["query"]
|
|
403
|
-
==
|
|
403
|
+
== "#repo=base_sensor | FilePath = /Prelude Security/ | groupBy([@timestamp, ParentBaseFileName, ImageFileName, aid], limit=20)| sort(@timestamp, limit=20)"
|
|
404
404
|
)
|
|
405
405
|
|
|
406
406
|
@pytest.mark.order(-7)
|
|
@@ -74,7 +74,7 @@ class TestIAM:
|
|
|
74
74
|
unwrap(self.iam_user.update_user)(self.iam_user, name="Robb")
|
|
75
75
|
|
|
76
76
|
for user in pytest.expected_account["users"]:
|
|
77
|
-
if user["handle"] == pytest.account.handle:
|
|
77
|
+
if user["handle"] == pytest.account.handle and not user["oidc"]:
|
|
78
78
|
user["name"] = "Robb"
|
|
79
79
|
break
|
|
80
80
|
|
|
@@ -100,7 +100,7 @@ class TestPartnerAttach:
|
|
|
100
100
|
),
|
|
101
101
|
)
|
|
102
102
|
for c in Control
|
|
103
|
-
if c.value > 0
|
|
103
|
+
if c.value > 0 and not c.parent
|
|
104
104
|
]
|
|
105
105
|
|
|
106
106
|
def setup_class(self):
|
|
@@ -431,11 +431,8 @@ class TestPartner:
|
|
|
431
431
|
)
|
|
432
432
|
assert 1 == len(res)
|
|
433
433
|
expected = dict(
|
|
434
|
-
blocked=0,
|
|
435
|
-
detected=0,
|
|
436
434
|
detection_id=pytest.detection_id,
|
|
437
435
|
group_id=group_id,
|
|
438
|
-
monitored=0,
|
|
439
436
|
platform=pytest.expected_detection["rule"]["logsource"]["product"],
|
|
440
437
|
test_id=pytest.test_id,
|
|
441
438
|
)
|
|
@@ -471,32 +468,6 @@ class TestPartner:
|
|
|
471
468
|
res[control.name][0]["account_id"] == pytest.expected_account["account_id"]
|
|
472
469
|
)
|
|
473
470
|
|
|
474
|
-
def test_ioa_stats(
|
|
475
|
-
self,
|
|
476
|
-
unwrap,
|
|
477
|
-
host,
|
|
478
|
-
edr_id,
|
|
479
|
-
control,
|
|
480
|
-
os,
|
|
481
|
-
platform,
|
|
482
|
-
policy,
|
|
483
|
-
policy_name,
|
|
484
|
-
webhook_keys,
|
|
485
|
-
group_id,
|
|
486
|
-
):
|
|
487
|
-
try:
|
|
488
|
-
if control != Control.CROWDSTRIKE:
|
|
489
|
-
pytest.skip("IOA stats only supported for CROWDSTRIKE")
|
|
490
|
-
if not pytest.expected_account["features"]["observed_detected"]:
|
|
491
|
-
pytest.skip("OBSERVED_DETECTED feature not enabled")
|
|
492
|
-
|
|
493
|
-
res = unwrap(self.partner.ioa_stats)(self.partner)
|
|
494
|
-
assert 0 == len(res)
|
|
495
|
-
finally:
|
|
496
|
-
unwrap(self.detect.delete_endpoint)(
|
|
497
|
-
self.detect, ident=pytest.endpoint["endpoint_id"]
|
|
498
|
-
)
|
|
499
|
-
|
|
500
471
|
def test_list_advisories(
|
|
501
472
|
self,
|
|
502
473
|
unwrap,
|
|
@@ -33,7 +33,7 @@ class TestScmAcrossControls:
|
|
|
33
33
|
unwrap(self.scm.upsert_notification)(
|
|
34
34
|
self.scm,
|
|
35
35
|
ControlCategory.XDR,
|
|
36
|
-
PartnerEvents.
|
|
36
|
+
PartnerEvents.MISSING_EDR,
|
|
37
37
|
RunCode.DAILY,
|
|
38
38
|
0,
|
|
39
39
|
["test@email.com"],
|
|
@@ -43,7 +43,7 @@ class TestScmAcrossControls:
|
|
|
43
43
|
for notification in notifications:
|
|
44
44
|
if notification["id"] == self.notification_id:
|
|
45
45
|
assert notification["scheduled_hour"] == 0
|
|
46
|
-
assert notification["event"] == PartnerEvents.
|
|
46
|
+
assert notification["event"] == PartnerEvents.MISSING_EDR.value
|
|
47
47
|
|
|
48
48
|
def test_update_notification(self, unwrap):
|
|
49
49
|
unwrap(self.scm.upsert_notification)(
|
|
@@ -89,158 +89,13 @@ class TestScmAcrossControls:
|
|
|
89
89
|
_compare_keys(nested_expected, nested_actual)
|
|
90
90
|
|
|
91
91
|
summary = unwrap(self.scm.evaluation_summary)(self.scm)
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"inbox_count": None,
|
|
98
|
-
"excepted": {
|
|
99
|
-
"inbox_count": None,
|
|
100
|
-
},
|
|
101
|
-
"instances": [
|
|
102
|
-
{
|
|
103
|
-
"control": None,
|
|
104
|
-
"inbox_count": None,
|
|
105
|
-
"instance_id": None,
|
|
106
|
-
"setting_count": None,
|
|
107
|
-
"setting_misconfiguration_count": None,
|
|
108
|
-
"excepted": {
|
|
109
|
-
"inbox_count": None,
|
|
110
|
-
"setting_misconfiguration_count": None,
|
|
111
|
-
},
|
|
112
|
-
}
|
|
113
|
-
],
|
|
114
|
-
}
|
|
115
|
-
],
|
|
116
|
-
"inbox_count": None,
|
|
117
|
-
"excepted": {
|
|
118
|
-
"inbox_count": None,
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
"user_summary": {
|
|
122
|
-
"categories": [
|
|
123
|
-
{
|
|
124
|
-
"category": None,
|
|
125
|
-
"control_failure_count": None,
|
|
126
|
-
"any_endpoint_failure_count": None,
|
|
127
|
-
"all_endpoint_failure_count": None,
|
|
128
|
-
"user_count": None,
|
|
129
|
-
"excepted": {
|
|
130
|
-
"control_failure_count": None,
|
|
131
|
-
"any_endpoint_failure_count": None,
|
|
132
|
-
"all_endpoint_failure_count": None,
|
|
133
|
-
"user_count": None,
|
|
134
|
-
},
|
|
135
|
-
"instances": [
|
|
136
|
-
{
|
|
137
|
-
"control": None,
|
|
138
|
-
"instance_id": None,
|
|
139
|
-
"control_failure_count": None,
|
|
140
|
-
"missing_mfa_count": None,
|
|
141
|
-
"user_count": None,
|
|
142
|
-
"missing_asset_manager_count": None,
|
|
143
|
-
"missing_edr_count": None,
|
|
144
|
-
"missing_vuln_manager_count": None,
|
|
145
|
-
"any_endpoint_failure_count": None,
|
|
146
|
-
"all_endpoint_failure_count": None,
|
|
147
|
-
"setting_count": None,
|
|
148
|
-
"setting_misconfiguration_count": None,
|
|
149
|
-
"excepted": {
|
|
150
|
-
"control_failure_count": None,
|
|
151
|
-
"missing_mfa_count": None,
|
|
152
|
-
"user_count": None,
|
|
153
|
-
"missing_asset_manager_count": None,
|
|
154
|
-
"missing_edr_count": None,
|
|
155
|
-
"missing_vuln_manager_count": None,
|
|
156
|
-
"any_endpoint_failure_count": None,
|
|
157
|
-
"all_endpoint_failure_count": None,
|
|
158
|
-
"setting_misconfiguration_count": None,
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
}
|
|
163
|
-
],
|
|
164
|
-
"user_count": None,
|
|
165
|
-
"control_failure_count": None,
|
|
166
|
-
"any_endpoint_failure_count": None,
|
|
167
|
-
"all_endpoint_failure_count": None,
|
|
168
|
-
"excepted": {
|
|
169
|
-
"user_count": None,
|
|
170
|
-
"control_failure_count": None,
|
|
171
|
-
"any_endpoint_failure_count": None,
|
|
172
|
-
"all_endpoint_failure_count": None,
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
"endpoint_summary": {
|
|
176
|
-
"categories": [
|
|
177
|
-
{
|
|
178
|
-
"category": None,
|
|
179
|
-
"control_failure_count": None,
|
|
180
|
-
"endpoint_count": None,
|
|
181
|
-
"missing_asset_manager_count": None,
|
|
182
|
-
"missing_server_manager_count": None,
|
|
183
|
-
"missing_edr_count": None,
|
|
184
|
-
"missing_vuln_manager_count": None,
|
|
185
|
-
"missing_vuln_scan_count": None,
|
|
186
|
-
"excepted": {
|
|
187
|
-
"control_failure_count": None,
|
|
188
|
-
"endpoint_count": None,
|
|
189
|
-
"missing_asset_manager_count": None,
|
|
190
|
-
"missing_server_manager_count": None,
|
|
191
|
-
"missing_edr_count": None,
|
|
192
|
-
"missing_vuln_manager_count": None,
|
|
193
|
-
"missing_vuln_scan_count": None,
|
|
194
|
-
},
|
|
195
|
-
"instances": [
|
|
196
|
-
{
|
|
197
|
-
"control": None,
|
|
198
|
-
"instance_id": None,
|
|
199
|
-
"control_failure_count": None,
|
|
200
|
-
"endpoint_count": None,
|
|
201
|
-
"no_av_policy": None,
|
|
202
|
-
"no_edr_policy": None,
|
|
203
|
-
"policy_conflict_count": None,
|
|
204
|
-
"reduced_functionality_mode": None,
|
|
205
|
-
"missing_agent_count": None,
|
|
206
|
-
"missing_scan_count": None,
|
|
207
|
-
"out_of_date_scan_count": None,
|
|
208
|
-
"setting_count": None,
|
|
209
|
-
"setting_misconfiguration_count": None,
|
|
210
|
-
"excepted": {
|
|
211
|
-
"control_failure_count": None,
|
|
212
|
-
"endpoint_count": None,
|
|
213
|
-
"no_av_policy": None,
|
|
214
|
-
"no_edr_policy": None,
|
|
215
|
-
"reduced_functionality_mode": None,
|
|
216
|
-
"missing_agent_count": None,
|
|
217
|
-
"missing_scan_count": None,
|
|
218
|
-
"out_of_date_scan_count": None,
|
|
219
|
-
"setting_misconfiguration_count": None,
|
|
220
|
-
},
|
|
221
|
-
}
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
"endpoint_count": None,
|
|
226
|
-
"missing_asset_manager_count": None,
|
|
227
|
-
"missing_server_manager_count": None,
|
|
228
|
-
"missing_edr_count": None,
|
|
229
|
-
"missing_vuln_manager_count": None,
|
|
230
|
-
"missing_vuln_scan_count": None,
|
|
231
|
-
"excepted": {
|
|
232
|
-
"endpoint_count": None,
|
|
233
|
-
"missing_asset_manager_count": None,
|
|
234
|
-
"missing_server_manager_count": None,
|
|
235
|
-
"missing_edr_count": None,
|
|
236
|
-
"missing_vuln_manager_count": None,
|
|
237
|
-
"missing_vuln_scan_count": None,
|
|
238
|
-
},
|
|
239
|
-
},
|
|
92
|
+
assert summary.keys() == {
|
|
93
|
+
"endpoint_summary",
|
|
94
|
+
"inbox_summary",
|
|
95
|
+
"network_device_summary",
|
|
96
|
+
"user_summary",
|
|
240
97
|
}
|
|
241
98
|
|
|
242
|
-
_compare_keys(expected, summary)
|
|
243
|
-
|
|
244
99
|
def test_technique_summary(self, unwrap):
|
|
245
100
|
summary = unwrap(self.scm.technique_summary)(self.scm, "T1078,T1027")
|
|
246
101
|
assert len(summary) > 0
|
|
@@ -261,7 +116,7 @@ class TestScmAcrossControls:
|
|
|
261
116
|
job_id = unwrap(self.export.export_scm)(
|
|
262
117
|
self.export,
|
|
263
118
|
SCMCategory.ENDPOINT,
|
|
264
|
-
filter="contains(
|
|
119
|
+
filter="contains(hostname, 'spencer')",
|
|
265
120
|
top=1,
|
|
266
121
|
)["job_id"]
|
|
267
122
|
while (result := unwrap(self.jobs.job_status)(self.jobs, job_id))[
|
|
@@ -276,7 +131,8 @@ class TestScmAcrossControls:
|
|
|
276
131
|
@pytest.mark.order(9)
|
|
277
132
|
@pytest.mark.usefixtures("setup_account")
|
|
278
133
|
@pytest.mark.parametrize(
|
|
279
|
-
"control",
|
|
134
|
+
"control",
|
|
135
|
+
[c for c in Control if c.scm_category != SCMCategory.NONE and not c.parent],
|
|
280
136
|
)
|
|
281
137
|
class TestScmPerControl:
|
|
282
138
|
def setup_class(self):
|
|
@@ -313,15 +169,29 @@ class TestScmPerControl:
|
|
|
313
169
|
raise e
|
|
314
170
|
|
|
315
171
|
def test_evaluation(self, unwrap, control):
|
|
172
|
+
def _wait_for_policies(control, instance_id, category):
|
|
173
|
+
timeout = time.time() + 300
|
|
174
|
+
while time.time() < timeout:
|
|
175
|
+
evaluation = unwrap(self.scm.evaluation)(self.scm, control, instance_id)
|
|
176
|
+
evaluation = evaluation[f"{category}_evaluation"]
|
|
177
|
+
if len(evaluation["policies"]) > 0:
|
|
178
|
+
return evaluation
|
|
179
|
+
time.sleep(5)
|
|
180
|
+
assert False, "Timed out waiting for policies to show up in evaluation"
|
|
181
|
+
|
|
316
182
|
instance_id = pytest.controls.get(control.value)
|
|
317
183
|
assert instance_id
|
|
318
184
|
evaluation = unwrap(self.scm.evaluation)(self.scm, control, instance_id)
|
|
319
185
|
if "endpoint_evaluation" in evaluation:
|
|
320
186
|
evaluation = evaluation["endpoint_evaluation"]
|
|
321
187
|
assert {"policies"} == evaluation.keys()
|
|
322
|
-
if
|
|
323
|
-
|
|
188
|
+
if (
|
|
189
|
+
control.control_category == ControlCategory.XDR
|
|
190
|
+
or control == Control.INTUNE
|
|
191
|
+
):
|
|
192
|
+
evaluation = _wait_for_policies(control, instance_id, "endpoint")
|
|
324
193
|
assert {
|
|
194
|
+
"excepted",
|
|
325
195
|
"id",
|
|
326
196
|
"name",
|
|
327
197
|
"platform",
|
|
@@ -335,21 +205,17 @@ class TestScmPerControl:
|
|
|
335
205
|
elif "user_evaluation" in evaluation:
|
|
336
206
|
evaluation = evaluation["user_evaluation"]
|
|
337
207
|
assert {"policies"} == evaluation.keys()
|
|
338
|
-
|
|
339
|
-
assert {
|
|
340
|
-
"
|
|
341
|
-
|
|
342
|
-
"noncompliant_hostnames",
|
|
343
|
-
"settings",
|
|
344
|
-
"user_count",
|
|
345
|
-
} == evaluation["policies"][0].keys()
|
|
208
|
+
evaluation = _wait_for_policies(control, instance_id, "user")
|
|
209
|
+
assert {"excepted", "id", "name", "settings", "user_count"} == evaluation[
|
|
210
|
+
"policies"
|
|
211
|
+
][0].keys()
|
|
346
212
|
elif "inbox_evaluation" in evaluation:
|
|
347
213
|
evaluation = evaluation["inbox_evaluation"]
|
|
348
214
|
assert {"policies"} == evaluation.keys()
|
|
349
|
-
|
|
350
|
-
assert {"id", "name", "settings", "inbox_count"} == evaluation[
|
|
351
|
-
|
|
352
|
-
].keys()
|
|
215
|
+
evaluation = _wait_for_policies(control, instance_id, "inbox")
|
|
216
|
+
assert {"excepted", "id", "name", "settings", "inbox_count"} == evaluation[
|
|
217
|
+
"policies"
|
|
218
|
+
][0].keys()
|
|
353
219
|
else:
|
|
354
220
|
assert False, "No evaluation returned"
|
|
355
221
|
|
|
@@ -17,7 +17,7 @@ class TestScmBuild:
|
|
|
17
17
|
res = unwrap(self.scm.create_object_exception)(
|
|
18
18
|
self.scm,
|
|
19
19
|
ControlCategory.ASSET_MANAGER,
|
|
20
|
-
"
|
|
20
|
+
"hostname eq 'host1'",
|
|
21
21
|
name="filter me",
|
|
22
22
|
expires="5555-05-05",
|
|
23
23
|
)
|
|
@@ -28,7 +28,7 @@ class TestScmBuild:
|
|
|
28
28
|
res = unwrap(self.scm.update_object_exception)(
|
|
29
29
|
self.scm,
|
|
30
30
|
pytest.exception_id,
|
|
31
|
-
filter="
|
|
31
|
+
filter="hostname eq 'host2'",
|
|
32
32
|
expires=None,
|
|
33
33
|
)
|
|
34
34
|
assert res["status"]
|
|
@@ -43,7 +43,7 @@ class TestScmBuild:
|
|
|
43
43
|
assert exception == {
|
|
44
44
|
"category": ControlCategory.ASSET_MANAGER.value,
|
|
45
45
|
"expires": None,
|
|
46
|
-
"filter": "
|
|
46
|
+
"filter": "hostname eq 'host2'",
|
|
47
47
|
"id": pytest.exception_id,
|
|
48
48
|
"name": "filter me",
|
|
49
49
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/export_controller.py
RENAMED
|
File without changes
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/generate_controller.py
RENAMED
|
File without changes
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta/controllers/probe_controller.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{prelude_sdk_beta-1398 → prelude_sdk_beta-1440}/prelude_sdk_beta.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|