mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 22:54:53 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- 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
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user