Files
Anthropic-Cybersecurity-Skills/skills/building-patch-tuesday-response-process/scripts/agent.py
T
mukul975 c21af3347e 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
2026-03-11 00:22:12 +01:00

162 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""Patch Tuesday Response Agent - Tracks Microsoft patches, assesses risk, and prioritizes deployment."""
import json
import logging
import argparse
from datetime import datetime
import requests
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
MSRC_API_BASE = "https://api.msrc.microsoft.com/cvrf/v3.0"
def fetch_patch_tuesday_updates(api_key, year_month):
"""Fetch Microsoft Security Update Guide data via MSRC API."""
headers = {"api-key": api_key, "Accept": "application/json"}
resp = requests.get(f"{MSRC_API_BASE}/Updates('{year_month}')", headers=headers, timeout=30)
resp.raise_for_status()
data = resp.json()
logger.info("Fetched MSRC update for %s", year_month)
return data
def parse_vulnerabilities(cvrf_data):
"""Parse CVRF data to extract vulnerability details."""
vulns = []
for vuln in cvrf_data.get("Vulnerability", []):
cve_id = vuln.get("CVE", "")
title = vuln.get("Title", {}).get("Value", "")
severity = "unknown"
cvss_score = 0.0
exploited = False
for threat in vuln.get("Threats", []):
desc = threat.get("Description", {}).get("Value", "")
if "Exploited:Yes" in desc:
exploited = True
if threat.get("Type") == 3:
severity = desc
for score_set in vuln.get("CVSSScoreSets", []):
base = score_set.get("BaseScore", 0.0)
if base > cvss_score:
cvss_score = base
affected_products = []
for product_status in vuln.get("ProductStatuses", []):
for pid in product_status.get("ProductID", []):
affected_products.append(pid)
vulns.append({
"cve": cve_id, "title": title, "severity": severity,
"cvss_score": cvss_score, "exploited_in_wild": exploited,
"affected_product_ids": affected_products[:10],
})
logger.info("Parsed %d vulnerabilities", len(vulns))
return vulns
def check_cisa_kev(cve_list):
"""Check CVEs against CISA Known Exploited Vulnerabilities catalog."""
try:
resp = requests.get("https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json", timeout=15)
resp.raise_for_status()
kev_data = resp.json()
kev_cves = {v["cveID"] for v in kev_data.get("vulnerabilities", [])}
in_kev = [cve for cve in cve_list if cve in kev_cves]
logger.info("Found %d CVEs in CISA KEV", len(in_kev))
return in_kev
except requests.RequestException as e:
logger.warning("Failed to check CISA KEV: %s", e)
return []
def prioritize_patches(vulns, kev_cves):
"""Prioritize patches based on CVSS, exploitation status, and CISA KEV."""
for vuln in vulns:
priority_score = 0
if vuln["exploited_in_wild"]:
priority_score += 40
if vuln["cve"] in kev_cves:
priority_score += 30
if vuln["cvss_score"] >= 9.0:
priority_score += 20
elif vuln["cvss_score"] >= 7.0:
priority_score += 10
if vuln["severity"].lower() == "critical":
priority_score += 10
vuln["priority_score"] = priority_score
if priority_score >= 60:
vuln["deployment_urgency"] = "emergency"
vuln["sla_hours"] = 24
elif priority_score >= 40:
vuln["deployment_urgency"] = "critical"
vuln["sla_hours"] = 72
elif priority_score >= 20:
vuln["deployment_urgency"] = "standard"
vuln["sla_hours"] = 168
else:
vuln["deployment_urgency"] = "routine"
vuln["sla_hours"] = 720
return sorted(vulns, key=lambda v: v["priority_score"], reverse=True)
def generate_deployment_plan(prioritized_vulns):
"""Generate phased deployment plan."""
phases = {"emergency_24h": [], "critical_72h": [], "standard_7d": [], "routine_30d": []}
for vuln in prioritized_vulns:
urgency = vuln.get("deployment_urgency", "routine")
entry = {"cve": vuln["cve"], "title": vuln["title"], "cvss": vuln["cvss_score"],
"exploited": vuln["exploited_in_wild"]}
if urgency == "emergency":
phases["emergency_24h"].append(entry)
elif urgency == "critical":
phases["critical_72h"].append(entry)
elif urgency == "standard":
phases["standard_7d"].append(entry)
else:
phases["routine_30d"].append(entry)
return phases
def generate_report(vulns, kev_cves, deployment_plan, year_month):
"""Generate Patch Tuesday response report."""
report = {
"timestamp": datetime.utcnow().isoformat(),
"patch_tuesday": year_month,
"total_vulnerabilities": len(vulns),
"actively_exploited": sum(1 for v in vulns if v.get("exploited_in_wild")),
"in_cisa_kev": len(kev_cves),
"critical_cvss": sum(1 for v in vulns if v.get("cvss_score", 0) >= 9.0),
"deployment_plan": deployment_plan,
"top_10_priority": [{"cve": v["cve"], "title": v["title"], "cvss": v["cvss_score"],
"exploited": v["exploited_in_wild"], "priority": v["priority_score"]}
for v in vulns[:10]],
}
print(f"PATCH TUESDAY REPORT ({year_month}): {len(vulns)} vulns, "
f"{report['actively_exploited']} exploited, {len(kev_cves)} in KEV")
return report
def main():
parser = argparse.ArgumentParser(description="Patch Tuesday Response Process Agent")
parser.add_argument("--api-key", required=True, help="MSRC API key")
parser.add_argument("--month", required=True, help="Year-Month (e.g., 2024-Jan)")
parser.add_argument("--output", default="patch_tuesday_report.json")
args = parser.parse_args()
cvrf_data = fetch_patch_tuesday_updates(args.api_key, args.month)
vulns = parse_vulnerabilities(cvrf_data)
cve_list = [v["cve"] for v in vulns]
kev_cves = check_cisa_kev(cve_list)
prioritized = prioritize_patches(vulns, set(kev_cves))
deployment_plan = generate_deployment_plan(prioritized)
report = generate_report(prioritized, kev_cves, deployment_plan, args.month)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
logger.info("Report saved to %s", args.output)
if __name__ == "__main__":
main()