Files
Anthropic-Cybersecurity-Skills/skills/building-incident-response-playbook/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

231 lines
9.5 KiB
Python

#!/usr/bin/env python3
"""Agent for building and managing incident response playbooks."""
import os
import json
import argparse
from datetime import datetime
import requests
PLAYBOOK_TYPES = {
"ransomware": {
"name": "Ransomware Incident Response",
"trigger": "EDR alert for file encryption activity or ransom note detection",
"severity_default": "P1",
"phases": {
"detection": [
"EDR alerts on mass file encryption (CrowdStrike, SentinelOne)",
"Canary file modification alerts from deception tools",
"User reports of inaccessible files or ransom note",
],
"containment": [
"Isolate affected hosts via EDR network containment",
"Block C2 IPs/domains at firewall and DNS sinkhole",
"Disable compromised accounts in Active Directory",
"Segment affected network VLANs at switch level",
],
"eradication": [
"Identify ransomware variant and initial access vector",
"Remove persistence mechanisms (scheduled tasks, services, registry)",
"Scan enterprise for IOCs using EDR threat hunt",
"Patch exploited vulnerability if applicable",
],
"recovery": [
"Restore from verified clean backups (test integrity first)",
"Rebuild compromised systems from gold images",
"Re-enable accounts with forced password reset and MFA",
"Monitor restored systems for 72 hours post-recovery",
],
},
},
"phishing": {
"name": "Phishing Incident Response",
"trigger": "User report via abuse mailbox or phishing button",
"severity_default": "P2",
"phases": {
"detection": [
"User report to abuse@company.com or phish report button",
"Email gateway alert on suspicious attachment/link",
"SIEM correlation of multiple reports for same sender",
],
"containment": [
"Quarantine phishing email from all mailboxes (O365 purge or Exchange)",
"Block sender domain and reply-to address in email gateway",
"If credentials entered: force password reset and revoke sessions",
"Block malicious URL at web proxy",
],
"eradication": [
"Extract and submit IOCs (URLs, hashes, sender) to TI platform",
"Search email logs for all recipients of the phishing email",
"Verify no malware payload executed on endpoints",
"Update email filtering rules to catch similar campaigns",
],
"recovery": [
"Notify affected users with guidance on what to watch for",
"Restore any quarantined legitimate emails (false positives)",
"Schedule targeted phishing awareness training for clickers",
],
},
},
"data_breach": {
"name": "Data Breach / Exfiltration Response",
"trigger": "DLP alert, anomalous data transfer, or external notification",
"severity_default": "P1",
"phases": {
"detection": [
"DLP policy violation alert on sensitive data movement",
"Network anomaly detection for large outbound transfers",
"Cloud access broker alert for unauthorized file sharing",
],
"containment": [
"Block exfiltration channel (IP, domain, cloud service)",
"Revoke access for compromised account",
"Preserve affected system for forensic imaging",
"Engage legal counsel for breach notification assessment",
],
"eradication": [
"Determine scope: what data, how much, which systems",
"Identify root cause and attack vector",
"Remove attacker access and persistence",
"Audit all access to affected data stores",
],
"recovery": [
"Implement additional DLP controls on affected data",
"Reset credentials for all accounts with access to breached data",
"Execute breach notification plan if PII/PHI involved",
"Engage external forensics firm if required by cyber insurance",
],
},
},
}
def generate_playbook(playbook_type):
"""Generate a structured IR playbook for the given incident type."""
template = PLAYBOOK_TYPES.get(playbook_type)
if not template:
return {"error": f"Unknown playbook type: {playbook_type}"}
playbook = {
"metadata": {
"name": template["name"],
"version": "1.0",
"created": datetime.utcnow().isoformat(),
"owner": "SOC Manager",
"default_severity": template["severity_default"],
"trigger": template["trigger"],
"framework": "NIST SP 800-61r3",
},
"raci": {
"detection": {"R": "SOC L1", "A": "SOC L2", "C": "IR Lead", "I": ""},
"containment": {"R": "SOC L2", "A": "IR Lead", "C": "CISO", "I": "Legal"},
"eradication": {"R": "IR Lead", "A": "CISO", "C": "IT Ops", "I": "Mgmt"},
"recovery": {"R": "IT Ops", "A": "IR Lead", "C": "Business Owner", "I": "CISO"},
"post_incident": {"R": "IR Lead", "A": "CISO", "C": "All", "I": "Exec"},
},
"phases": template["phases"],
"escalation": {
"P1": "Immediate: IR Lead + CISO + Legal within 15 minutes",
"P2": "IR Lead notified within 30 minutes",
"P3": "Queued for investigation within 4 hours",
},
"communication": {
"internal_cadence": "Every 2 hours during active incident",
"executive_update": "Within 1 hour of P1 declaration",
"regulatory": "Within 72 hours if personal data involved (GDPR)",
},
}
return playbook
def check_thehive_cases(thehive_url, api_key):
"""List open incident cases from TheHive."""
headers = {"Authorization": f"Bearer {api_key}"}
resp = requests.post(f"{thehive_url}/api/v1/query", headers=headers, json={
"query": [
{"_name": "listCase"},
{"_name": "filter", "_field": "status", "_value": "Open"},
{"_name": "sort", "_fields": [{"startDate": "desc"}]},
{"_name": "page", "from": 0, "to": 50},
]
}, timeout=30)
resp.raise_for_status()
cases = []
for c in resp.json():
cases.append({
"number": c.get("number"),
"title": c.get("title"),
"severity": c.get("severity"),
"status": c.get("status"),
"start_date": c.get("startDate"),
"owner": c.get("owner"),
})
return cases
def calculate_ir_metrics(thehive_url, api_key, days=30):
"""Calculate incident response metrics from TheHive cases."""
headers = {"Authorization": f"Bearer {api_key}"}
resp = requests.post(f"{thehive_url}/api/v1/query", headers=headers, json={
"query": [
{"_name": "listCase"},
{"_name": "filter", "_field": "status", "_value": "Resolved"},
{"_name": "page", "from": 0, "to": 500},
]
}, timeout=30)
resp.raise_for_status()
cases = resp.json()
total_resolve_ms = 0
count = 0
for c in cases:
if c.get("endDate") and c.get("startDate"):
resolve_ms = c["endDate"] - c["startDate"]
total_resolve_ms += resolve_ms
count += 1
avg_mttr_hours = (total_resolve_ms / count / 3600000) if count else 0
return {"resolved_cases": count, "avg_mttr_hours": round(avg_mttr_hours, 1)}
def main():
parser = argparse.ArgumentParser(description="Incident Response Playbook Agent")
parser.add_argument("--playbook-type", choices=list(PLAYBOOK_TYPES.keys()))
parser.add_argument("--thehive-url", default=os.getenv("THEHIVE_URL"))
parser.add_argument("--thehive-key", default=os.getenv("THEHIVE_API_KEY"))
parser.add_argument("--output", default="ir_playbook_output.json")
parser.add_argument("--action", choices=[
"generate", "list_cases", "metrics", "all_playbooks"
], default="generate")
args = parser.parse_args()
report = {"generated_at": datetime.utcnow().isoformat()}
if args.action == "generate" and args.playbook_type:
playbook = generate_playbook(args.playbook_type)
report["playbook"] = playbook
print(f"[+] Generated playbook: {playbook['metadata']['name']}")
if args.action == "all_playbooks":
report["playbooks"] = {}
for ptype in PLAYBOOK_TYPES:
report["playbooks"][ptype] = generate_playbook(ptype)
print(f"[+] Generated: {PLAYBOOK_TYPES[ptype]['name']}")
if args.action == "list_cases" and args.thehive_url:
cases = check_thehive_cases(args.thehive_url, args.thehive_key)
report["open_cases"] = cases
print(f"[+] Open cases: {len(cases)}")
if args.action == "metrics" and args.thehive_url:
metrics = calculate_ir_metrics(args.thehive_url, args.thehive_key)
report["metrics"] = metrics
print(f"[+] Avg MTTR: {metrics['avg_mttr_hours']}h across {metrics['resolved_cases']} cases")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Output saved to {args.output}")
if __name__ == "__main__":
main()