contentctl 3.6.0__py3-none-any.whl → 4.0.2__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.
Files changed (142) hide show
  1. contentctl/actions/build.py +89 -0
  2. contentctl/actions/detection_testing/DetectionTestingManager.py +48 -49
  3. contentctl/actions/detection_testing/GitService.py +148 -230
  4. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py +14 -24
  5. contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py +43 -17
  6. contentctl/actions/detection_testing/views/DetectionTestingView.py +3 -2
  7. contentctl/actions/detection_testing/views/DetectionTestingViewFile.py +0 -8
  8. contentctl/actions/doc_gen.py +1 -1
  9. contentctl/actions/initialize.py +28 -65
  10. contentctl/actions/inspect.py +260 -0
  11. contentctl/actions/new_content.py +106 -13
  12. contentctl/actions/release_notes.py +168 -144
  13. contentctl/actions/reporting.py +24 -13
  14. contentctl/actions/test.py +39 -20
  15. contentctl/actions/validate.py +25 -48
  16. contentctl/contentctl.py +196 -754
  17. contentctl/enrichments/attack_enrichment.py +69 -19
  18. contentctl/enrichments/cve_enrichment.py +28 -13
  19. contentctl/helper/link_validator.py +24 -26
  20. contentctl/helper/utils.py +7 -3
  21. contentctl/input/director.py +139 -201
  22. contentctl/input/new_content_questions.py +63 -61
  23. contentctl/input/sigma_converter.py +1 -2
  24. contentctl/input/ssa_detection_builder.py +16 -7
  25. contentctl/input/yml_reader.py +4 -3
  26. contentctl/objects/abstract_security_content_objects/detection_abstract.py +487 -154
  27. contentctl/objects/abstract_security_content_objects/security_content_object_abstract.py +155 -51
  28. contentctl/objects/alert_action.py +40 -0
  29. contentctl/objects/atomic.py +212 -0
  30. contentctl/objects/baseline.py +44 -43
  31. contentctl/objects/baseline_tags.py +69 -20
  32. contentctl/objects/config.py +857 -125
  33. contentctl/objects/constants.py +0 -1
  34. contentctl/objects/correlation_search.py +1 -1
  35. contentctl/objects/data_source.py +2 -4
  36. contentctl/objects/deployment.py +61 -21
  37. contentctl/objects/deployment_email.py +2 -2
  38. contentctl/objects/deployment_notable.py +4 -4
  39. contentctl/objects/deployment_phantom.py +2 -2
  40. contentctl/objects/deployment_rba.py +3 -4
  41. contentctl/objects/deployment_scheduling.py +2 -3
  42. contentctl/objects/deployment_slack.py +2 -2
  43. contentctl/objects/detection.py +1 -5
  44. contentctl/objects/detection_tags.py +210 -119
  45. contentctl/objects/enums.py +312 -24
  46. contentctl/objects/integration_test.py +1 -1
  47. contentctl/objects/integration_test_result.py +0 -2
  48. contentctl/objects/investigation.py +62 -53
  49. contentctl/objects/investigation_tags.py +30 -6
  50. contentctl/objects/lookup.py +80 -31
  51. contentctl/objects/macro.py +29 -45
  52. contentctl/objects/mitre_attack_enrichment.py +29 -5
  53. contentctl/objects/observable.py +3 -7
  54. contentctl/objects/playbook.py +60 -30
  55. contentctl/objects/playbook_tags.py +45 -8
  56. contentctl/objects/security_content_object.py +1 -5
  57. contentctl/objects/ssa_detection.py +8 -4
  58. contentctl/objects/ssa_detection_tags.py +19 -26
  59. contentctl/objects/story.py +142 -44
  60. contentctl/objects/story_tags.py +46 -33
  61. contentctl/objects/unit_test.py +7 -2
  62. contentctl/objects/unit_test_attack_data.py +10 -19
  63. contentctl/objects/unit_test_baseline.py +1 -1
  64. contentctl/objects/unit_test_old.py +4 -3
  65. contentctl/objects/unit_test_result.py +5 -3
  66. contentctl/objects/unit_test_ssa.py +31 -0
  67. contentctl/output/api_json_output.py +202 -130
  68. contentctl/output/attack_nav_output.py +20 -9
  69. contentctl/output/attack_nav_writer.py +3 -3
  70. contentctl/output/ba_yml_output.py +3 -3
  71. contentctl/output/conf_output.py +125 -391
  72. contentctl/output/conf_writer.py +169 -31
  73. contentctl/output/jinja_writer.py +2 -2
  74. contentctl/output/json_writer.py +17 -5
  75. contentctl/output/new_content_yml_output.py +8 -7
  76. contentctl/output/svg_output.py +17 -27
  77. contentctl/output/templates/analyticstories_detections.j2 +8 -4
  78. contentctl/output/templates/analyticstories_investigations.j2 +1 -1
  79. contentctl/output/templates/analyticstories_stories.j2 +6 -6
  80. contentctl/output/templates/app.conf.j2 +2 -2
  81. contentctl/output/templates/app.manifest.j2 +2 -2
  82. contentctl/output/templates/detection_coverage.j2 +6 -8
  83. contentctl/output/templates/doc_detection_page.j2 +2 -2
  84. contentctl/output/templates/doc_detections.j2 +2 -2
  85. contentctl/output/templates/doc_stories.j2 +1 -1
  86. contentctl/output/templates/es_investigations_investigations.j2 +1 -1
  87. contentctl/output/templates/es_investigations_stories.j2 +1 -1
  88. contentctl/output/templates/header.j2 +2 -1
  89. contentctl/output/templates/macros.j2 +6 -10
  90. contentctl/output/templates/savedsearches_baselines.j2 +5 -5
  91. contentctl/output/templates/savedsearches_detections.j2 +36 -33
  92. contentctl/output/templates/savedsearches_investigations.j2 +4 -4
  93. contentctl/output/templates/transforms.j2 +4 -4
  94. contentctl/output/yml_writer.py +2 -2
  95. contentctl/templates/app_template/README.md +7 -0
  96. contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/nav/default.xml +1 -0
  97. contentctl/templates/app_template/lookups/mitre_enrichment.csv +638 -0
  98. contentctl/templates/deployments/{00_default_anomaly.yml → escu_default_configuration_anomaly.yml} +1 -2
  99. contentctl/templates/deployments/{00_default_baseline.yml → escu_default_configuration_baseline.yml} +1 -2
  100. contentctl/templates/deployments/{00_default_correlation.yml → escu_default_configuration_correlation.yml} +2 -2
  101. contentctl/templates/deployments/{00_default_hunting.yml → escu_default_configuration_hunting.yml} +2 -2
  102. contentctl/templates/deployments/{00_default_ttp.yml → escu_default_configuration_ttp.yml} +1 -2
  103. contentctl/templates/detections/anomalous_usage_of_7zip.yml +0 -1
  104. contentctl/templates/stories/cobalt_strike.yml +0 -1
  105. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/METADATA +36 -15
  106. contentctl-4.0.2.dist-info/RECORD +168 -0
  107. contentctl/actions/detection_testing/DataManipulation.py +0 -149
  108. contentctl/actions/generate.py +0 -91
  109. contentctl/helper/config_handler.py +0 -75
  110. contentctl/input/baseline_builder.py +0 -66
  111. contentctl/input/basic_builder.py +0 -58
  112. contentctl/input/detection_builder.py +0 -370
  113. contentctl/input/investigation_builder.py +0 -42
  114. contentctl/input/new_content_generator.py +0 -95
  115. contentctl/input/playbook_builder.py +0 -68
  116. contentctl/input/story_builder.py +0 -106
  117. contentctl/objects/app.py +0 -214
  118. contentctl/objects/repo_config.py +0 -163
  119. contentctl/objects/test_config.py +0 -630
  120. contentctl/output/templates/macros_detections.j2 +0 -7
  121. contentctl/output/templates/splunk_app/README.md +0 -7
  122. contentctl-3.6.0.dist-info/RECORD +0 -176
  123. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_story_detail.txt +0 -0
  124. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_summary.txt +0 -0
  125. /contentctl/{output/templates/splunk_app → templates/app_template}/README/essoc_usage_dashboard.txt +0 -0
  126. /contentctl/{output/templates/splunk_app → templates/app_template}/default/analytic_stories.conf +0 -0
  127. /contentctl/{output/templates/splunk_app → templates/app_template}/default/app.conf +0 -0
  128. /contentctl/{output/templates/splunk_app → templates/app_template}/default/commands.conf +0 -0
  129. /contentctl/{output/templates/splunk_app → templates/app_template}/default/content-version.conf +0 -0
  130. /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/escu_summary.xml +0 -0
  131. /contentctl/{output/templates/splunk_app → templates/app_template}/default/data/ui/views/feedback.xml +0 -0
  132. /contentctl/{output/templates/splunk_app → templates/app_template}/default/distsearch.conf +0 -0
  133. /contentctl/{output/templates/splunk_app → templates/app_template}/default/usage_searches.conf +0 -0
  134. /contentctl/{output/templates/splunk_app → templates/app_template}/default/use_case_library.conf +0 -0
  135. /contentctl/{output/templates/splunk_app → templates/app_template}/metadata/default.meta +0 -0
  136. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon.png +0 -0
  137. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt.png +0 -0
  138. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIconAlt_2x.png +0 -0
  139. /contentctl/{output/templates/splunk_app → templates/app_template}/static/appIcon_2x.png +0 -0
  140. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/LICENSE.md +0 -0
  141. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/WHEEL +0 -0
  142. {contentctl-3.6.0.dist-info → contentctl-4.0.2.dist-info}/entry_points.txt +0 -0
@@ -1,174 +1,246 @@
1
1
  import os
2
2
  import json
3
-
3
+ import pathlib
4
4
 
5
5
  from contentctl.output.json_writer import JsonWriter
6
6
  from contentctl.objects.enums import SecurityContentType
7
+ from contentctl.objects.abstract_security_content_objects.security_content_object_abstract import (
8
+ SecurityContentObject_Abstract,
9
+ )
7
10
 
8
- # Maximum Lambda Request Response Limit is 6MB
9
- # https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html
10
- # Note that if you are not using AWS Lambda, this file size may be increased.
11
- AWS_LAMBDA_LIMIT = 1024 * 1024 * 6 - 1
12
11
 
13
12
 
14
13
  class ApiJsonOutput:
14
+
15
15
  def writeObjects(
16
- self, objects: list, output_path: str, type: SecurityContentType = None
16
+ self,
17
+ objects: list[SecurityContentObject_Abstract],
18
+ output_path: pathlib.Path,
19
+ app_label:str = "ESCU",
20
+ contentType: SecurityContentType = None
17
21
  ) -> None:
18
- if type == SecurityContentType.detections:
19
- obj_array = []
20
- for detection in objects:
21
- detection.id = str(detection.id)
22
- obj_array.append(
23
- detection.dict(
24
- exclude_none=True,
25
- exclude={
26
- "deprecated": True,
27
- "experimental": True,
28
- "annotations": True,
29
- "risk": True,
30
- "playbooks": True,
31
- "baselines": True,
32
- "mappings": True,
33
- "test": True,
34
- "deployment": True,
35
- "type": True,
36
- "status": True,
37
- "data_source": True,
38
- "tests": True,
39
- "cve_enrichment": True,
40
- "file_path": True,
41
- "tags": {
42
- "file_path": True,
43
- "required_fields": True,
44
- "confidence": True,
45
- "impact": True,
46
- "product": True,
47
- "cve": True,
48
- },
49
- },
22
+ """#Serialize all objects
23
+ try:
24
+ for obj in objects:
25
+
26
+ serialized_objects.append(obj.model_dump())
27
+ except Exception as e:
28
+ raise Exception(f"Error serializing object with name '{obj.name}' and type '{type(obj).__name__}': '{str(e)}'")
29
+ """
30
+
31
+ if contentType == SecurityContentType.detections:
32
+ detections = [
33
+ detection.model_dump(
34
+ include=set(
35
+ [
36
+ "name",
37
+ "author",
38
+ "date",
39
+ "version",
40
+ "id",
41
+ "description",
42
+ "tags",
43
+ "search",
44
+ "how_to_implement",
45
+ "known_false_positives",
46
+ "references",
47
+ "datamodel",
48
+ "macros",
49
+ "lookups",
50
+ "source",
51
+ "nes_fields",
52
+ ]
50
53
  )
51
54
  )
52
-
53
- for detection in obj_array:
54
- # Loop through each macro in the detection
55
- for macro in detection["macros"]:
56
- # Remove the 'file_path' key if it exists
57
- macro.pop("file_path", None)
58
-
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
+
59
72
  JsonWriter.writeJsonObject(
60
- os.path.join(output_path, "detections.json"), {"detections": obj_array}
73
+ os.path.join(output_path, "detections.json"), "detections", detections
61
74
  )
62
75
 
63
- ### Code to be added to contentctl to ship filter macros to macros.json
64
-
65
- obj_array = []
66
- for detection in objects:
67
- detection_dict = detection.dict()
68
- if "macros" in detection_dict:
69
- for macro in detection_dict["macros"]:
70
- obj_array.append(macro)
71
-
72
- uniques: set[str] = set()
73
- for obj in obj_array:
74
- if obj.get("arguments", None) != None:
75
- uniques.add(json.dumps(obj, sort_keys=True))
76
- else:
77
- obj.pop("arguments")
78
- uniques.add(json.dumps(obj, sort_keys=True))
79
-
80
- obj_array = []
81
- for item in uniques:
82
- obj_array.append(json.loads(item))
83
-
84
- for obj in obj_array:
85
- if "file_path" in obj:
86
- del obj["file_path"]
87
-
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])
88
85
  JsonWriter.writeJsonObject(
89
- os.path.join(output_path, "macros.json"), {"macros": obj_array}
86
+ os.path.join(output_path, "macros.json"), "macros", macros
90
87
  )
91
88
 
92
- elif type == SecurityContentType.stories:
93
- obj_array = []
94
- for story in objects:
95
- story.id = str(story.id)
96
- obj_array.append(
97
- story.dict(
98
- exclude_none=True,
99
- exclude={"investigations": True, "file_path": True},
89
+ elif contentType == SecurityContentType.stories:
90
+ stories = [
91
+ story.model_dump(
92
+ include=set(
93
+ [
94
+ "name",
95
+ "author",
96
+ "date",
97
+ "version",
98
+ "id",
99
+ "description",
100
+ "narrative",
101
+ "references",
102
+ "tags",
103
+ "detections_names",
104
+ "investigation_names",
105
+ "baseline_names",
106
+ "detections",
107
+ ]
100
108
  )
101
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"]]
125
+
102
126
 
103
127
  JsonWriter.writeJsonObject(
104
- os.path.join(output_path, "stories.json"), {"stories": obj_array}
128
+ os.path.join(output_path, "stories.json"), "stories", stories
105
129
  )
106
130
 
107
- elif type == SecurityContentType.baselines:
108
- obj_array = []
109
- for baseline in objects:
110
- baseline.id = str(baseline.id)
111
- obj_array.append(
112
- baseline.dict(
113
- exclude={
114
- "deployment": True,
115
- "check_references": True,
116
- "file_path": True,
117
- }
131
+ elif contentType == SecurityContentType.baselines:
132
+ try:
133
+ baselines = [
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
+ )
118
152
  )
119
- )
153
+ for baseline in objects
154
+ ]
155
+ except Exception as e:
156
+ print(e)
157
+ print('wait')
120
158
 
121
159
  JsonWriter.writeJsonObject(
122
- os.path.join(output_path, "baselines.json"), {"baselines": obj_array}
123
- )
160
+ os.path.join(output_path, "baselines.json"), "baselines", baselines
161
+ )
124
162
 
125
- elif type == SecurityContentType.investigations:
126
- obj_array = []
127
- for investigation in objects:
128
- investigation.id = str(investigation.id)
129
- obj_array.append(
130
- investigation.dict(
131
- exclude={
132
- "file_path": True,
133
- }
163
+ elif contentType == SecurityContentType.investigations:
164
+ investigations = [
165
+ investigation.model_dump(
166
+ include=set(
167
+ [
168
+ "name",
169
+ "author",
170
+ "date",
171
+ "version",
172
+ "id",
173
+ "description",
174
+ "type",
175
+ "datamodel",
176
+ "search",
177
+ "how_to_implemnet",
178
+ "known_false_positives",
179
+ "references",
180
+ "inputs",
181
+ "tags",
182
+ "lowercase_name",
183
+ ]
134
184
  )
135
185
  )
136
-
186
+ for investigation in objects
187
+ ]
137
188
  JsonWriter.writeJsonObject(
138
189
  os.path.join(output_path, "response_tasks.json"),
139
- {"response_tasks": obj_array},
190
+ "response_tasks",
191
+ investigations,
140
192
  )
141
193
 
142
- elif type == SecurityContentType.lookups:
143
- obj_array = []
144
- for lookup in objects:
145
-
146
- obj_array.append(
147
- lookup.dict(
148
- exclude={
149
- "file_path": True,
150
- }
194
+ elif contentType == SecurityContentType.lookups:
195
+ lookups = [
196
+ lookup.model_dump(
197
+ include=set(
198
+ [
199
+ "name",
200
+ "description",
201
+ "collection",
202
+ "fields_list",
203
+ "filename",
204
+ "default_match",
205
+ "match_type",
206
+ "min_matches",
207
+ "case_sensitive_match",
208
+ ]
151
209
  )
152
210
  )
153
-
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])
154
217
  JsonWriter.writeJsonObject(
155
- os.path.join(output_path, "lookups.json"), {"lookups": obj_array}
218
+ os.path.join(output_path, "lookups.json"), "lookups", lookups
156
219
  )
157
220
 
158
- elif type == SecurityContentType.deployments:
159
- obj_array = []
160
- for deployment in objects:
161
- deployment.id = str(deployment.id)
162
- obj_array.append(
163
- deployment.dict(
164
- exclude_none=True,
165
- exclude={
166
- "file_path": True,
167
- },
221
+ elif contentType == SecurityContentType.deployments:
222
+ deployments = [
223
+ deployment.model_dump(
224
+ include=set(
225
+ [
226
+ "name",
227
+ "author",
228
+ "date",
229
+ "version",
230
+ "id",
231
+ "description",
232
+ "scheduling",
233
+ "rba",
234
+ "tags"
235
+ ]
168
236
  )
169
237
  )
170
-
238
+ for deployment in objects
239
+ ]
240
+ #references are not to be included, but have been deleted in the
241
+ #model_serialization logic
171
242
  JsonWriter.writeJsonObject(
172
243
  os.path.join(output_path, "deployments.json"),
173
- {"deployments": obj_array},
174
- )
244
+ "deployments",
245
+ deployments,
246
+ )
@@ -1,28 +1,39 @@
1
1
  import os
2
+ from typing import List,Union
3
+ import pathlib
2
4
 
3
-
4
- from contentctl.objects.enums import SecurityContentType
5
+ from contentctl.objects.detection import Detection
5
6
  from contentctl.output.attack_nav_writer import AttackNavWriter
6
7
 
7
8
 
8
9
  class AttackNavOutput():
9
10
 
10
- def writeObjects(self, objects: list, output_path: str, type: SecurityContentType = None) -> None:
11
- techniques = dict()
11
+ def writeObjects(self, detections: List[Detection], output_path: pathlib.Path) -> None:
12
+ techniques:dict[str,dict[str,Union[List[str],int]]] = {}
13
+ for detection in detections:
14
+ for tactic in detection.tags.mitre_attack_id:
15
+ if tactic not in techniques:
16
+ techniques[tactic] = {'score':0,'file_paths':[]}
17
+
18
+ detection_url = f"https://github.com/splunk/security_content/blob/develop/detections/{detection.source}/{detection.file_path.name}"
19
+ techniques[tactic]['score'] += 1
20
+ techniques[tactic]['file_paths'].append(detection_url)
21
+
22
+ '''
12
23
  for detection in objects:
13
24
  if detection.tags.mitre_attack_enrichments:
14
25
  for mitre_attack_enrichment in detection.tags.mitre_attack_enrichments:
15
26
  if not mitre_attack_enrichment.mitre_attack_id in techniques:
16
27
  techniques[mitre_attack_enrichment.mitre_attack_id] = {
17
28
  'score': 1,
18
- 'file_paths': ['https://github.com/splunk/security_content/blob/develop/detections/' + detection.source + '/' + self.convertNameToFileName(detection.name)]
29
+ 'file_paths': ['https://github.com/splunk/security_content/blob/develop/detections/' + detection.getSource() + '/' + self.convertNameToFileName(detection.name)]
19
30
  }
20
31
  else:
21
32
  techniques[mitre_attack_enrichment.mitre_attack_id]['score'] = techniques[mitre_attack_enrichment.mitre_attack_id]['score'] + 1
22
- techniques[mitre_attack_enrichment.mitre_attack_id]['file_paths'].append('https://github.com/splunk/security_content/blob/develop/detections/' + detection.source + '/' + self.convertNameToFileName(detection.name))
23
-
24
- AttackNavWriter.writeAttackNavFile(techniques, os.path.join(output_path, 'coverage.json'))
25
-
33
+ techniques[mitre_attack_enrichment.mitre_attack_id]['file_paths'].append('https://github.com/splunk/security_content/blob/develop/detections/' + detection.getSource() + '/' + self.convertNameToFileName(detection.name))
34
+ '''
35
+ AttackNavWriter.writeAttackNavFile(techniques, output_path / 'coverage.json')
36
+
26
37
 
27
38
  def convertNameToFileName(self, name: str):
28
39
  file_name = name \
@@ -1,7 +1,7 @@
1
1
 
2
2
  import json
3
-
4
-
3
+ from typing import Union, List
4
+ import pathlib
5
5
  VERSION = "4.3"
6
6
  NAME = "Detection Coverage"
7
7
  DESCRIPTION = "security_content detection coverage"
@@ -11,7 +11,7 @@ DOMAIN = "mitre-enterprise"
11
11
  class AttackNavWriter():
12
12
 
13
13
  @staticmethod
14
- def writeAttackNavFile(mitre_techniques : dict, output_path : str) -> None:
14
+ def writeAttackNavFile(mitre_techniques : dict[str,dict[str,Union[List[str],int]]], output_path : pathlib.Path) -> None:
15
15
  max_count = 0
16
16
  for technique_id in mitre_techniques.keys():
17
17
  if mitre_techniques[technique_id]['score'] > max_count:
@@ -21,7 +21,7 @@ class BAYmlOutput():
21
21
  YmlWriter.writeYmlFile(file_path, object)
22
22
 
23
23
 
24
- def writeObjects(self, objects: list, output_path: str, type: SecurityContentType = None) -> None:
24
+ def writeObjects(self, objects: list, output_path: str, contentType: SecurityContentType = None) -> None:
25
25
  for obj in objects:
26
26
  file_name = "ssa___" + self.convertNameToFileName(obj.name, obj.tags)
27
27
  if self.isComplexBARule(obj.search):
@@ -46,8 +46,9 @@ class BAYmlOutput():
46
46
  }
47
47
  test_dict["tests"][0]["name"] = obj.name
48
48
  for count in range(len(test_dict["tests"][0]["attack_data"])):
49
- a = urlparse(test_dict["tests"][0]["attack_data"][count]["data"])
49
+ a = urlparse(str(test_dict["tests"][0]["attack_data"][count]["data"]))
50
50
  test_dict["tests"][0]["attack_data"][count]["file_name"] = os.path.basename(a.path)
51
+
51
52
  test = UnitTestOld.parse_obj(test_dict)
52
53
 
53
54
  obj.test = test
@@ -150,4 +151,3 @@ class BAYmlOutput():
150
151
  def isComplexBARule(self, search):
151
152
  return re.findall("stats|first_time_event|adaptive_threshold", search)
152
153
 
153
-