Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions contentctl/output/attack_nav_output.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List, Union
import pathlib
from typing import List, Union

from contentctl.objects.detection import Detection
from contentctl.output.attack_nav_writer import AttackNavWriter
Expand All @@ -10,14 +10,21 @@ def writeObjects(
self, detections: List[Detection], output_path: pathlib.Path
) -> None:
techniques: dict[str, dict[str, Union[List[str], int]]] = {}

for detection in detections:
for tactic in detection.tags.mitre_attack_id:
if tactic not in techniques:
techniques[tactic] = {"score": 0, "file_paths": []}

detection_url = f"https://github.com/splunk/security_content/blob/develop/detections/{detection.source}/{detection.file_path.name}"
techniques[tactic]["score"] += 1
techniques[tactic]["file_paths"].append(detection_url)
detection_type = detection.source
detection_id = detection.id

# Store all three pieces of information separately
detection_info = f"{detection_type}|{detection_id}|{detection.name}"

techniques[tactic]["score"] = techniques[tactic].get("score", 0) + 1
if isinstance(techniques[tactic]["file_paths"], list):
techniques[tactic]["file_paths"].append(detection_info)

"""
for detection in objects:
Expand Down
90 changes: 53 additions & 37 deletions contentctl/output/attack_nav_writer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json
from typing import Union, List
import pathlib
from typing import List, Union

VERSION = "4.3"
VERSION = "4.5"
NAME = "Detection Coverage"
DESCRIPTION = "security_content detection coverage"
DOMAIN = "mitre-enterprise"
DESCRIPTION = "Security Content Detection Coverage"
DOMAIN = "enterprise-attack"


class AttackNavWriter:
Expand All @@ -14,52 +14,68 @@ def writeAttackNavFile(
mitre_techniques: dict[str, dict[str, Union[List[str], int]]],
output_path: pathlib.Path,
) -> None:
max_count = 0
for technique_id in mitre_techniques.keys():
if mitre_techniques[technique_id]["score"] > max_count:
max_count = mitre_techniques[technique_id]["score"]
max_count = max(
(technique["score"] for technique in mitre_techniques.values()), default=0
)

layer_json = {
"version": VERSION,
"versions": {"attack": "16", "navigator": "5.1.0", "layer": VERSION},
"name": NAME,
"description": DESCRIPTION,
"domain": DOMAIN,
"techniques": [],
"gradient": {
"colors": ["#ffffff", "#66b1ff", "#096ed7"],
"minValue": 0,
"maxValue": max_count,
},
"filters": {
"platforms": [
"Windows",
"Linux",
"macOS",
"Network",
"AWS",
"GCP",
"Azure",
"Azure AD",
"Office 365",
"SaaS",
]
},
"layout": {
"layout": "side",
"showName": True,
"showID": True,
"showAggregateScores": False,
},
"legendItems": [
{"label": "No detections", "color": "#ffffff"},
{"label": "Has detections", "color": "#66b1ff"},
],
"showTacticRowBackground": True,
"tacticRowBackground": "#dddddd",
"selectTechniquesAcrossTactics": True,
}

layer_json["gradient"] = {
"colors": ["#ffffff", "#66b1ff", "#096ed7"],
"minValue": 0,
"maxValue": max_count,
}

layer_json["filters"] = {
"platforms": [
"Windows",
"Linux",
"macOS",
"AWS",
"GCP",
"Azure",
"Office 365",
"SaaS",
]
}
for technique_id, data in mitre_techniques.items():
links = []
for detection_info in data["file_paths"]:
# Split the detection info into its components
detection_type, detection_id, detection_name = detection_info.split("|")

layer_json["legendItems"] = [
{"label": "NO available detections", "color": "#ffffff"},
{"label": "Some detections available", "color": "#66b1ff"},
]
# Construct research website URL (without the name)
research_url = (
f"https://research.splunk.com/{detection_type}/{detection_id}/"
)

layer_json["showTacticRowBackground"] = True
layer_json["tacticRowBackground"] = "#dddddd"
layer_json["sorting"] = 3
links.append({"label": detection_name, "url": research_url})

for technique_id in mitre_techniques.keys():
layer_technique = {
"techniqueID": technique_id,
"score": mitre_techniques[technique_id]["score"],
"comment": "\n\n".join(mitre_techniques[technique_id]["file_paths"]),
"score": data["score"],
"enabled": True,
"links": links,
}
layer_json["techniques"].append(layer_technique)

Expand Down
Loading