mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-10 21:24:56 +03:00
c21af3347e
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
177 lines
6.8 KiB
Python
177 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""MITRE ATT&CK Coverage Mapping Agent - maps detection rules to ATT&CK techniques and identifies gaps."""
|
|
|
|
import json
|
|
import argparse
|
|
import logging
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
ENTERPRISE_TACTICS = [
|
|
"TA0001", "TA0002", "TA0003", "TA0004", "TA0005",
|
|
"TA0006", "TA0007", "TA0008", "TA0009", "TA0010",
|
|
"TA0011", "TA0040", "TA0042", "TA0043",
|
|
]
|
|
|
|
TACTIC_NAMES = {
|
|
"TA0001": "Initial Access", "TA0002": "Execution", "TA0003": "Persistence",
|
|
"TA0004": "Privilege Escalation", "TA0005": "Defense Evasion",
|
|
"TA0006": "Credential Access", "TA0007": "Discovery", "TA0008": "Lateral Movement",
|
|
"TA0009": "Collection", "TA0010": "Exfiltration", "TA0011": "Command and Control",
|
|
"TA0040": "Impact", "TA0042": "Resource Development", "TA0043": "Reconnaissance",
|
|
}
|
|
|
|
|
|
def load_detection_rules(filepath):
|
|
"""Load detection rules with ATT&CK mappings."""
|
|
with open(filepath) as f:
|
|
return json.load(f)
|
|
|
|
|
|
def load_attack_matrix(filepath):
|
|
"""Load ATT&CK enterprise matrix (techniques per tactic)."""
|
|
with open(filepath) as f:
|
|
return json.load(f)
|
|
|
|
|
|
def map_rules_to_techniques(rules):
|
|
"""Map detection rules to ATT&CK technique IDs."""
|
|
technique_rules = defaultdict(list)
|
|
unmapped = []
|
|
for rule in rules:
|
|
techniques = rule.get("mitre_attack", [])
|
|
if not techniques:
|
|
unmapped.append(rule.get("name", "unknown"))
|
|
continue
|
|
for tech in techniques:
|
|
technique_rules[tech].append({
|
|
"rule_name": rule.get("name", ""),
|
|
"severity": rule.get("severity", "medium"),
|
|
"data_source": rule.get("data_source", ""),
|
|
"enabled": rule.get("enabled", True),
|
|
})
|
|
return dict(technique_rules), unmapped
|
|
|
|
|
|
def calculate_coverage(technique_rules, attack_matrix):
|
|
"""Calculate coverage percentage per tactic."""
|
|
tactic_coverage = {}
|
|
for tactic_id, tactic_name in TACTIC_NAMES.items():
|
|
techniques_in_tactic = attack_matrix.get(tactic_id, [])
|
|
total = len(techniques_in_tactic)
|
|
covered = sum(1 for t in techniques_in_tactic if t in technique_rules)
|
|
tactic_coverage[tactic_id] = {
|
|
"tactic_name": tactic_name,
|
|
"total_techniques": total,
|
|
"covered": covered,
|
|
"coverage_percent": round(covered / max(total, 1) * 100, 1),
|
|
"gaps": [t for t in techniques_in_tactic if t not in technique_rules],
|
|
}
|
|
return tactic_coverage
|
|
|
|
|
|
def identify_priority_gaps(tactic_coverage, priority_techniques=None):
|
|
"""Identify high-priority coverage gaps."""
|
|
gaps = []
|
|
for tactic_id, data in tactic_coverage.items():
|
|
for tech in data["gaps"]:
|
|
priority = "high" if (priority_techniques and tech in priority_techniques) else "medium"
|
|
gaps.append({
|
|
"technique": tech,
|
|
"tactic": data["tactic_name"],
|
|
"tactic_id": tactic_id,
|
|
"priority": priority,
|
|
})
|
|
return sorted(gaps, key=lambda x: (0 if x["priority"] == "high" else 1, x["tactic"]))
|
|
|
|
|
|
def calculate_detection_depth(technique_rules):
|
|
"""Assess detection depth per technique (number of rules covering it)."""
|
|
depth = {}
|
|
for tech, rules in technique_rules.items():
|
|
enabled_rules = [r for r in rules if r["enabled"]]
|
|
data_sources = list(set(r["data_source"] for r in enabled_rules if r["data_source"]))
|
|
depth[tech] = {
|
|
"total_rules": len(rules),
|
|
"enabled_rules": len(enabled_rules),
|
|
"data_sources": data_sources,
|
|
"depth": "deep" if len(enabled_rules) >= 3 else "moderate" if len(enabled_rules) >= 2 else "shallow",
|
|
}
|
|
return depth
|
|
|
|
|
|
def generate_navigator_layer(technique_rules, tactic_coverage):
|
|
"""Generate ATT&CK Navigator layer JSON."""
|
|
techniques = []
|
|
for tech_id, rules in technique_rules.items():
|
|
score = min(len(rules), 4)
|
|
techniques.append({
|
|
"techniqueID": tech_id,
|
|
"score": score,
|
|
"comment": f"{len(rules)} detection rules",
|
|
"enabled": True,
|
|
})
|
|
layer = {
|
|
"name": "Detection Coverage",
|
|
"versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"},
|
|
"domain": "enterprise-attack",
|
|
"techniques": techniques,
|
|
"gradient": {"colors": ["#ffffff", "#66b1ff", "#0044cc"], "minValue": 0, "maxValue": 4},
|
|
}
|
|
return layer
|
|
|
|
|
|
def generate_report(rules, technique_rules, unmapped, tactic_coverage, depth):
|
|
total_techniques_covered = len(technique_rules)
|
|
total_rules = len(rules)
|
|
report = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"total_detection_rules": total_rules,
|
|
"mapped_rules": total_rules - len(unmapped),
|
|
"unmapped_rules": len(unmapped),
|
|
"techniques_covered": total_techniques_covered,
|
|
"tactic_coverage": tactic_coverage,
|
|
"detection_depth_summary": {
|
|
"deep": sum(1 for d in depth.values() if d["depth"] == "deep"),
|
|
"moderate": sum(1 for d in depth.values() if d["depth"] == "moderate"),
|
|
"shallow": sum(1 for d in depth.values() if d["depth"] == "shallow"),
|
|
},
|
|
"priority_gaps": identify_priority_gaps(tactic_coverage)[:20],
|
|
}
|
|
return report
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="MITRE ATT&CK Coverage Mapping Agent")
|
|
parser.add_argument("--rules", required=True, help="JSON file with detection rules and ATT&CK mappings")
|
|
parser.add_argument("--matrix", help="ATT&CK matrix JSON (techniques per tactic)")
|
|
parser.add_argument("--navigator-output", help="Output ATT&CK Navigator layer JSON")
|
|
parser.add_argument("--output", default="attack_coverage_report.json")
|
|
args = parser.parse_args()
|
|
|
|
rules = load_detection_rules(args.rules)
|
|
attack_matrix = load_attack_matrix(args.matrix) if args.matrix else {t: [] for t in ENTERPRISE_TACTICS}
|
|
|
|
technique_rules, unmapped = map_rules_to_techniques(rules)
|
|
tactic_coverage = calculate_coverage(technique_rules, attack_matrix)
|
|
depth = calculate_detection_depth(technique_rules)
|
|
report = generate_report(rules, technique_rules, unmapped, tactic_coverage, depth)
|
|
|
|
if args.navigator_output:
|
|
layer = generate_navigator_layer(technique_rules, tactic_coverage)
|
|
with open(args.navigator_output, "w") as f:
|
|
json.dump(layer, f, indent=2)
|
|
logger.info("Navigator layer saved to %s", args.navigator_output)
|
|
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
logger.info("Coverage: %d techniques covered by %d rules", len(technique_rules), len(rules))
|
|
print(json.dumps(report, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|