Skip to content
Draft
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
74 changes: 74 additions & 0 deletions scripts/analyze_ttp_risk_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""
Analyze TTP detections with multiple risk_objects.

Finds all YAML files under detections/**/*.yml where:
- type == TTP
- rba.risk_objects has 2 or more entries

Aggregates results by the set of risk_object types and reports
files that do NOT follow the standard (1 user + 1 system) pattern.

Usage:
uv run --with pyyaml scripts/analyze_ttp_risk_objects.py
"""

import os
import yaml
from collections import defaultdict

DETECTIONS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "detections")


def main():
results = defaultdict(list)
total_ttp_with_multi_risk = 0

for root, dirs, files in os.walk(DETECTIONS_DIR):
for fname in files:
if not fname.endswith(".yml"):
continue
fpath = os.path.join(root, fname)
try:
with open(fpath) as f:
data = yaml.safe_load(f)
except Exception:
continue

if not isinstance(data, dict):
continue
if data.get("type") != "TTP":
continue

rba = data.get("rba")
if not isinstance(rba, dict):
continue

risk_objects = rba.get("risk_objects", [])
if not isinstance(risk_objects, list) or len(risk_objects) < 2:
continue

total_ttp_with_multi_risk += 1
types = tuple(sorted(ro.get("type", "unknown") for ro in risk_objects))
results[types].append(fpath)

print(f"Total TTP files with 2+ risk_objects: {total_ttp_with_multi_risk}\n")
print("Aggregated by risk_object_type set:")
for type_combo, files in sorted(results.items(), key=lambda x: -len(x[1])):
print(f" {list(type_combo)} — {len(files)} file(s)")

print("\nNon-(1 user + 1 system) combos:")
non_standard_total = 0
for type_combo, files in sorted(results.items(), key=lambda x: -len(x[1])):
if type_combo == ("system", "user"):
continue
non_standard_total += len(files)
print(f" {list(type_combo)} — {len(files)} file(s)")
for f in files:
print(f" {os.path.relpath(f, DETECTIONS_DIR)}")

print(f"\nTotal non-(1 user + 1 system): {non_standard_total}")


if __name__ == "__main__":
main()