contentctl 4.4.7__py3-none-any.whl → 5.0.0a0__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.
- contentctl/actions/build.py +39 -27
- contentctl/actions/detection_testing/DetectionTestingManager.py +0 -1
- contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +32 -26
- contentctl/actions/detection_testing/progress_bar.py +6 -6
- contentctl/actions/detection_testing/views/DetectionTestingView.py +4 -4
- contentctl/actions/new_content.py +98 -81
- contentctl/actions/test.py +4 -5
- contentctl/actions/validate.py +2 -1
- contentctl/contentctl.py +114 -79
- contentctl/helper/utils.py +0 -14
- contentctl/input/director.py +5 -5
- contentctl/input/new_content_questions.py +2 -2
- contentctl/input/yml_reader.py +11 -6
- contentctl/objects/abstract_security_content_objects/detection_abstract.py +228 -120
- contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +5 -7
- contentctl/objects/alert_action.py +2 -1
- contentctl/objects/atomic.py +1 -0
- contentctl/objects/base_test.py +4 -3
- contentctl/objects/base_test_result.py +3 -3
- contentctl/objects/baseline.py +26 -6
- contentctl/objects/baseline_tags.py +2 -3
- contentctl/objects/config.py +26 -45
- contentctl/objects/constants.py +4 -1
- contentctl/objects/correlation_search.py +89 -95
- contentctl/objects/data_source.py +5 -6
- contentctl/objects/deployment.py +2 -10
- contentctl/objects/deployment_email.py +2 -1
- contentctl/objects/deployment_notable.py +2 -1
- contentctl/objects/deployment_phantom.py +2 -1
- contentctl/objects/deployment_rba.py +2 -1
- contentctl/objects/deployment_scheduling.py +2 -1
- contentctl/objects/deployment_slack.py +2 -1
- contentctl/objects/detection_tags.py +7 -42
- contentctl/objects/drilldown.py +1 -0
- contentctl/objects/enums.py +21 -58
- contentctl/objects/investigation.py +6 -5
- contentctl/objects/investigation_tags.py +2 -3
- contentctl/objects/lookup.py +145 -63
- contentctl/objects/macro.py +2 -3
- contentctl/objects/mitre_attack_enrichment.py +2 -2
- contentctl/objects/observable.py +3 -1
- contentctl/objects/playbook_tags.py +5 -1
- contentctl/objects/rba.py +90 -0
- contentctl/objects/risk_event.py +87 -144
- contentctl/objects/story_tags.py +1 -2
- contentctl/objects/test_attack_data.py +2 -1
- contentctl/objects/unit_test_baseline.py +2 -1
- contentctl/output/api_json_output.py +233 -220
- contentctl/output/conf_output.py +51 -44
- contentctl/output/conf_writer.py +201 -125
- contentctl/output/data_source_writer.py +0 -1
- contentctl/output/json_writer.py +2 -4
- contentctl/output/svg_output.py +1 -1
- contentctl/output/templates/analyticstories_detections.j2 +1 -1
- contentctl/output/templates/collections.j2 +1 -1
- contentctl/output/templates/doc_detections.j2 +0 -5
- contentctl/output/templates/savedsearches_detections.j2 +8 -3
- contentctl/output/templates/transforms.j2 +4 -4
- contentctl/output/yml_writer.py +15 -0
- contentctl/templates/detections/endpoint/anomalous_usage_of_7zip.yml +16 -34
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/METADATA +5 -4
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/RECORD +65 -68
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/WHEEL +1 -1
- contentctl/objects/event_source.py +0 -11
- contentctl/output/detection_writer.py +0 -28
- contentctl/output/new_content_yml_output.py +0 -56
- contentctl/output/yml_output.py +0 -66
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/LICENSE.md +0 -0
- {contentctl-4.4.7.dist-info → contentctl-5.0.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -1,246 +1,259 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from contentctl.objects.detection import Detection
|
|
5
|
+
from contentctl.objects.lookup import Lookup
|
|
6
|
+
from contentctl.objects.macro import Macro
|
|
7
|
+
from contentctl.objects.story import Story
|
|
8
|
+
from contentctl.objects.baseline import Baseline
|
|
9
|
+
from contentctl.objects.investigation import Investigation
|
|
10
|
+
from contentctl.objects.deployment import Deployment
|
|
11
|
+
|
|
1
12
|
import os
|
|
2
|
-
import json
|
|
3
13
|
import pathlib
|
|
4
14
|
|
|
5
15
|
from contentctl.output.json_writer import JsonWriter
|
|
6
|
-
from contentctl.objects.enums import SecurityContentType
|
|
7
|
-
from contentctl.objects.abstract_security_content_objects.security_content_object_abstract import (
|
|
8
|
-
SecurityContentObject_Abstract,
|
|
9
|
-
)
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class ApiJsonOutput:
|
|
20
|
+
output_path: pathlib.Path
|
|
21
|
+
app_label: str
|
|
22
|
+
|
|
23
|
+
def __init__(self, output_path:pathlib.Path, app_label: str):
|
|
24
|
+
self.output_path = output_path
|
|
25
|
+
self.app_label = app_label
|
|
14
26
|
|
|
15
|
-
def
|
|
27
|
+
def writeDetections(
|
|
16
28
|
self,
|
|
17
|
-
objects: list[
|
|
18
|
-
output_path: pathlib.Path,
|
|
19
|
-
app_label:str = "ESCU",
|
|
20
|
-
contentType: SecurityContentType = None
|
|
29
|
+
objects: list[Detection],
|
|
21
30
|
) -> None:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"search",
|
|
44
|
-
"how_to_implement",
|
|
45
|
-
"known_false_positives",
|
|
46
|
-
"references",
|
|
47
|
-
"datamodel",
|
|
48
|
-
"macros",
|
|
49
|
-
"lookups",
|
|
50
|
-
"source",
|
|
51
|
-
"nes_fields",
|
|
52
|
-
]
|
|
53
|
-
)
|
|
31
|
+
detections = [
|
|
32
|
+
detection.model_dump(
|
|
33
|
+
include=set(
|
|
34
|
+
[
|
|
35
|
+
"name",
|
|
36
|
+
"author",
|
|
37
|
+
"date",
|
|
38
|
+
"version",
|
|
39
|
+
"id",
|
|
40
|
+
"description",
|
|
41
|
+
"tags",
|
|
42
|
+
"search",
|
|
43
|
+
"how_to_implement",
|
|
44
|
+
"known_false_positives",
|
|
45
|
+
"references",
|
|
46
|
+
"datamodel",
|
|
47
|
+
"macros",
|
|
48
|
+
"lookups",
|
|
49
|
+
"source",
|
|
50
|
+
"nes_fields",
|
|
51
|
+
]
|
|
54
52
|
)
|
|
55
|
-
for detection in objects
|
|
56
|
-
]
|
|
57
|
-
#Only a subset of macro fields are required:
|
|
58
|
-
# for detection in detections:
|
|
59
|
-
# new_macros = []
|
|
60
|
-
# for macro in detection.get("macros",[]):
|
|
61
|
-
# new_macro_fields = {}
|
|
62
|
-
# new_macro_fields["name"] = macro.get("name")
|
|
63
|
-
# new_macro_fields["definition"] = macro.get("definition")
|
|
64
|
-
# new_macro_fields["description"] = macro.get("description")
|
|
65
|
-
# if len(macro.get("arguments", [])) > 0:
|
|
66
|
-
# new_macro_fields["arguments"] = macro.get("arguments")
|
|
67
|
-
# new_macros.append(new_macro_fields)
|
|
68
|
-
# detection["macros"] = new_macros
|
|
69
|
-
# del()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
JsonWriter.writeJsonObject(
|
|
73
|
-
os.path.join(output_path, "detections.json"), "detections", detections
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
elif contentType == SecurityContentType.macros:
|
|
77
|
-
macros = [
|
|
78
|
-
macro.model_dump(include=set(["definition", "description", "name"]))
|
|
79
|
-
for macro in objects
|
|
80
|
-
]
|
|
81
|
-
for macro in macros:
|
|
82
|
-
for k in ["author", "date","version","id","references"]:
|
|
83
|
-
if k in macro:
|
|
84
|
-
del(macro[k])
|
|
85
|
-
JsonWriter.writeJsonObject(
|
|
86
|
-
os.path.join(output_path, "macros.json"), "macros", macros
|
|
87
53
|
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"detections_names",
|
|
104
|
-
"investigation_names",
|
|
105
|
-
"baseline_names",
|
|
106
|
-
"detections",
|
|
107
|
-
]
|
|
108
|
-
)
|
|
109
|
-
)
|
|
110
|
-
for story in objects
|
|
111
|
-
]
|
|
112
|
-
# Only get certain fields from detections
|
|
113
|
-
for story in stories:
|
|
114
|
-
# Only use a small subset of fields from the detection
|
|
115
|
-
story["detections"] = [
|
|
116
|
-
{
|
|
117
|
-
"name": detection["name"],
|
|
118
|
-
"source": detection["source"],
|
|
119
|
-
"type": detection["type"],
|
|
120
|
-
"tags": detection["tags"].get("mitre_attack_enrichments", []),
|
|
121
|
-
}
|
|
122
|
-
for detection in story["detections"]
|
|
123
|
-
]
|
|
124
|
-
story["detection_names"] = [f"{app_label} - {name} - Rule" for name in story["detection_names"]]
|
|
54
|
+
for detection in objects
|
|
55
|
+
]
|
|
56
|
+
#Only a subset of macro fields are required:
|
|
57
|
+
# for detection in detections:
|
|
58
|
+
# new_macros = []
|
|
59
|
+
# for macro in detection.get("macros",[]):
|
|
60
|
+
# new_macro_fields = {}
|
|
61
|
+
# new_macro_fields["name"] = macro.get("name")
|
|
62
|
+
# new_macro_fields["definition"] = macro.get("definition")
|
|
63
|
+
# new_macro_fields["description"] = macro.get("description")
|
|
64
|
+
# if len(macro.get("arguments", [])) > 0:
|
|
65
|
+
# new_macro_fields["arguments"] = macro.get("arguments")
|
|
66
|
+
# new_macros.append(new_macro_fields)
|
|
67
|
+
# detection["macros"] = new_macros
|
|
68
|
+
# del()
|
|
125
69
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
70
|
+
|
|
71
|
+
JsonWriter.writeJsonObject(
|
|
72
|
+
os.path.join(self.output_path, "detections.json"), "detections", detections
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def writeMacros(
|
|
76
|
+
self,
|
|
77
|
+
objects: list[Macro],
|
|
78
|
+
) -> None:
|
|
79
|
+
macros = [
|
|
80
|
+
macro.model_dump(include=set(["definition", "description", "name"]))
|
|
81
|
+
for macro in objects
|
|
82
|
+
]
|
|
83
|
+
for macro in macros:
|
|
84
|
+
for k in ["author", "date","version","id","references"]:
|
|
85
|
+
if k in macro:
|
|
86
|
+
del(macro[k])
|
|
87
|
+
JsonWriter.writeJsonObject(
|
|
88
|
+
os.path.join(self.output_path, "macros.json"), "macros", macros
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def writeStories(
|
|
92
|
+
self,
|
|
93
|
+
objects: list[Story],
|
|
94
|
+
) -> None:
|
|
95
|
+
stories = [
|
|
96
|
+
story.model_dump(
|
|
97
|
+
include=set(
|
|
98
|
+
[
|
|
99
|
+
"name",
|
|
100
|
+
"author",
|
|
101
|
+
"date",
|
|
102
|
+
"version",
|
|
103
|
+
"id",
|
|
104
|
+
"description",
|
|
105
|
+
"narrative",
|
|
106
|
+
"references",
|
|
107
|
+
"tags",
|
|
108
|
+
"detections_names",
|
|
109
|
+
"investigation_names",
|
|
110
|
+
"baseline_names",
|
|
111
|
+
"detections",
|
|
112
|
+
]
|
|
113
|
+
)
|
|
129
114
|
)
|
|
115
|
+
for story in objects
|
|
116
|
+
]
|
|
117
|
+
# Only get certain fields from detections
|
|
118
|
+
for story in stories:
|
|
119
|
+
# Only use a small subset of fields from the detection
|
|
120
|
+
story["detections"] = [
|
|
121
|
+
{
|
|
122
|
+
"name": detection["name"],
|
|
123
|
+
"source": detection["source"],
|
|
124
|
+
"type": detection["type"],
|
|
125
|
+
"tags": detection["tags"].get("mitre_attack_enrichments", []),
|
|
126
|
+
}
|
|
127
|
+
for detection in story["detections"]
|
|
128
|
+
]
|
|
129
|
+
story["detection_names"] = [f"{self.app_label} - {name} - Rule" for name in story["detection_names"]]
|
|
130
|
+
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
baseline.model_dump(
|
|
135
|
-
include=set(
|
|
136
|
-
[
|
|
137
|
-
"name",
|
|
138
|
-
"author",
|
|
139
|
-
"date",
|
|
140
|
-
"version",
|
|
141
|
-
"id",
|
|
142
|
-
"description",
|
|
143
|
-
"type",
|
|
144
|
-
"datamodel",
|
|
145
|
-
"search",
|
|
146
|
-
"how_to_implement",
|
|
147
|
-
"known_false_positives",
|
|
148
|
-
"references",
|
|
149
|
-
"tags",
|
|
150
|
-
]
|
|
151
|
-
)
|
|
152
|
-
)
|
|
153
|
-
for baseline in objects
|
|
154
|
-
]
|
|
155
|
-
except Exception as e:
|
|
156
|
-
print(e)
|
|
157
|
-
print('wait')
|
|
132
|
+
JsonWriter.writeJsonObject(
|
|
133
|
+
os.path.join(self.output_path, "stories.json"), "stories", stories
|
|
134
|
+
)
|
|
158
135
|
|
|
159
|
-
|
|
160
|
-
|
|
136
|
+
def writeBaselines(
|
|
137
|
+
self,
|
|
138
|
+
objects: list[Baseline],
|
|
139
|
+
) -> None:
|
|
140
|
+
baselines = [
|
|
141
|
+
baseline.model_dump(
|
|
142
|
+
include=set(
|
|
143
|
+
[
|
|
144
|
+
"name",
|
|
145
|
+
"author",
|
|
146
|
+
"date",
|
|
147
|
+
"version",
|
|
148
|
+
"id",
|
|
149
|
+
"description",
|
|
150
|
+
"type",
|
|
151
|
+
"datamodel",
|
|
152
|
+
"search",
|
|
153
|
+
"how_to_implement",
|
|
154
|
+
"known_false_positives",
|
|
155
|
+
"references",
|
|
156
|
+
"tags",
|
|
157
|
+
]
|
|
161
158
|
)
|
|
159
|
+
)
|
|
160
|
+
for baseline in objects
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
JsonWriter.writeJsonObject(
|
|
164
|
+
os.path.join(self.output_path, "baselines.json"), "baselines", baselines
|
|
165
|
+
)
|
|
162
166
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
167
|
+
def writeInvestigations(
|
|
168
|
+
self,
|
|
169
|
+
objects: list[Investigation],
|
|
170
|
+
) -> None:
|
|
171
|
+
investigations = [
|
|
172
|
+
investigation.model_dump(
|
|
173
|
+
include=set(
|
|
174
|
+
[
|
|
175
|
+
"name",
|
|
176
|
+
"author",
|
|
177
|
+
"date",
|
|
178
|
+
"version",
|
|
179
|
+
"id",
|
|
180
|
+
"description",
|
|
181
|
+
"type",
|
|
182
|
+
"datamodel",
|
|
183
|
+
"search",
|
|
184
|
+
"how_to_implemnet",
|
|
185
|
+
"known_false_positives",
|
|
186
|
+
"references",
|
|
187
|
+
"inputs",
|
|
188
|
+
"tags",
|
|
189
|
+
"lowercase_name",
|
|
190
|
+
]
|
|
185
191
|
)
|
|
186
|
-
for investigation in objects
|
|
187
|
-
]
|
|
188
|
-
JsonWriter.writeJsonObject(
|
|
189
|
-
os.path.join(output_path, "response_tasks.json"),
|
|
190
|
-
"response_tasks",
|
|
191
|
-
investigations,
|
|
192
192
|
)
|
|
193
|
+
for investigation in objects
|
|
194
|
+
]
|
|
195
|
+
JsonWriter.writeJsonObject(
|
|
196
|
+
os.path.join(self.output_path, "response_tasks.json"),
|
|
197
|
+
"response_tasks",
|
|
198
|
+
investigations,
|
|
199
|
+
)
|
|
193
200
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
201
|
+
def writeLookups(
|
|
202
|
+
self,
|
|
203
|
+
objects: list[Lookup],
|
|
204
|
+
) -> None:
|
|
205
|
+
lookups = [
|
|
206
|
+
lookup.model_dump(
|
|
207
|
+
include=set(
|
|
208
|
+
[
|
|
209
|
+
"name",
|
|
210
|
+
"description",
|
|
211
|
+
"collection",
|
|
212
|
+
"fields_list",
|
|
213
|
+
"filename",
|
|
214
|
+
"default_match",
|
|
215
|
+
"match_type",
|
|
216
|
+
"min_matches",
|
|
217
|
+
"case_sensitive_match",
|
|
218
|
+
]
|
|
210
219
|
)
|
|
211
|
-
for lookup in objects
|
|
212
|
-
]
|
|
213
|
-
for lookup in lookups:
|
|
214
|
-
for k in ["author","date","version","id","references"]:
|
|
215
|
-
if k in lookup:
|
|
216
|
-
del(lookup[k])
|
|
217
|
-
JsonWriter.writeJsonObject(
|
|
218
|
-
os.path.join(output_path, "lookups.json"), "lookups", lookups
|
|
219
220
|
)
|
|
221
|
+
for lookup in objects
|
|
222
|
+
]
|
|
223
|
+
for lookup in lookups:
|
|
224
|
+
for k in ["author","date","version","id","references"]:
|
|
225
|
+
if k in lookup:
|
|
226
|
+
del(lookup[k])
|
|
227
|
+
JsonWriter.writeJsonObject(
|
|
228
|
+
os.path.join(self.output_path, "lookups.json"), "lookups", lookups
|
|
229
|
+
)
|
|
220
230
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
231
|
+
def writeDeployments(
|
|
232
|
+
self,
|
|
233
|
+
objects: list[Deployment],
|
|
234
|
+
) -> None:
|
|
235
|
+
deployments = [
|
|
236
|
+
deployment.model_dump(
|
|
237
|
+
include=set(
|
|
238
|
+
[
|
|
239
|
+
"name",
|
|
240
|
+
"author",
|
|
241
|
+
"date",
|
|
242
|
+
"version",
|
|
243
|
+
"id",
|
|
244
|
+
"description",
|
|
245
|
+
"scheduling",
|
|
246
|
+
"rba",
|
|
247
|
+
"tags"
|
|
248
|
+
]
|
|
237
249
|
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
250
|
+
)
|
|
251
|
+
for deployment in objects
|
|
252
|
+
]
|
|
253
|
+
#references are not to be included, but have been deleted in the
|
|
254
|
+
#model_serialization logic
|
|
255
|
+
JsonWriter.writeJsonObject(
|
|
256
|
+
os.path.join(self.output_path, "deployments.json"),
|
|
257
|
+
"deployments",
|
|
258
|
+
deployments,
|
|
259
|
+
)
|
contentctl/output/conf_output.py
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
from
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Callable
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from contentctl.objects.detection import Detection
|
|
5
|
+
from contentctl.objects.lookup import Lookup
|
|
6
|
+
from contentctl.objects.macro import Macro
|
|
7
|
+
from contentctl.objects.dashboard import Dashboard
|
|
8
|
+
from contentctl.objects.story import Story
|
|
9
|
+
from contentctl.objects.baseline import Baseline
|
|
10
|
+
from contentctl.objects.investigation import Investigation
|
|
11
|
+
|
|
12
|
+
from contentctl.objects.lookup import FileBackedLookup
|
|
4
13
|
import shutil
|
|
5
|
-
import sys
|
|
6
14
|
import tarfile
|
|
7
|
-
from typing import Union
|
|
8
|
-
from pathlib import Path
|
|
9
15
|
import pathlib
|
|
10
|
-
import time
|
|
11
16
|
import timeit
|
|
12
17
|
import datetime
|
|
13
|
-
import shutil
|
|
14
|
-
import json
|
|
15
18
|
from contentctl.output.conf_writer import ConfWriter
|
|
16
|
-
from contentctl.objects.enums import SecurityContentType
|
|
17
19
|
from contentctl.objects.config import build
|
|
18
|
-
from requests import Session, post, get
|
|
19
|
-
from requests.auth import HTTPBasicAuth
|
|
20
20
|
|
|
21
21
|
class ConfOutput:
|
|
22
22
|
config: build
|
|
@@ -80,25 +80,33 @@ class ConfOutput:
|
|
|
80
80
|
return written_files
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def
|
|
83
|
+
def writeDetections(self, objects:list[Detection]) -> set[pathlib.Path]:
|
|
84
84
|
written_files:set[pathlib.Path] = set()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
for output_app_path, template_name in [ ('default/savedsearches.conf', 'savedsearches_detections.j2'),
|
|
86
|
+
('default/analyticstories.conf', 'analyticstories_detections.j2')]:
|
|
87
|
+
written_files.add(ConfWriter.writeConfFile(pathlib.Path(output_app_path),
|
|
88
|
+
template_name, self.config, objects))
|
|
89
|
+
return written_files
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def writeStories(self, objects:list[Story]) -> set[pathlib.Path]:
|
|
93
|
+
written_files:set[pathlib.Path] = set()
|
|
92
94
|
written_files.add(ConfWriter.writeConfFile(pathlib.Path('default/analyticstories.conf'),
|
|
93
95
|
'analyticstories_stories.j2',
|
|
94
96
|
self.config, objects))
|
|
97
|
+
return written_files
|
|
98
|
+
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
def writeBaselines(self, objects:list[Baseline]) -> set[pathlib.Path]:
|
|
101
|
+
written_files:set[pathlib.Path] = set()
|
|
97
102
|
written_files.add(ConfWriter.writeConfFile(pathlib.Path('default/savedsearches.conf'),
|
|
98
103
|
'savedsearches_baselines.j2',
|
|
99
104
|
self.config, objects))
|
|
105
|
+
return written_files
|
|
106
|
+
|
|
100
107
|
|
|
101
|
-
|
|
108
|
+
def writeInvestigations(self, objects:list[Investigation]) -> set[pathlib.Path]:
|
|
109
|
+
written_files:set[pathlib.Path] = set()
|
|
102
110
|
for output_app_path, template_name in [ ('default/savedsearches.conf', 'savedsearches_investigations.j2'),
|
|
103
111
|
('default/analyticstories.conf', 'analyticstories_investigations.j2')]:
|
|
104
112
|
ConfWriter.writeConfFile(pathlib.Path(output_app_path),
|
|
@@ -106,7 +114,7 @@ class ConfOutput:
|
|
|
106
114
|
self.config,
|
|
107
115
|
objects)
|
|
108
116
|
|
|
109
|
-
workbench_panels = []
|
|
117
|
+
workbench_panels:list[Investigation] = []
|
|
110
118
|
for investigation in objects:
|
|
111
119
|
if investigation.inputs:
|
|
112
120
|
response_file_name_xml = investigation.lowercase_name + "___response_task.xml"
|
|
@@ -128,8 +136,11 @@ class ConfOutput:
|
|
|
128
136
|
template_name,
|
|
129
137
|
self.config,
|
|
130
138
|
workbench_panels))
|
|
139
|
+
return written_files
|
|
140
|
+
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
def writeLookups(self, objects:list[Lookup]) -> set[pathlib.Path]:
|
|
143
|
+
written_files:set[pathlib.Path] = set()
|
|
133
144
|
for output_app_path, template_name in [ ('default/collections.conf', 'collections.j2'),
|
|
134
145
|
('default/transforms.conf', 'transforms.j2')]:
|
|
135
146
|
written_files.add(ConfWriter.writeConfFile(pathlib.Path(output_app_path),
|
|
@@ -137,9 +148,7 @@ class ConfOutput:
|
|
|
137
148
|
self.config,
|
|
138
149
|
objects))
|
|
139
150
|
|
|
140
|
-
|
|
141
|
-
#we want to copy all *.mlmodel files as well, not just csvs
|
|
142
|
-
files = list(glob.iglob(str(self.config.path/ 'lookups/*.csv'))) + list(glob.iglob(str(self.config.path / 'lookups/*.mlmodel')))
|
|
151
|
+
#Get the path to the lookups folder
|
|
143
152
|
lookup_folder = self.config.getPackageDirectoryPath()/"lookups"
|
|
144
153
|
|
|
145
154
|
# Make the new folder for the lookups
|
|
@@ -147,26 +156,24 @@ class ConfOutput:
|
|
|
147
156
|
lookup_folder.mkdir(exist_ok=True)
|
|
148
157
|
|
|
149
158
|
#Copy each lookup into the folder
|
|
150
|
-
for
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
for lookup in objects:
|
|
160
|
+
if isinstance(lookup, FileBackedLookup):
|
|
161
|
+
shutil.copy(lookup.filename, lookup_folder/lookup.app_filename.name)
|
|
162
|
+
return written_files
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def writeMacros(self, objects:list[Macro]) -> set[pathlib.Path]:
|
|
166
|
+
written_files:set[pathlib.Path] = set()
|
|
158
167
|
written_files.add(ConfWriter.writeConfFile(pathlib.Path('default/macros.conf'),
|
|
159
168
|
'macros.j2',
|
|
160
169
|
self.config, objects))
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
written_files.update(ConfWriter.writeDashboardFiles(self.config, objects))
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return written_files
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
return written_files
|
|
171
|
+
|
|
169
172
|
|
|
173
|
+
def writeDashboards(self, objects:list[Dashboard]) -> set[pathlib.Path]:
|
|
174
|
+
written_files:set[pathlib.Path] = set()
|
|
175
|
+
written_files.update(ConfWriter.writeDashboardFiles(self.config, objects))
|
|
176
|
+
return written_files
|
|
170
177
|
|
|
171
178
|
|
|
172
179
|
def packageAppTar(self) -> None:
|
|
@@ -202,7 +209,7 @@ class ConfOutput:
|
|
|
202
209
|
|
|
203
210
|
|
|
204
211
|
|
|
205
|
-
def packageApp(self, method=packageAppTar)->None:
|
|
212
|
+
def packageApp(self, method: Callable[[ConfOutput],None]=packageAppTar)->None:
|
|
206
213
|
return method(self)
|
|
207
214
|
|
|
208
215
|
|