prelude-sdk-beta 1447__py3-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.
File without changes
File without changes
@@ -0,0 +1,315 @@
1
+ import urllib
2
+
3
+ from prelude_sdk_beta.controllers.http_controller import HttpController
4
+ from prelude_sdk_beta.models.account import verify_credentials
5
+ from prelude_sdk_beta.models.codes import Control, EDRResponse
6
+
7
+
8
+ class BuildController(HttpController):
9
+
10
+ def __init__(self, account):
11
+ super().__init__(account)
12
+
13
+ @verify_credentials
14
+ def clone_test(self, source_test_id):
15
+ """Clone a test"""
16
+ res = self.post(
17
+ f"{self.account.hq}/build/tests",
18
+ json=dict(source_test_id=source_test_id),
19
+ headers=self.account.headers,
20
+ timeout=10,
21
+ )
22
+ return res.json()
23
+
24
+ @verify_credentials
25
+ def create_test(self, name, unit, technique=None, test_id=None):
26
+ """Create or update a test"""
27
+ body = dict(name=name, unit=unit)
28
+ if technique:
29
+ body["technique"] = technique
30
+ if test_id:
31
+ body["id"] = test_id
32
+
33
+ res = self.post(
34
+ f"{self.account.hq}/build/tests",
35
+ json=body,
36
+ headers=self.account.headers,
37
+ timeout=10,
38
+ )
39
+ return res.json()
40
+
41
+ @verify_credentials
42
+ def update_test(
43
+ self,
44
+ test_id,
45
+ name=None,
46
+ unit=None,
47
+ technique=None,
48
+ crowdstrike_expected_outcome: EDRResponse = None,
49
+ ):
50
+ """Update a test"""
51
+ body = dict()
52
+ if crowdstrike_expected_outcome:
53
+ body["expected"] = dict(crowdstrike=crowdstrike_expected_outcome.value)
54
+ if name:
55
+ body["name"] = name
56
+ if unit:
57
+ body["unit"] = unit
58
+ if technique is not None:
59
+ body["technique"] = technique
60
+
61
+ res = self.post(
62
+ f"{self.account.hq}/build/tests/{test_id}",
63
+ json=body,
64
+ headers=self.account.headers,
65
+ timeout=10,
66
+ )
67
+ return res.json()
68
+
69
+ @verify_credentials
70
+ def delete_test(self, test_id, purge):
71
+ """Delete an existing test"""
72
+ res = self.delete(
73
+ f"{self.account.hq}/build/tests/{test_id}",
74
+ json=dict(purge=purge),
75
+ headers=self.account.headers,
76
+ timeout=10,
77
+ )
78
+ return res.json()
79
+
80
+ @verify_credentials
81
+ def undelete_test(self, test_id):
82
+ """Undelete a tombstoned test"""
83
+ res = self.post(
84
+ f"{self.account.hq}/build/tests/{test_id}/undelete",
85
+ headers=self.account.headers,
86
+ timeout=10,
87
+ )
88
+ return res.json()
89
+
90
+ @verify_credentials
91
+ def upload(self, test_id, filename, data, skip_compile=False):
92
+ """Upload a test or attachment"""
93
+ if len(data) > 1000000:
94
+ raise ValueError(f"File size must be under 1MB ({filename})")
95
+
96
+ h = self.account.headers | {"Content-Type": "application/octet-stream"}
97
+ query_params = ""
98
+ if skip_compile:
99
+ query_params = "?" + urllib.parse.urlencode(dict(skip_compile=True))
100
+ res = self.post(
101
+ f"{self.account.hq}/build/tests/{test_id}/{filename}{query_params}",
102
+ data=data,
103
+ headers=h,
104
+ timeout=10,
105
+ )
106
+ return res.json()
107
+
108
+ @verify_credentials
109
+ def compile_code_string(self, code: str, source_test_id: str = None):
110
+ """Compile a code string"""
111
+ res = self.post(
112
+ f"{self.account.hq}/build/compile",
113
+ json=dict(code=code, source_test_id=source_test_id),
114
+ headers=self.account.headers,
115
+ timeout=10,
116
+ )
117
+ return res.json()
118
+
119
+ @verify_credentials
120
+ def get_compile_status(self, job_id):
121
+ res = self.get(
122
+ f"{self.account.hq}/build/compile/{job_id}",
123
+ headers=self.account.headers,
124
+ timeout=10,
125
+ )
126
+ return res.json()
127
+
128
+ @verify_credentials
129
+ def create_threat(
130
+ self, name, published, threat_id=None, source_id=None, source=None, tests=None
131
+ ):
132
+ """Create a threat"""
133
+ body = dict(name=name, published=published)
134
+ if threat_id:
135
+ body["id"] = threat_id
136
+ if source_id:
137
+ body["source_id"] = source_id
138
+ if source:
139
+ body["source"] = source
140
+ if tests:
141
+ body["tests"] = tests
142
+
143
+ res = self.post(
144
+ f"{self.account.hq}/build/threats",
145
+ json=body,
146
+ headers=self.account.headers,
147
+ timeout=10,
148
+ )
149
+ return res.json()
150
+
151
+ @verify_credentials
152
+ def update_threat(
153
+ self,
154
+ threat_id,
155
+ name=None,
156
+ source_id=None,
157
+ source=None,
158
+ published=None,
159
+ tests=None,
160
+ ):
161
+ """Update a threat"""
162
+ body = dict()
163
+ if name:
164
+ body["name"] = name
165
+ if source_id is not None:
166
+ body["source_id"] = source_id
167
+ if source is not None:
168
+ body["source"] = source
169
+ if published is not None:
170
+ body["published"] = published
171
+ if tests is not None:
172
+ body["tests"] = tests
173
+
174
+ res = self.post(
175
+ f"{self.account.hq}/build/threats/{threat_id}",
176
+ json=body,
177
+ headers=self.account.headers,
178
+ timeout=10,
179
+ )
180
+ return res.json()
181
+
182
+ @verify_credentials
183
+ def delete_threat(self, threat_id, purge):
184
+ """Delete an existing threat"""
185
+ res = self.delete(
186
+ f"{self.account.hq}/build/threats/{threat_id}",
187
+ json=dict(purge=purge),
188
+ headers=self.account.headers,
189
+ timeout=10,
190
+ )
191
+ return res.json()
192
+
193
+ @verify_credentials
194
+ def undelete_threat(self, threat_id):
195
+ """Undelete a tombstoned threat"""
196
+ res = self.post(
197
+ f"{self.account.hq}/build/threats/{threat_id}/undelete",
198
+ headers=self.account.headers,
199
+ timeout=10,
200
+ )
201
+ return res.json()
202
+
203
+ @verify_credentials
204
+ def create_detection(
205
+ self, rule: str, test_id: str, detection_id=None, rule_id=None
206
+ ):
207
+ """Create a detection"""
208
+ body = dict(rule=rule, test_id=test_id)
209
+ if detection_id:
210
+ body["detection_id"] = detection_id
211
+ if rule_id:
212
+ body["rule_id"] = rule_id
213
+
214
+ res = self.post(
215
+ f"{self.account.hq}/build/detections",
216
+ json=body,
217
+ headers=self.account.headers,
218
+ timeout=10,
219
+ )
220
+ return res.json()
221
+
222
+ @verify_credentials
223
+ def update_detection(self, detection_id: str, rule=None, test_id=None):
224
+ """Update a detection"""
225
+ body = dict()
226
+ if rule:
227
+ body["rule"] = rule
228
+ if test_id:
229
+ body["test_id"] = test_id
230
+
231
+ res = self.post(
232
+ f"{self.account.hq}/build/detections/{detection_id}",
233
+ json=body,
234
+ headers=self.account.headers,
235
+ timeout=10,
236
+ )
237
+ return res.json()
238
+
239
+ @verify_credentials
240
+ def delete_detection(self, detection_id: str):
241
+ """Delete an existing detection"""
242
+ res = self.delete(
243
+ f"{self.account.hq}/build/detections/{detection_id}",
244
+ headers=self.account.headers,
245
+ timeout=10,
246
+ )
247
+ return res.json()
248
+
249
+ @verify_credentials
250
+ def create_threat_hunt(
251
+ self,
252
+ control: Control,
253
+ test_id: str,
254
+ name: str,
255
+ query: str,
256
+ threat_hunt_id: str = None,
257
+ ):
258
+ """Create a threat hunt"""
259
+ body = dict(
260
+ control=control.name,
261
+ name=name,
262
+ query=query,
263
+ test_id=test_id,
264
+ )
265
+ if threat_hunt_id:
266
+ body["id"] = threat_hunt_id
267
+
268
+ res = self.post(
269
+ f"{self.account.hq}/build/threat_hunts",
270
+ json=body,
271
+ headers=self.account.headers,
272
+ timeout=10,
273
+ )
274
+ threat_hunt = res.json()
275
+ if self.account.resolve_enums:
276
+ self.resolve_enums(threat_hunt, [(Control, "control")])
277
+ return threat_hunt
278
+
279
+ @verify_credentials
280
+ def update_threat_hunt(
281
+ self,
282
+ threat_hunt_id: str,
283
+ name: str = None,
284
+ query: str = None,
285
+ test_id: str = None,
286
+ ):
287
+ """Update a threat hunt"""
288
+ body = dict()
289
+ if name:
290
+ body["name"] = name
291
+ if query:
292
+ body["query"] = query
293
+ if test_id:
294
+ body["test_id"] = test_id
295
+
296
+ res = self.post(
297
+ f"{self.account.hq}/build/threat_hunts/{threat_hunt_id}",
298
+ json=body,
299
+ headers=self.account.headers,
300
+ timeout=10,
301
+ )
302
+ threat_hunt = res.json()
303
+ if self.account.resolve_enums:
304
+ self.resolve_enums(threat_hunt, [(Control, "control")])
305
+ return threat_hunt
306
+
307
+ @verify_credentials
308
+ def delete_threat_hunt(self, threat_hunt_id: str):
309
+ """Delete an existing threat hunt"""
310
+ res = self.delete(
311
+ f"{self.account.hq}/build/threat_hunts/{threat_hunt_id}",
312
+ headers=self.account.headers,
313
+ timeout=10,
314
+ )
315
+ return res.json()
@@ -0,0 +1,256 @@
1
+ from prelude_sdk_beta.controllers.http_controller import HttpController
2
+ from prelude_sdk_beta.models.account import verify_credentials
3
+ from prelude_sdk_beta.models.codes import Control, RunCode
4
+
5
+
6
+ class DetectController(HttpController):
7
+
8
+ def __init__(self, account):
9
+ super().__init__(account)
10
+
11
+ def register_endpoint(self, host, serial_num, reg_string, tags=None):
12
+ """Register (or re-register) an endpoint to your account"""
13
+ body = dict(id=f"{host}:{serial_num}")
14
+ if tags:
15
+ body["tags"] = tags
16
+ account, token = reg_string.split("/")
17
+
18
+ res = self._session.post(
19
+ f"{self.account.hq}/detect/endpoint",
20
+ headers=dict(account=account, token=token, _product="py-sdk"),
21
+ json=body,
22
+ timeout=10,
23
+ )
24
+ return res.text
25
+
26
+ @verify_credentials
27
+ def update_endpoint(self, endpoint_id, tags=None):
28
+ """Update an endpoint in your account"""
29
+ body = dict()
30
+ if tags is not None:
31
+ body["tags"] = tags
32
+
33
+ res = self.post(
34
+ f"{self.account.hq}/detect/endpoint/{endpoint_id}",
35
+ headers=self.account.headers,
36
+ json=body,
37
+ timeout=10,
38
+ )
39
+ return res.json()
40
+
41
+ @verify_credentials
42
+ def delete_endpoint(self, ident: str):
43
+ """Delete an endpoint from your account"""
44
+ params = dict(id=ident)
45
+ res = self.delete(
46
+ f"{self.account.hq}/detect/endpoint",
47
+ headers=self.account.headers,
48
+ json=params,
49
+ timeout=10,
50
+ )
51
+ return res.json()
52
+
53
+ @verify_credentials
54
+ def list_endpoints(self, days: int = 90):
55
+ """List all endpoints on your account"""
56
+ params = dict(days=days)
57
+ res = self.get(
58
+ f"{self.account.hq}/detect/endpoint",
59
+ headers=self.account.headers,
60
+ params=params,
61
+ timeout=10,
62
+ )
63
+ endpoints = res.json()
64
+ if self.account.resolve_enums:
65
+ self.resolve_enums(endpoints, [(Control, "control")])
66
+ return endpoints
67
+
68
+ @verify_credentials
69
+ def describe_activity(self, filters: dict, view: str = "protected"):
70
+ """Get report for an Account"""
71
+ params = dict(view=view, **filters)
72
+ res = self.get(
73
+ f"{self.account.hq}/detect/activity",
74
+ headers=self.account.headers,
75
+ params=params,
76
+ timeout=10,
77
+ )
78
+ return res.json()
79
+
80
+ @verify_credentials
81
+ def threat_hunt_activity(self, threat_hunt_id=None, test_id=None, threat_id=None):
82
+ """Get threat hunt activity"""
83
+ filters = dict(
84
+ threat_hunt_id=threat_hunt_id, test_id=test_id, threat_id=threat_id
85
+ )
86
+ res = self.get(
87
+ f"{self.account.hq}/detect/threat_hunt_activity",
88
+ headers=self.account.headers,
89
+ params=filters,
90
+ timeout=10,
91
+ )
92
+ return res.json()
93
+
94
+ @verify_credentials
95
+ def list_tests(self, filters: dict = None):
96
+ """List all tests available to an account"""
97
+ res = self.get(
98
+ f"{self.account.hq}/detect/tests",
99
+ headers=self.account.headers,
100
+ params=filters if filters else {},
101
+ timeout=10,
102
+ )
103
+ return res.json()
104
+
105
+ @verify_credentials
106
+ def get_test(self, test_id):
107
+ """Get properties of an existing test"""
108
+ res = self.get(
109
+ f"{self.account.hq}/detect/tests/{test_id}",
110
+ headers=self.account.headers,
111
+ timeout=10,
112
+ )
113
+ return res.json()
114
+
115
+ @verify_credentials
116
+ def list_techniques(self):
117
+ """List techniques"""
118
+ res = self.get(
119
+ f"{self.account.hq}/detect/techniques",
120
+ headers=self.account.headers,
121
+ timeout=10,
122
+ )
123
+ return res.json()
124
+
125
+ @verify_credentials
126
+ def list_threats(self):
127
+ """List threats"""
128
+ res = self.get(
129
+ f"{self.account.hq}/detect/threats",
130
+ headers=self.account.headers,
131
+ params={},
132
+ timeout=10,
133
+ )
134
+ return res.json()
135
+
136
+ @verify_credentials
137
+ def get_threat(self, threat_id):
138
+ """Get properties of an existing threat"""
139
+ res = self.get(
140
+ f"{self.account.hq}/detect/threats/{threat_id}",
141
+ headers=self.account.headers,
142
+ timeout=10,
143
+ )
144
+ return res.json()
145
+
146
+ @verify_credentials
147
+ def list_detections(self):
148
+ """List detections"""
149
+ res = self.get(
150
+ f"{self.account.hq}/detect/detections",
151
+ headers=self.account.headers,
152
+ params={},
153
+ timeout=10,
154
+ )
155
+ return res.json()
156
+
157
+ @verify_credentials
158
+ def get_detection(self, detection_id):
159
+ """Get properties of an existing detection"""
160
+ res = self.get(
161
+ f"{self.account.hq}/detect/detections/{detection_id}",
162
+ headers=self.account.headers,
163
+ timeout=10,
164
+ )
165
+ return res.json()
166
+
167
+ @verify_credentials
168
+ def list_threat_hunts(self, filters: dict = None):
169
+ """List threat hunts"""
170
+ res = self.get(
171
+ f"{self.account.hq}/detect/threat_hunts",
172
+ headers=self.account.headers,
173
+ params=filters if filters else {},
174
+ timeout=10,
175
+ )
176
+ threat_hunts = res.json()
177
+ if self.account.resolve_enums:
178
+ self.resolve_enums(threat_hunts, [(Control, "control")])
179
+ return threat_hunts
180
+
181
+ @verify_credentials
182
+ def get_threat_hunt(self, threat_hunt_id):
183
+ """Get properties of an existing threat hunt"""
184
+ res = self.get(
185
+ f"{self.account.hq}/detect/threat_hunts/{threat_hunt_id}",
186
+ headers=self.account.headers,
187
+ timeout=10,
188
+ )
189
+ threat_hunt = res.json()
190
+ if self.account.resolve_enums:
191
+ self.resolve_enums(threat_hunt, [(Control, "control")])
192
+ return threat_hunt
193
+
194
+ @verify_credentials
195
+ def do_threat_hunt(self, threat_hunt_id):
196
+ """Run a threat hunt"""
197
+ res = self.post(
198
+ f"{self.account.hq}/detect/threat_hunts/{threat_hunt_id}",
199
+ headers=self.account.headers,
200
+ timeout=10,
201
+ )
202
+ return res.json()
203
+
204
+ @verify_credentials
205
+ def download(self, test_id, filename):
206
+ """Clone a test file or attachment"""
207
+ res = self.get(
208
+ f"{self.account.hq}/detect/tests/{test_id}/{filename}",
209
+ headers=self.account.headers,
210
+ timeout=10,
211
+ )
212
+ return res.content
213
+
214
+ @verify_credentials
215
+ def schedule(self, items: list):
216
+ """Schedule tests and threats so endpoints will start running them
217
+
218
+ Example: items=[dict(run_code='DAILY', tags='grp-1,grp2', test_id='123-123-123'),
219
+ dict(run_code='DAILY', tags='grp-1', threat_id='abc-def-ghi')]
220
+ """
221
+ res = self.post(
222
+ url=f"{self.account.hq}/detect/queue",
223
+ headers=self.account.headers,
224
+ json=dict(items=items),
225
+ timeout=10,
226
+ )
227
+ schedule = res.json()
228
+ if self.account.resolve_enums:
229
+ self.resolve_enums(schedule, [(RunCode, "run_code")])
230
+ return schedule
231
+
232
+ @verify_credentials
233
+ def unschedule(self, items: list):
234
+ """Unschedule tests and threats so endpoints will stop running them
235
+
236
+ Example: items=[dict(tags='grp-1,grp2', test_id='123-123-123'),
237
+ dict(tags='grp-1', threat_id='abc-def-ghi')]
238
+ """
239
+ res = self.delete(
240
+ f"{self.account.hq}/detect/queue",
241
+ headers=self.account.headers,
242
+ json=dict(items=items),
243
+ timeout=10,
244
+ )
245
+ return res.json()
246
+
247
+ @verify_credentials
248
+ def accept_terms(self, name, version):
249
+ """Accept terms and conditions"""
250
+ res = self.post(
251
+ f"{self.account.hq}/iam/terms",
252
+ headers=self.account.headers,
253
+ json=dict(name=name, version=version),
254
+ timeout=10,
255
+ )
256
+ return res.json()
@@ -0,0 +1,31 @@
1
+ from prelude_sdk_beta.controllers.http_controller import HttpController
2
+ from prelude_sdk_beta.models.account import verify_credentials
3
+ from prelude_sdk_beta.models.codes import SCMCategory
4
+
5
+
6
+ class ExportController(HttpController):
7
+
8
+ def __init__(self, account):
9
+ super().__init__(account)
10
+
11
+ @verify_credentials
12
+ def export_scm(
13
+ self,
14
+ export_type: SCMCategory,
15
+ filter: str = None,
16
+ orderby: str = None,
17
+ top: int = None,
18
+ ):
19
+ """Download partner data as a CSV"""
20
+ params = {
21
+ "$filter": filter,
22
+ "$orderby": orderby,
23
+ "$top": top,
24
+ }
25
+ res = self.post(
26
+ f"{self.account.hq}/export/scm/{export_type.name}",
27
+ params=params,
28
+ headers=self.account.headers,
29
+ timeout=10,
30
+ )
31
+ return res.json()
@@ -0,0 +1,40 @@
1
+ from prelude_sdk_beta.controllers.http_controller import HttpController
2
+ from prelude_sdk_beta.models.account import verify_credentials
3
+ from prelude_sdk_beta.models.codes import Control
4
+
5
+
6
+ class GenerateController(HttpController):
7
+ def __init__(self, account):
8
+ super().__init__(account)
9
+
10
+ @verify_credentials
11
+ def upload_threat_intel(self, file: str):
12
+ with open(file, "rb") as f:
13
+ body = f.read()
14
+ res = self.post(
15
+ f"{self.account.hq}/generate/threat-intel",
16
+ data=body,
17
+ headers=self.account.headers | {"Content-Type": "application/pdf"},
18
+ timeout=30,
19
+ )
20
+ return res.json()
21
+
22
+ @verify_credentials
23
+ def get_threat_intel(self, job_id: str):
24
+ res = self.get(
25
+ f"{self.account.hq}/generate/threat-intel/{job_id}",
26
+ headers=self.account.headers,
27
+ timeout=10,
28
+ )
29
+ return res.json()
30
+
31
+ @verify_credentials
32
+ def generate_from_partner_advisory(self, partner: Control, advisory_id: str):
33
+ params = dict(advisory_id=advisory_id)
34
+ res = self.post(
35
+ f"{self.account.hq}/generate/partner-advisories/{partner.name}",
36
+ headers=self.account.headers,
37
+ json=params,
38
+ timeout=30,
39
+ )
40
+ return res.json()