# building-patch-tuesday-response-process

Establish a repeatable operational process for triaging, testing, and deploying Microsoft Patch Tuesday security updates (Windows, Office, Exchange, SQL Server, Azure) via WSUS/SCCM within risk-based remediation SLAs, from advisory review through validation. Use when building or improving a monthly patch management workflow or prioritizing which CVEs to remediate first.

- **Kind:** skill
- **Source:** https://github.com/mukul975/Anthropic-Cybersecurity-Skills
- **Page:** https://forefy.com/skills/6bfb7611-0e37-44dd-921e-23c8e286d8b8
- **API (JSON + files):** https://forefy.com/api/asr/6bfb7611-0e37-44dd-921e-23c8e286d8b8

---

## LICENSE

```

```

## SKILL.md

---
name: building-patch-tuesday-response-process
description: Establish a repeatable operational process for triaging, testing, and
  deploying Microsoft Patch Tuesday security updates (Windows, Office, Exchange, SQL
  Server, Azure) via WSUS/SCCM within risk-based remediation SLAs, from advisory review
  through validation. Use when building or improving a monthly patch management workflow
  or prioritizing which CVEs to remediate first.
domain: cybersecurity
subdomain: vulnerability-management
tags:
- patch-management
- patch-tuesday
- microsoft
- wsus
- sccm
- vulnerability-remediation
- windows-update
version: '1.0'
author: mahipal
license: Apache-2.0
nist_csf:
- ID.RA-01
- ID.RA-02
- ID.IM-02
- ID.RA-06
mitre_attack:
- T1190
- T1203
- T1068
- T1210
- T1588.006
---
# Building Patch Tuesday Response Process

## Overview
Microsoft releases security updates on the second Tuesday of each month ("Patch Tuesday"), addressing vulnerabilities across Windows, Office, Exchange, SQL Server, Azure services, and other products. In 2025, Microsoft patched over 1,129 vulnerabilities across the year -- an 11.9% increase from 2024 -- making a structured response process critical. The leading risk types include elevation of privilege (49%), remote code execution (34%), and information disclosure (7%). This skill covers building a repeatable Patch Tuesday response workflow from initial advisory review through testing, deployment, and validation.


## When to Use

- When deploying or configuring building patch tuesday response process capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation

## Prerequisites
- Access to Microsoft Security Response Center (MSRC) update guide
- Vulnerability management platform (Qualys VMDR, Rapid7, Tenable)
- Patch deployment infrastructure (WSUS, SCCM/MECM, Intune, or third-party)
- Test environment mirroring production configurations
- Change management process (ITIL-based or equivalent)
- Communication channels for cross-team coordination

## Core Concepts

### Patch Tuesday Timeline

| Day | Activity | Owner |
|-----|----------|-------|
| T+0 (Tuesday 10 AM PT) | Microsoft releases patches and advisories | Microsoft |
| T+0 (Tuesday afternoon) | Security team reviews advisories and triages | Security Ops |
| T+1 (Wednesday) | Qualys/vendor scan signatures updated | VM Platform |
| T+1-T+2 | Emergency patches deployed for zero-days | IT Operations |
| T+2-T+5 | Test patches in staging environment | QA/IT Ops |
| T+5-T+7 | Deploy to Pilot group (5-10% of fleet) | IT Operations |
| T+7-T+14 | Deploy to Production Ring 1 (servers) | IT Operations |
| T+14-T+21 | Deploy to Production Ring 2 (workstations) | IT Operations |
| T+21-T+30 | Validation scanning and compliance reporting | Security Ops |

### Patch Categorization Framework

| Category | Criteria | Response SLA |
|----------|----------|-------------|
| Zero-Day / Exploited | Active exploitation confirmed, CISA KEV listed | 24-48 hours |
| Critical RCE | CVSS >= 9.0, remote code execution, no auth required | 3-5 days |
| Critical with Exploit | Public exploit code or EPSS > 0.7 | 7 days |
| High Severity | CVSS 7.0-8.9, privilege escalation | 14 days |
| Medium Severity | CVSS 4.0-6.9 | 30 days |
| Low / Informational | CVSS < 4.0, defense-in-depth | Next maintenance window |

### Microsoft Product Categories to Monitor

| Category | Products | Risk Level |
|----------|----------|------------|
| Windows OS | Windows 10, 11, Server 2016-2025 | Critical |
| Exchange Server | Exchange 2016, 2019, Online | Critical |
| SQL Server | SQL 2016-2022 | High |
| Office Suite | Microsoft 365, Office 2019-2024 | High |
| .NET Framework | .NET 4.x, .NET 6-9 | Medium |
| Azure Services | Azure AD, Entra ID, Azure Stack | High |
| Edge/Browser | Edge Chromium, IE mode | Medium |
| Development Tools | Visual Studio, VS Code | Low |

## Workflow

### Step 1: Pre-Patch Tuesday Preparation (Monday before)
```
Preparation Checklist:
  [ ] Confirm WSUS/SCCM sync schedules are active
  [ ] Verify test environment is available and current
  [ ] Review outstanding patches from previous month
  [ ] Confirm monitoring dashboards are operational
  [ ] Pre-stage communication templates
  [ ] Ensure rollback procedures are documented
  [ ] Verify backup jobs ran successfully on critical servers
```

### Step 2: Day-of Triage (Patch Tuesday)

```
Triage Process:
  1. Monitor MSRC Update Guide (https://msrc.microsoft.com/update-guide)
  2. Review Microsoft Security Blog for advisory summaries
  3. Cross-reference with CISA KEV additions (same day)
  4. Check vendor advisories (Qualys, Rapid7, CrowdStrike analysis)
  5. Identify zero-day and actively exploited vulnerabilities
  6. Classify each CVE by severity and applicability
  7. Determine deployment rings and timeline for each patch
  8. Submit emergency change request for zero-day patches
  9. Communicate triage results to IT Operations and management
```

### Step 3: Scan and Gap Analysis

```python
# Post-Patch-Tuesday scan workflow
def run_patch_tuesday_scan(scanner_api, target_groups):
    """Trigger vulnerability scans after Patch Tuesday updates."""
    for group in target_groups:
        print(f"[*] Scanning {group['name']}...")
        scan_id = scanner_api.launch_scan(
            target=group["targets"],
            template="patch-tuesday-focused",
            credentials=group["creds"]
        )
        print(f"    Scan launched: {scan_id}")

    # Wait for scan completion, then generate report
    results = scanner_api.get_scan_results(scan_id)
    missing_patches = [r for r in results if r["status"] == "missing"]

    # Categorize by Patch Tuesday release
    current_month = [p for p in missing_patches
                     if p["vendor_advisory_date"] >= patch_tuesday_date]

    return {
        "total_missing": len(missing_patches),
        "current_month": len(current_month),
        "zero_day": [p for p in current_month if p.get("actively_exploited")],
        "critical": [p for p in current_month if p["cvss"] >= 9.0],
    }
```

### Step 4: Ring-Based Deployment Strategy

```
Ring 0 - Emergency (0-48 hours):
    Scope:     Zero-day and actively exploited CVEs only
    Method:    Manual or targeted push (SCCM expedite)
    Targets:   Internet-facing servers, critical infrastructure
    Approval:  Emergency change, verbal CISO approval
    Rollback:  Immediate rollback if service degradation

Ring 1 - Pilot (Day 2-7):
    Scope:     All critical and high patches
    Method:    WSUS/SCCM automatic deployment
    Targets:   IT department machines, test group (5-10%)
    Approval:  Standard change with CAB notification
    Monitoring: 48-hour soak period, check for BSOD, app crashes

Ring 2 - Production Servers (Day 7-14):
    Scope:     All security patches
    Method:    SCCM maintenance windows (off-hours)
    Targets:   Production servers by tier
    Approval:  Standard change with CAB approval
    Monitoring: Application health checks, performance baseline

Ring 3 - Workstations (Day 14-21):
    Scope:     All security patches + quality updates
    Method:    Windows Update for Business / Intune
    Targets:   All managed workstations
    Approval:  Pre-approved standard change
    Monitoring: Help desk ticket monitoring for issues

Ring 4 - Stragglers (Day 21-30):
    Scope:     Catch remaining unpatched systems
    Method:    Forced deployment with restart
    Targets:   Systems that missed prior rings
    Approval:  Compliance-driven enforcement
```

### Step 5: Validation and Reporting

```
Post-Deployment Validation:
  1. Re-scan environment with updated vulnerability signatures
  2. Compare pre-patch and post-patch scan results
  3. Calculate patch compliance rate per ring and department
  4. Identify failed patches and investigate root causes
  5. Generate compliance report for management review
  6. Update risk register with residual unpatched vulnerabilities
  7. Document exceptions and compensating controls
```

## Best Practices
1. Subscribe to MSRC notifications and vendor analysis blogs for early intelligence
2. Maintain a dedicated Patch Tuesday war room or Slack/Teams channel
3. Always patch zero-day vulnerabilities outside the normal ring schedule
4. Test patches against critical business applications before broad deployment
5. Track patch compliance metrics month-over-month for trend analysis
6. Maintain rollback procedures for every deployment ring
7. Coordinate with application owners for compatibility testing
8. Document all exceptions with compensating controls and review dates

## Common Pitfalls
- Deploying all patches simultaneously without ring-based testing
- Not scanning after patching to validate remediation
- Treating all patches equally without risk-based prioritization
- Ignoring cumulative update dependencies causing patch failures
- Not accounting for server reboot requirements in maintenance windows
- Failing to communicate patch status to business stakeholders

## Related Skills
- implementing-rapid7-insightvm-for-scanning
- performing-cve-prioritization-with-kev-catalog
- implementing-vulnerability-remediation-sla
- implementing-patch-management-workflow

## assets

```

```

## assets/template.md

# Patch Tuesday Response Report Template

## Monthly Patch Summary
| Field | Value |
|-------|-------|
| Patch Tuesday Date | [YYYY-MM-DD] |
| Total CVEs Released | [N] |
| Zero-Days | [N] |
| Critical | [N] |
| Important | [N] |
| Moderate | [N] |

## Deployment Status by Ring
| Ring | Name | SLA | Patches | Deployed | Compliance |
|------|------|-----|---------|----------|------------|
| 0 | Emergency | 48h | [N] | [N] | [%] |
| 1 | Pilot | 7 days | [N] | [N] | [%] |
| 2 | Production | 14 days | [N] | [N] | [%] |
| 3 | Workstations | 21 days | [N] | [N] | [%] |
| 4 | Stragglers | 30 days | [N] | [N] | [%] |

## Zero-Day / Actively Exploited Patches
| CVE | Product | CVSS | KEV | Status | Deployed |
|-----|---------|------|-----|--------|----------|
| [CVE-ID] | [Product] | [N.N] | [Y/N] | [Deployed/Pending] | [Date] |

## Exceptions and Deferrals
| CVE | Reason | Compensating Control | Review Date | Approver |
|-----|--------|---------------------|-------------|----------|
| [CVE-ID] | [Reason] | [Control] | [Date] | [Name] |

## references

```

```

## references/api-reference.md

# API Reference: Patch Tuesday Response Process

## MSRC Security Update API
```
GET https://api.msrc.microsoft.com/cvrf/v3.0/Updates('{yyyy-Mon}')
api-key: YOUR_MSRC_KEY
Accept: application/json
```

## CVRF Vulnerability Fields
| Field | Description |
|-------|-------------|
| `CVE` | CVE identifier |
| `Title.Value` | Vulnerability title |
| `Threats[].Description.Value` | Severity, exploitation status |
| `CVSSScoreSets[].BaseScore` | CVSS v3 base score |
| `ProductStatuses[].ProductID` | Affected product IDs |
| `Remediations[].URL` | KB article / patch URL |

## CISA Known Exploited Vulnerabilities (KEV)
```
GET https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
```

### KEV Entry Fields
| Field | Description |
|-------|-------------|
| `cveID` | CVE identifier |
| `vendorProject` | Vendor name |
| `product` | Product name |
| `dateAdded` | Date added to KEV |
| `dueDate` | Remediation due date |

## Patch Priority Matrix
| Priority | Criteria | SLA |
|----------|----------|-----|
| Emergency | Exploited + KEV + CVSS >= 9.0 | 24 hours |
| Critical | Exploited OR KEV + CVSS >= 7.0 | 72 hours |
| Standard | CVSS >= 7.0, no exploitation | 7 days |
| Routine | CVSS < 7.0, no exploitation | 30 days |

## NVD API v2
```
GET https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={CVE-ID}
apiKey: YOUR_NVD_KEY
```

## WSUS Deployment API (PowerShell)
```powershell
$wsus = Get-WsusServer
$update = $wsus.SearchUpdates("KB5034441")
$group = $wsus.GetComputerTargetGroup("Production")
$update.Approve("Install", $group)
```

## Deployment Phase Timeline
| Phase | Window | Targets |
|-------|--------|---------|
| Emergency | 0-24h | Critical servers, exploited CVEs |
| Pilot | 24-72h | Test group (5% of fleet) |
| Broad | 3-7d | All production systems |
| Cleanup | 7-30d | Exceptions, rollback monitoring |

## references/standards.md

# Standards and References - Patch Tuesday Response Process

## Microsoft Resources
- MSRC Security Update Guide: https://msrc.microsoft.com/update-guide
- Microsoft Security Blog: https://www.microsoft.com/en-us/security/blog/
- Windows Update for Business: https://learn.microsoft.com/en-us/windows/deployment/update/waas-manage-updates-wufb
- SCCM/MECM Patch Management: https://learn.microsoft.com/en-us/mem/configmgr/sum/

## Industry Standards
- **NIST SP 800-40 Rev 4**: Guide to Enterprise Patch Management Planning
- **CIS Controls v8.1 Control 7.4**: Perform Automated Patch Management
- **PCI DSS v4.0 Req 6.3.3**: Install security patches within one month of release
- **ISO 27001:2022 A.8.8**: Management of technical vulnerabilities

## Patch Tuesday Statistics (2025)
| Metric | Value |
|--------|-------|
| Total CVEs patched in 2025 | 1,129 |
| Year-over-year increase | 11.9% |
| Average CVEs per month | ~94 |
| Top category: Elevation of Privilege | ~49% |
| Top category: Remote Code Execution | ~34% |
| Zero-days patched in 2025 | Multiple per quarter |

## Vendor Analysis Resources
- Qualys Patch Tuesday Blog: https://blog.qualys.com/tag/patch-tuesday
- Tenable Patch Tuesday Analysis: https://www.tenable.com/blog/tag/patch-tuesday
- CrowdStrike Patch Tuesday: https://www.crowdstrike.com/blog/tag/patch-tuesday
- SANS ISC Patch Tuesday Dashboard: https://isc.sans.edu/patchtuesday/

## references/workflows.md

# Workflows - Patch Tuesday Response Process

## Workflow 1: Monthly Patch Tuesday Lifecycle

```
Week 1 (Patch Tuesday):
  Mon: Pre-staging, verify infrastructure readiness
  Tue: Patch release, triage, zero-day emergency deployment
  Wed: Scan environment, update signatures, gap analysis
  Thu: Begin pilot deployment (Ring 1)
  Fri: Monitor pilot, document issues

Week 2:
  Mon-Wed: Production server deployment (Ring 2)
  Thu-Fri: Monitor server health, rollback if needed

Week 3:
  Mon-Fri: Workstation deployment (Ring 3)

Week 4:
  Mon-Wed: Catch stragglers (Ring 4)
  Thu: Validation scanning
  Fri: Compliance report, close change tickets
```

## Workflow 2: Zero-Day Emergency Response

```
┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│ Zero-Day CVE     │────>│ CISO Approves    │────>│ Emergency Change │
│ Identified       │     │ Emergency Patch  │     │ Ticket Created   │
└──────────────────┘     └──────────────────┘     └──────────────────┘
                                                          │
        ┌────────────────────────────────────────────────┘
        v
┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│ Quick Smoke Test │────>│ Deploy to Ring 0 │────>│ Monitor for      │
│ (1-2 hours)      │     │ (Critical Assets)│     │ Issues (4 hours) │
└──────────────────┘     └──────────────────┘     └──────────────────┘
        │
        v
┌──────────────────┐     ┌──────────────────┐
│ Broader Rollout  │────>│ Validation Scan  │
│ (All Rings)      │     │ & Report         │
└──────────────────┘     └──────────────────┘
```

## Workflow 3: Patch Compliance Tracking

| Metric | Target | Measurement |
|--------|--------|-------------|
| Zero-day patch rate | 100% in 48 hours | SCCM compliance report |
| Critical patch rate | 95% in 7 days | Vulnerability scan delta |
| High patch rate | 90% in 14 days | Vulnerability scan delta |
| Overall compliance | 95% in 30 days | Monthly compliance dashboard |
| Exception documentation | 100% documented | GRC platform audit |

## scripts

```

```

## scripts/agent.py

```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()
```

## scripts/process.py

```python
#!/usr/bin/env python3
"""
Patch Tuesday Response Automation Tool

Fetches Microsoft Patch Tuesday advisory data, cross-references with
CISA KEV, and generates a prioritized deployment plan.

Requirements:
    pip install requests pandas

Usage:
    python process.py fetch --month 2025-12      # Fetch patches for month
    python process.py analyze --month 2025-12     # Analyze and prioritize
    python process.py plan --month 2025-12 --output deployment_plan.csv
"""

import argparse
import json
import sys
from datetime import datetime

import pandas as pd
import requests


MSRC_API = "https://api.msrc.microsoft.com/cvrf/v3.0/cvrf"
MSRC_UPDATES_API = "https://api.msrc.microsoft.com/cvrf/v3.0/updates"
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
EPSS_API = "https://api.first.org/data/v1/epss"


class PatchTuesdayAnalyzer:
    """Analyze and prioritize Patch Tuesday security updates."""

    SEVERITY_MAP = {
        "Critical": 4,
        "Important": 3,
        "Moderate": 2,
        "Low": 1,
    }

    RING_ASSIGNMENT = {
        "zero_day": {"ring": 0, "name": "Emergency", "sla_hours": 48},
        "critical_rce": {"ring": 0, "name": "Emergency", "sla_hours": 72},
        "critical": {"ring": 1, "name": "Pilot", "sla_hours": 168},
        "important": {"ring": 2, "name": "Production", "sla_hours": 336},
        "moderate": {"ring": 3, "name": "Workstations", "sla_hours": 504},
        "low": {"ring": 4, "name": "Maintenance", "sla_hours": 720},
    }

    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "Accept": "application/json",
            "User-Agent": "PatchTuesday-Analyzer/1.0"
        })
        self.kev_catalog = set()

    def load_kev_catalog(self):
        """Load CISA KEV catalog for cross-reference."""
        try:
            response = self.session.get(KEV_URL, timeout=30)
            if response.status_code == 200:
                data = response.json()
                self.kev_catalog = {
                    v["cveID"] for v in data.get("vulnerabilities", [])
                }
                print(f"[+] Loaded {len(self.kev_catalog)} CVEs from CISA KEV")
        except Exception as e:
            print(f"[!] Failed to load KEV: {e}")

    def get_epss_scores(self, cve_list):
        """Fetch EPSS scores for CVEs."""
        scores = {}
        for i in range(0, len(cve_list), 100):
            batch = cve_list[i:i + 100]
            try:
                response = self.session.get(
                    EPSS_API,
                    params={"cve": ",".join(batch)},
                    timeout=30
                )
                if response.status_code == 200:
                    for entry in response.json().get("data", []):
                        scores[entry["cve"]] = float(entry.get("epss", 0))
            except Exception as e:
                print(f"  [!] EPSS error: {e}")
        return scores

    def classify_patch(self, cve_data):
        """Classify a patch into deployment ring."""
        is_exploited = cve_data.get("actively_exploited", False)
        in_kev = cve_data.get("cve_id", "") in self.kev_catalog
        severity = cve_data.get("severity", "").lower()
        attack_type = cve_data.get("attack_type", "").lower()
        cvss = float(cve_data.get("cvss_score", 0))
        epss = float(cve_data.get("epss_score", 0))

        if is_exploited or in_kev:
            return self.RING_ASSIGNMENT["zero_day"]
        elif severity == "critical" and "remote code execution" in attack_type:
            return self.RING_ASSIGNMENT["critical_rce"]
        elif severity == "critical" or cvss >= 9.0 or epss > 0.7:
            return self.RING_ASSIGNMENT["critical"]
        elif severity == "important" or cvss >= 7.0:
            return self.RING_ASSIGNMENT["important"]
        elif severity == "moderate" or cvss >= 4.0:
            return self.RING_ASSIGNMENT["moderate"]
        else:
            return self.RING_ASSIGNMENT["low"]

    def generate_deployment_plan(self, patches):
        """Generate a ring-based deployment plan."""
        self.load_kev_catalog()

        cve_list = [p["cve_id"] for p in patches if p.get("cve_id")]
        epss_scores = self.get_epss_scores(cve_list)

        plan = []
        for patch in patches:
            cve_id = patch.get("cve_id", "")
            patch["epss_score"] = epss_scores.get(cve_id, 0)
            patch["in_cisa_kev"] = cve_id in self.kev_catalog

            ring = self.classify_patch(patch)

            plan.append({
                "cve_id": cve_id,
                "product": patch.get("product", ""),
                "severity": patch.get("severity", ""),
                "attack_type": patch.get("attack_type", ""),
                "cvss_score": patch.get("cvss_score", 0),
                "epss_score": round(patch.get("epss_score", 0), 4),
                "in_cisa_kev": patch.get("in_cisa_kev", False),
                "actively_exploited": patch.get("actively_exploited", False),
                "deployment_ring": ring["ring"],
                "ring_name": ring["name"],
                "sla_hours": ring["sla_hours"],
                "kb_article": patch.get("kb_article", ""),
            })

        df = pd.DataFrame(plan)
        df = df.sort_values(["deployment_ring", "cvss_score"],
                            ascending=[True, False])
        return df

    def print_summary(self, df):
        """Print deployment plan summary."""
        print(f"\n{'=' * 70}")
        print("PATCH TUESDAY DEPLOYMENT PLAN")
        print(f"{'=' * 70}")
        print(f"Total patches: {len(df)}")
        print(f"Zero-day/KEV (Ring 0): {len(df[df['deployment_ring'] == 0])}")

        print(f"\nBy Severity:")
        print(df["severity"].value_counts().to_string())

        print(f"\nBy Deployment Ring:")
        for ring in sorted(df["deployment_ring"].unique()):
            ring_data = df[df["deployment_ring"] == ring]
            ring_name = ring_data.iloc[0]["ring_name"]
            sla = ring_data.iloc[0]["sla_hours"]
            print(f"  Ring {ring} ({ring_name}): {len(ring_data)} patches, "
                  f"SLA: {sla}h")

        print(f"\nBy Attack Type:")
        print(df["attack_type"].value_counts().head(5).to_string())


def main():
    parser = argparse.ArgumentParser(
        description="Patch Tuesday Response Automation"
    )
    subparsers = parser.add_subparsers(dest="command")

    plan_p = subparsers.add_parser("plan", help="Generate deployment plan from CSV")
    plan_p.add_argument("--csv", required=True, help="Input CSV of patches")
    plan_p.add_argument("--output", default="deployment_plan.csv")

    args = parser.parse_args()
    analyzer = PatchTuesdayAnalyzer()

    if args.command == "plan":
        df = pd.read_csv(args.csv)
        patches = df.to_dict("records")
        plan = analyzer.generate_deployment_plan(patches)
        analyzer.print_summary(plan)
        plan.to_csv(args.output, index=False)
        print(f"\n[+] Deployment plan saved to {args.output}")
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
```

