1
1
import json
2
- from typing import Union , List
3
2
import pathlib
3
+ from typing import List , Union
4
4
5
- VERSION = "4.3 "
5
+ VERSION = "4.5 "
6
6
NAME = "Detection Coverage"
7
- DESCRIPTION = "security_content detection coverage "
8
- DOMAIN = "mitre- enterprise"
7
+ DESCRIPTION = "Security Content Detection Coverage "
8
+ DOMAIN = "enterprise-attack "
9
9
10
10
11
11
class AttackNavWriter :
@@ -14,52 +14,68 @@ def writeAttackNavFile(
14
14
mitre_techniques : dict [str , dict [str , Union [List [str ], int ]]],
15
15
output_path : pathlib .Path ,
16
16
) -> None :
17
- max_count = 0
18
- for technique_id in mitre_techniques .keys ():
19
- if mitre_techniques [technique_id ]["score" ] > max_count :
20
- max_count = mitre_techniques [technique_id ]["score" ]
17
+ max_count = max (
18
+ (technique ["score" ] for technique in mitre_techniques .values ()), default = 0
19
+ )
21
20
22
21
layer_json = {
23
- "version " : VERSION ,
22
+ "versions " : { "attack" : "16" , "navigator" : "5.1.0" , "layer" : VERSION } ,
24
23
"name" : NAME ,
25
24
"description" : DESCRIPTION ,
26
25
"domain" : DOMAIN ,
27
26
"techniques" : [],
27
+ "gradient" : {
28
+ "colors" : ["#ffffff" , "#66b1ff" , "#096ed7" ],
29
+ "minValue" : 0 ,
30
+ "maxValue" : max_count ,
31
+ },
32
+ "filters" : {
33
+ "platforms" : [
34
+ "Windows" ,
35
+ "Linux" ,
36
+ "macOS" ,
37
+ "Network" ,
38
+ "AWS" ,
39
+ "GCP" ,
40
+ "Azure" ,
41
+ "Azure AD" ,
42
+ "Office 365" ,
43
+ "SaaS" ,
44
+ ]
45
+ },
46
+ "layout" : {
47
+ "layout" : "side" ,
48
+ "showName" : True ,
49
+ "showID" : True ,
50
+ "showAggregateScores" : False ,
51
+ },
52
+ "legendItems" : [
53
+ {"label" : "No detections" , "color" : "#ffffff" },
54
+ {"label" : "Has detections" , "color" : "#66b1ff" },
55
+ ],
56
+ "showTacticRowBackground" : True ,
57
+ "tacticRowBackground" : "#dddddd" ,
58
+ "selectTechniquesAcrossTactics" : True ,
28
59
}
29
60
30
- layer_json ["gradient" ] = {
31
- "colors" : ["#ffffff" , "#66b1ff" , "#096ed7" ],
32
- "minValue" : 0 ,
33
- "maxValue" : max_count ,
34
- }
35
-
36
- layer_json ["filters" ] = {
37
- "platforms" : [
38
- "Windows" ,
39
- "Linux" ,
40
- "macOS" ,
41
- "AWS" ,
42
- "GCP" ,
43
- "Azure" ,
44
- "Office 365" ,
45
- "SaaS" ,
46
- ]
47
- }
61
+ for technique_id , data in mitre_techniques .items ():
62
+ links = []
63
+ for detection_info in data ["file_paths" ]:
64
+ # Split the detection info into its components
65
+ detection_type , detection_id , detection_name = detection_info .split ("|" )
48
66
49
- layer_json [ "legendItems" ] = [
50
- { "label" : "NO available detections" , "color" : "#ffffff" },
51
- { "label" : "Some detections available" , "color" : "#66b1ff" },
52
- ]
67
+ # Construct research website URL (without the name)
68
+ research_url = (
69
+ f"https://research.splunk.com/ { detection_type } / { detection_id } /"
70
+ )
53
71
54
- layer_json ["showTacticRowBackground" ] = True
55
- layer_json ["tacticRowBackground" ] = "#dddddd"
56
- layer_json ["sorting" ] = 3
72
+ links .append ({"label" : detection_name , "url" : research_url })
57
73
58
- for technique_id in mitre_techniques .keys ():
59
74
layer_technique = {
60
75
"techniqueID" : technique_id ,
61
- "score" : mitre_techniques [technique_id ]["score" ],
62
- "comment" : "\n \n " .join (mitre_techniques [technique_id ]["file_paths" ]),
76
+ "score" : data ["score" ],
77
+ "enabled" : True ,
78
+ "links" : links ,
63
79
}
64
80
layer_json ["techniques" ].append (layer_technique )
65
81
0 commit comments