11import json
2- from typing import Union , List
32import pathlib
3+ from typing import List , Union
44
5- VERSION = "4.3 "
5+ VERSION = "4.5 "
66NAME = "Detection Coverage"
7- DESCRIPTION = "security_content detection coverage "
8- DOMAIN = "mitre- enterprise"
7+ DESCRIPTION = "Security Content Detection Coverage "
8+ DOMAIN = "enterprise-attack "
99
1010
1111class AttackNavWriter :
@@ -14,52 +14,68 @@ def writeAttackNavFile(
1414 mitre_techniques : dict [str , dict [str , Union [List [str ], int ]]],
1515 output_path : pathlib .Path ,
1616 ) -> 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+ )
2120
2221 layer_json = {
23- "version " : VERSION ,
22+ "versions " : { "attack" : "16" , "navigator" : "5.1.0" , "layer" : VERSION } ,
2423 "name" : NAME ,
2524 "description" : DESCRIPTION ,
2625 "domain" : DOMAIN ,
2726 "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 ,
2859 }
2960
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 ("|" )
4866
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+ )
5371
54- layer_json ["showTacticRowBackground" ] = True
55- layer_json ["tacticRowBackground" ] = "#dddddd"
56- layer_json ["sorting" ] = 3
72+ links .append ({"label" : detection_name , "url" : research_url })
5773
58- for technique_id in mitre_techniques .keys ():
5974 layer_technique = {
6075 "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 ,
6379 }
6480 layer_json ["techniques" ].append (layer_technique )
6581
0 commit comments