mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-14 06:54:57 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
---
|
||||
name: None
|
||||
description: Patch management is the systematic process of identifying, testing, deploying, and verifying software updates to remediate vulnerabilities across an organization's IT infrastructure. An effective patc
|
||||
domain: cybersecurity
|
||||
subdomain: vulnerability-management
|
||||
tags: [vulnerability-management, patch-management, wsus, sccm, ansible, risk]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
# Implementing Patch Management Workflow
|
||||
|
||||
## Overview
|
||||
Patch management is the systematic process of identifying, testing, deploying, and verifying software updates to remediate vulnerabilities across an organization's IT infrastructure. An effective patch management workflow reduces the attack surface while minimizing operational disruption through structured testing, approval gates, and phased rollouts.
|
||||
|
||||
## Prerequisites
|
||||
- Vulnerability scan results identifying missing patches
|
||||
- Patch management tools (WSUS, SCCM/MECM, Ansible, Intune, Jamf)
|
||||
- Test environment mirroring production
|
||||
- Change management process (ITIL or equivalent)
|
||||
- Asset inventory with OS and application versions
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Patch Lifecycle Phases
|
||||
1. **Discovery**: Identify available patches from vendors and vulnerability scans
|
||||
2. **Assessment**: Evaluate patch applicability and risk
|
||||
3. **Prioritization**: Rank patches by severity, exploitability, and asset criticality
|
||||
4. **Testing**: Validate patches in non-production environment
|
||||
5. **Approval**: Change advisory board (CAB) review and approval
|
||||
6. **Deployment**: Phased rollout to production systems
|
||||
7. **Verification**: Confirm successful installation and no regressions
|
||||
8. **Reporting**: Document compliance metrics and exceptions
|
||||
|
||||
### Patch Categories
|
||||
- **Security Patches**: Address CVEs and security vulnerabilities
|
||||
- **Critical Updates**: Non-security bug fixes affecting stability
|
||||
- **Service Packs**: Cumulative update collections
|
||||
- **Feature Updates**: New functionality (Windows feature updates, etc.)
|
||||
- **Firmware Updates**: BIOS/UEFI, NIC, storage controller firmware
|
||||
- **Third-Party Patches**: Adobe, Java, Chrome, Firefox, etc.
|
||||
|
||||
### Deployment Rings (Phased Rollout)
|
||||
| Ring | Environment | % of Fleet | Soak Time | Purpose |
|
||||
|------|------------|------------|-----------|---------|
|
||||
| Ring 0 | Lab/Test | N/A | 24-48 hrs | Functional validation |
|
||||
| Ring 1 | IT Early Adopters | 5% | 48-72 hrs | Real-world pilot |
|
||||
| Ring 2 | Business Pilot | 15% | 5-7 days | Broader compatibility |
|
||||
| Ring 3 | General Deployment | 50% | 7-14 days | Main rollout |
|
||||
| Ring 4 | Mission Critical | 30% | After Ring 3 | Final deployment |
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Configure Patch Sources
|
||||
|
||||
```bash
|
||||
# WSUS (Windows Server Update Services)
|
||||
# Configure WSUS server to sync with Microsoft Update
|
||||
# Via PowerShell on WSUS server:
|
||||
Install-WindowsFeature -Name UpdateServices -IncludeManagementTools
|
||||
& "C:\Program Files\Update Services\Tools\WsusUtil.exe" postinstall CONTENT_DIR=D:\WSUS
|
||||
|
||||
# Configure GPO for WSUS clients
|
||||
# Computer Configuration > Administrative Templates > Windows Components > Windows Update
|
||||
# Specify intranet Microsoft update service location: http://wsus-server:8530
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Ansible: Configure patch repositories for Linux
|
||||
# roles/patch-management/tasks/configure_repos.yml
|
||||
---
|
||||
- name: Configure RHEL patch repository
|
||||
yum_repository:
|
||||
name: rhel-patches
|
||||
description: RHEL Security Patches
|
||||
baseurl: https://satellite.corp.local/pulp/repos/patches
|
||||
gpgcheck: yes
|
||||
gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
|
||||
enabled: yes
|
||||
|
||||
- name: Configure Ubuntu patch sources
|
||||
apt_repository:
|
||||
repo: "deb https://apt-mirror.corp.local/ubuntu {{ ansible_distribution_release }}-security main"
|
||||
state: present
|
||||
when: ansible_os_family == "Debian"
|
||||
```
|
||||
|
||||
### Step 2: Automated Patch Assessment
|
||||
|
||||
```python
|
||||
# patch_assessment.py - Correlate vulnerability scans with available patches
|
||||
import subprocess
|
||||
import platform
|
||||
import json
|
||||
|
||||
def get_windows_pending_patches():
|
||||
"""Query Windows Update for pending patches via PowerShell."""
|
||||
ps_cmd = """
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
$Results = $Searcher.Search("IsInstalled=0 AND Type='Software'")
|
||||
$Results.Updates | ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
Title = $_.Title
|
||||
KB = ($_.KBArticleIDs -join ',')
|
||||
Severity = $_.MsrcSeverity
|
||||
Size = [math]::Round($_.MaxDownloadSize / 1MB, 2)
|
||||
Published = $_.LastDeploymentChangeTime.ToString('yyyy-MM-dd')
|
||||
CVE = ($_.CveIDs -join ',')
|
||||
}
|
||||
} | ConvertTo-Json
|
||||
"""
|
||||
result = subprocess.run(
|
||||
["powershell", "-Command", ps_cmd],
|
||||
capture_output=True, text=True, timeout=120
|
||||
)
|
||||
return json.loads(result.stdout) if result.stdout.strip() else []
|
||||
|
||||
def get_linux_pending_patches():
|
||||
"""Query package manager for available security updates."""
|
||||
if platform.system() != "Linux":
|
||||
return []
|
||||
|
||||
# Try apt (Debian/Ubuntu)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["apt", "list", "--upgradable"],
|
||||
capture_output=True, text=True, timeout=60
|
||||
)
|
||||
packages = []
|
||||
for line in result.stdout.strip().split("\n")[1:]:
|
||||
if line:
|
||||
parts = line.split("/")
|
||||
packages.append({
|
||||
"package": parts[0],
|
||||
"available_version": parts[1].split()[0] if len(parts) > 1 else "",
|
||||
"source": "apt"
|
||||
})
|
||||
return packages
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Try yum/dnf (RHEL/CentOS)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["dnf", "updateinfo", "list", "security", "--available"],
|
||||
capture_output=True, text=True, timeout=60
|
||||
)
|
||||
packages = []
|
||||
for line in result.stdout.strip().split("\n"):
|
||||
parts = line.split()
|
||||
if len(parts) >= 3:
|
||||
packages.append({
|
||||
"advisory": parts[0],
|
||||
"severity": parts[1],
|
||||
"package": parts[2],
|
||||
"source": "dnf"
|
||||
})
|
||||
return packages
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
```
|
||||
|
||||
### Step 3: Patch Testing Automation
|
||||
|
||||
```yaml
|
||||
# Ansible playbook: test_patches.yml
|
||||
---
|
||||
- name: Test Patches in Lab Environment
|
||||
hosts: test_servers
|
||||
become: yes
|
||||
vars:
|
||||
rollback_snapshot: "pre-patch-{{ ansible_date_time.date }}"
|
||||
|
||||
tasks:
|
||||
- name: Create VM snapshot before patching
|
||||
community.vmware.vmware_guest_snapshot:
|
||||
hostname: "{{ vcenter_host }}"
|
||||
username: "{{ vcenter_user }}"
|
||||
password: "{{ vcenter_pass }}"
|
||||
datacenter: "{{ datacenter }}"
|
||||
name: "{{ inventory_hostname }}"
|
||||
snapshot_name: "{{ rollback_snapshot }}"
|
||||
state: present
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Apply security patches (RHEL/CentOS)
|
||||
dnf:
|
||||
name: "*"
|
||||
state: latest
|
||||
security: yes
|
||||
update_cache: yes
|
||||
when: ansible_os_family == "RedHat"
|
||||
register: patch_result
|
||||
|
||||
- name: Apply security patches (Ubuntu/Debian)
|
||||
apt:
|
||||
upgrade: dist
|
||||
update_cache: yes
|
||||
only_upgrade: yes
|
||||
when: ansible_os_family == "Debian"
|
||||
register: patch_result
|
||||
|
||||
- name: Reboot if required
|
||||
reboot:
|
||||
reboot_timeout: 600
|
||||
msg: "Rebooting for patch installation"
|
||||
when: patch_result.changed
|
||||
|
||||
- name: Run post-patch validation
|
||||
include_tasks: validate_services.yml
|
||||
|
||||
- name: Report patch results
|
||||
debug:
|
||||
msg: "Patching {{ 'succeeded' if patch_result.changed else 'no updates' }} on {{ inventory_hostname }}"
|
||||
```
|
||||
|
||||
### Step 4: Production Deployment
|
||||
|
||||
```yaml
|
||||
# deploy_patches.yml - Phased production rollout
|
||||
---
|
||||
- name: Ring 1 - IT Early Adopters
|
||||
hosts: ring1_hosts
|
||||
serial: "25%"
|
||||
max_fail_percentage: 10
|
||||
become: yes
|
||||
tasks:
|
||||
- import_tasks: apply_patches.yml
|
||||
- import_tasks: validate_services.yml
|
||||
- name: Wait for soak period
|
||||
pause:
|
||||
hours: 48
|
||||
run_once: true
|
||||
|
||||
- name: Ring 2 - Business Pilot
|
||||
hosts: ring2_hosts
|
||||
serial: "20%"
|
||||
max_fail_percentage: 5
|
||||
become: yes
|
||||
tasks:
|
||||
- import_tasks: apply_patches.yml
|
||||
- import_tasks: validate_services.yml
|
||||
|
||||
- name: Ring 3 - General Deployment
|
||||
hosts: ring3_hosts
|
||||
serial: "10%"
|
||||
max_fail_percentage: 3
|
||||
become: yes
|
||||
tasks:
|
||||
- import_tasks: apply_patches.yml
|
||||
- import_tasks: validate_services.yml
|
||||
```
|
||||
|
||||
### Step 5: Verification and Reporting
|
||||
|
||||
Run a post-patch vulnerability scan to confirm patch installation:
|
||||
```bash
|
||||
# Trigger post-patch verification scan
|
||||
curl -k -X POST "https://nessus:8834/scans/$VERIFY_SCAN_ID/launch" \
|
||||
-H "X-Cookie: token=$TOKEN"
|
||||
|
||||
# Compare pre-patch and post-patch results
|
||||
# Expecting reduction in vulnerabilities matching deployed patches
|
||||
```
|
||||
|
||||
## Patch Management SLAs
|
||||
| Severity | SLA (Internet-Facing) | SLA (Internal) | SLA (Air-Gapped) |
|
||||
|----------|----------------------|----------------|-------------------|
|
||||
| Critical (CVSS 9+) | 48 hours | 7 days | 14 days |
|
||||
| High (CVSS 7-8.9) | 7 days | 14 days | 30 days |
|
||||
| Medium (CVSS 4-6.9) | 30 days | 30 days | 60 days |
|
||||
| Low (CVSS 0.1-3.9) | 90 days | 90 days | 90 days |
|
||||
|
||||
## Best Practices
|
||||
1. Maintain current asset inventory to ensure complete patch coverage
|
||||
2. Test all patches in a non-production environment before deployment
|
||||
3. Use phased rollouts with automatic rollback capabilities
|
||||
4. Coordinate patch windows with change management process
|
||||
5. Track patch compliance metrics and report to leadership
|
||||
6. Automate where possible to reduce manual effort and human error
|
||||
7. Maintain exception documentation for systems that cannot be patched
|
||||
8. Include third-party application patching (not just OS patches)
|
||||
|
||||
## Common Pitfalls
|
||||
- Patching only operating systems and ignoring third-party applications
|
||||
- No rollback plan if patches cause service disruption
|
||||
- Treating all patches with equal urgency (no risk-based prioritization)
|
||||
- Manual patch processes that cannot scale
|
||||
- No post-patch verification to confirm successful installation
|
||||
- Ignoring firmware and BIOS updates
|
||||
|
||||
## Related Skills
|
||||
- prioritizing-vulnerabilities-with-cvss-scoring
|
||||
- implementing-vulnerability-remediation-sla
|
||||
- implementing-continuous-vulnerability-monitoring
|
||||
@@ -0,0 +1,30 @@
|
||||
# Patch Management Report Template
|
||||
|
||||
## Patch Cycle Summary
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Patch Cycle | [Month Year] |
|
||||
| Deployment Window | [Start] to [End] |
|
||||
| Patches Deployed | [N] security, [N] critical, [N] feature |
|
||||
|
||||
## Compliance Metrics
|
||||
| Metric | Value | Target | Status |
|
||||
|--------|-------|--------|--------|
|
||||
| Overall Compliance | [%] | 95% | [Met/Not Met] |
|
||||
| Critical Patch Compliance | [%] | 100% | [Met/Not Met] |
|
||||
| Mean Time to Patch (Critical) | [N days] | 48 hours | [Met/Not Met] |
|
||||
| Mean Time to Patch (High) | [N days] | 7 days | [Met/Not Met] |
|
||||
| Rollback Rate | [%] | <2% | [Met/Not Met] |
|
||||
|
||||
## Deployment Results by Ring
|
||||
| Ring | Hosts | Success | Failed | Rollback | Duration |
|
||||
|------|-------|---------|--------|----------|----------|
|
||||
| Ring 0 (Lab) | [N] | [N] | [N] | [N] | [Nh] |
|
||||
| Ring 1 (Pilot) | [N] | [N] | [N] | [N] | [Nh] |
|
||||
| Ring 2 (General) | [N] | [N] | [N] | [N] | [Nh] |
|
||||
| Ring 3 (Critical) | [N] | [N] | [N] | [N] | [Nh] |
|
||||
|
||||
## Exceptions and Deferrals
|
||||
| Host/Group | Patch | Reason | Approved By | Expiry |
|
||||
|-----------|-------|--------|-------------|--------|
|
||||
| [host] | [KB/CVE] | [reason] | [approver] | [date] |
|
||||
@@ -0,0 +1,28 @@
|
||||
# Standards and References - Patch Management Workflow
|
||||
|
||||
## Industry Standards
|
||||
- **NIST SP 800-40 Rev 4**: Guide to Enterprise Patch Management Planning
|
||||
- **NIST SP 800-53 SI-2**: Flaw Remediation control
|
||||
- **CIS Controls v8 Control 7.3**: Perform automated patch management
|
||||
- **PCI DSS v4.0 Req 6.3**: Identify and address security vulnerabilities
|
||||
- **ISO 27001:2022 A.8.8**: Management of technical vulnerabilities
|
||||
|
||||
## Patch Management Tools
|
||||
| Tool | Platform | Type | License |
|
||||
|------|----------|------|---------|
|
||||
| WSUS | Windows | Microsoft native | Free with Windows Server |
|
||||
| SCCM/MECM | Windows/Linux | Enterprise endpoint management | Microsoft licensing |
|
||||
| Ansible | Linux/Windows | Agentless automation | Open source / Red Hat |
|
||||
| Intune | Windows/macOS/iOS/Android | Cloud MDM/MAM | Microsoft 365 |
|
||||
| Jamf Pro | macOS/iOS | Apple device management | Commercial |
|
||||
| Ivanti Patch | Multi-platform | Enterprise patching | Commercial |
|
||||
| ManageEngine | Multi-platform | IT management suite | Commercial |
|
||||
|
||||
## Vendor Patch Schedules
|
||||
| Vendor | Schedule | Source |
|
||||
|--------|----------|--------|
|
||||
| Microsoft | Second Tuesday monthly | https://msrc.microsoft.com/update-guide |
|
||||
| Adobe | Second Tuesday monthly | https://helpx.adobe.com/security/products.html |
|
||||
| Oracle | Quarterly (Jan, Apr, Jul, Oct) | https://www.oracle.com/security-alerts/ |
|
||||
| Cisco | As needed | https://sec.cloudapps.cisco.com/security/center |
|
||||
| Linux distributions | Continuous | Distribution-specific advisories |
|
||||
@@ -0,0 +1,49 @@
|
||||
# Workflows - Patch Management
|
||||
|
||||
## Workflow 1: End-to-End Patch Lifecycle
|
||||
|
||||
```
|
||||
┌────────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐
|
||||
│ Discover │──>│ Assess │──>│ Prioritize │──>│ Test │
|
||||
│ (Vendor │ │ (CVE │ │ (CVSS+EPSS │ │ (Lab │
|
||||
│ Feeds) │ │ Match) │ │ Scoring) │ │ Ring 0) │
|
||||
└────────────┘ └──────────┘ └──────────────┘ └──────────┘
|
||||
│
|
||||
┌───────────────────────────────────────────────────┘
|
||||
v
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Approve │──>│ Deploy │──>│ Verify │──>│ Report │
|
||||
│ (CAB / │ │ (Phased │ │ (Re-scan │ │ (Metrics │
|
||||
│ Change) │ │ Rings) │ │ Confirm)│ │ + KPIs) │
|
||||
└──────────┘ └──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## Workflow 2: Emergency Patch Process
|
||||
|
||||
For critical zero-day or actively exploited vulnerabilities:
|
||||
|
||||
1. **Alert** (T+0h): Vendor advisory or threat intel notification
|
||||
2. **Triage** (T+1h): Assess applicability and impact
|
||||
3. **Fast-track Test** (T+4h): Rapid testing on critical systems
|
||||
4. **Emergency CAB** (T+6h): Expedited approval
|
||||
5. **Deploy** (T+8h): Direct to production (skip pilot rings)
|
||||
6. **Verify** (T+12h): Post-patch scan verification
|
||||
7. **Post-mortem** (T+48h): Review process effectiveness
|
||||
|
||||
## Workflow 3: Rollback Procedure
|
||||
|
||||
```
|
||||
Patch Deployment Fails
|
||||
│
|
||||
├──> Application Not Starting
|
||||
│ └──> Restore from snapshot/backup
|
||||
│
|
||||
├──> Performance Degradation
|
||||
│ └──> Uninstall patch (wusa /uninstall /kb:NNNNN)
|
||||
│
|
||||
├──> Blue Screen / Kernel Panic
|
||||
│ └──> Boot to safe mode, remove update
|
||||
│
|
||||
└──> Network Connectivity Lost
|
||||
└──> Console access, rollback patch
|
||||
```
|
||||
@@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Patch Management Workflow Automation
|
||||
|
||||
Tracks patch compliance, generates deployment plans, and monitors
|
||||
patch installation success across the enterprise.
|
||||
|
||||
Requirements:
|
||||
pip install requests pandas jinja2 pyyaml
|
||||
|
||||
Usage:
|
||||
python process.py compliance --scan-csv scan_results.csv --asset-csv assets.csv
|
||||
python process.py plan --patches patches.csv --rings rings.yaml
|
||||
python process.py report --compliance-csv compliance.csv
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
import yaml
|
||||
|
||||
|
||||
class PatchComplianceTracker:
|
||||
"""Track and report patch compliance across the enterprise."""
|
||||
|
||||
SLA_MAP = {
|
||||
"Critical": 2,
|
||||
"High": 7,
|
||||
"Medium": 30,
|
||||
"Low": 90,
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.assets = pd.DataFrame()
|
||||
self.patches = pd.DataFrame()
|
||||
self.compliance = pd.DataFrame()
|
||||
|
||||
def load_assets(self, asset_file: str):
|
||||
"""Load asset inventory from CSV."""
|
||||
self.assets = pd.read_csv(asset_file)
|
||||
required = {"hostname", "ip_address", "os", "tier"}
|
||||
if not required.issubset(set(self.assets.columns)):
|
||||
raise ValueError(f"Asset CSV must contain columns: {required}")
|
||||
print(f"[+] Loaded {len(self.assets)} assets")
|
||||
|
||||
def load_scan_results(self, scan_file: str):
|
||||
"""Load vulnerability scan results showing missing patches."""
|
||||
self.patches = pd.read_csv(scan_file)
|
||||
required = {"hostname", "plugin_id", "severity", "cve", "plugin_name"}
|
||||
if not required.issubset(set(self.patches.columns)):
|
||||
raise ValueError(f"Scan CSV must contain columns: {required}")
|
||||
print(f"[+] Loaded {len(self.patches)} patch findings")
|
||||
|
||||
def calculate_compliance(self) -> pd.DataFrame:
|
||||
"""Calculate patch compliance by host and severity."""
|
||||
if self.patches.empty:
|
||||
return pd.DataFrame()
|
||||
|
||||
# Merge with asset data
|
||||
merged = self.patches.merge(
|
||||
self.assets[["hostname", "tier", "os"]],
|
||||
on="hostname", how="left"
|
||||
)
|
||||
|
||||
# Calculate per-host compliance
|
||||
host_compliance = []
|
||||
for hostname, group in merged.groupby("hostname"):
|
||||
tier = group["tier"].iloc[0] if "tier" in group.columns else "Unknown"
|
||||
os_name = group["os"].iloc[0] if "os" in group.columns else "Unknown"
|
||||
|
||||
critical = len(group[group["severity"] == "Critical"])
|
||||
high = len(group[group["severity"] == "High"])
|
||||
medium = len(group[group["severity"] == "Medium"])
|
||||
low = len(group[group["severity"] == "Low"])
|
||||
total = critical + high + medium + low
|
||||
|
||||
# Compliance score: weighted by severity
|
||||
max_score = 100
|
||||
penalty = critical * 10 + high * 5 + medium * 2 + low * 0.5
|
||||
score = max(0, max_score - penalty)
|
||||
|
||||
host_compliance.append({
|
||||
"hostname": hostname,
|
||||
"tier": tier,
|
||||
"os": os_name,
|
||||
"critical_missing": critical,
|
||||
"high_missing": high,
|
||||
"medium_missing": medium,
|
||||
"low_missing": low,
|
||||
"total_missing": total,
|
||||
"compliance_score": round(score, 1),
|
||||
"compliant": total == 0,
|
||||
})
|
||||
|
||||
self.compliance = pd.DataFrame(host_compliance)
|
||||
self.compliance = self.compliance.sort_values("compliance_score")
|
||||
return self.compliance
|
||||
|
||||
def get_summary(self) -> dict:
|
||||
"""Generate compliance summary statistics."""
|
||||
if self.compliance.empty:
|
||||
self.calculate_compliance()
|
||||
|
||||
total_hosts = len(self.compliance)
|
||||
compliant = len(self.compliance[self.compliance["compliant"]])
|
||||
avg_score = self.compliance["compliance_score"].mean()
|
||||
|
||||
by_tier = {}
|
||||
for tier, group in self.compliance.groupby("tier"):
|
||||
by_tier[tier] = {
|
||||
"total": len(group),
|
||||
"compliant": len(group[group["compliant"]]),
|
||||
"rate": f"{len(group[group['compliant']]) / len(group) * 100:.1f}%",
|
||||
"avg_score": round(group["compliance_score"].mean(), 1),
|
||||
}
|
||||
|
||||
return {
|
||||
"total_hosts": total_hosts,
|
||||
"compliant_hosts": compliant,
|
||||
"compliance_rate": f"{compliant / max(total_hosts, 1) * 100:.1f}%",
|
||||
"average_score": round(avg_score, 1),
|
||||
"total_missing_patches": int(self.compliance["total_missing"].sum()),
|
||||
"critical_patches_missing": int(self.compliance["critical_missing"].sum()),
|
||||
"by_tier": by_tier,
|
||||
}
|
||||
|
||||
|
||||
class DeploymentPlanner:
|
||||
"""Generate phased patch deployment plans."""
|
||||
|
||||
DEFAULT_RINGS = {
|
||||
"ring0": {"name": "Lab/Test", "percentage": 0, "soak_hours": 48},
|
||||
"ring1": {"name": "IT Early Adopters", "percentage": 5, "soak_hours": 72},
|
||||
"ring2": {"name": "Business Pilot", "percentage": 15, "soak_hours": 120},
|
||||
"ring3": {"name": "General Deployment", "percentage": 50, "soak_hours": 168},
|
||||
"ring4": {"name": "Mission Critical", "percentage": 30, "soak_hours": 0},
|
||||
}
|
||||
|
||||
def __init__(self, rings_config: dict = None):
|
||||
self.rings = rings_config or self.DEFAULT_RINGS
|
||||
|
||||
def create_deployment_plan(self, patches: list, assets: pd.DataFrame,
|
||||
start_date: datetime = None) -> dict:
|
||||
"""Create a phased deployment plan for patches."""
|
||||
start = start_date or datetime.now()
|
||||
plan = {
|
||||
"plan_id": f"PATCH-{start.strftime('%Y%m%d-%H%M')}",
|
||||
"created": start.isoformat(),
|
||||
"patches": patches,
|
||||
"rings": [],
|
||||
}
|
||||
|
||||
current_date = start
|
||||
for ring_id, ring_config in self.rings.items():
|
||||
ring_hosts = self._assign_ring_hosts(
|
||||
assets, ring_id, ring_config["percentage"]
|
||||
)
|
||||
|
||||
ring_plan = {
|
||||
"ring": ring_id,
|
||||
"name": ring_config["name"],
|
||||
"start_date": current_date.isoformat(),
|
||||
"end_date": (current_date + timedelta(hours=ring_config["soak_hours"])).isoformat(),
|
||||
"soak_hours": ring_config["soak_hours"],
|
||||
"host_count": len(ring_hosts),
|
||||
"hosts": ring_hosts,
|
||||
"status": "pending",
|
||||
"success_criteria": {
|
||||
"max_failure_rate": 5,
|
||||
"required_services_up": True,
|
||||
"no_critical_incidents": True,
|
||||
},
|
||||
}
|
||||
plan["rings"].append(ring_plan)
|
||||
current_date += timedelta(hours=ring_config["soak_hours"])
|
||||
|
||||
plan["estimated_completion"] = current_date.isoformat()
|
||||
return plan
|
||||
|
||||
def _assign_ring_hosts(self, assets: pd.DataFrame, ring_id: str,
|
||||
percentage: int) -> list:
|
||||
"""Assign hosts to deployment rings based on tier and percentage."""
|
||||
if assets.empty or percentage == 0:
|
||||
return []
|
||||
|
||||
ring_map = {
|
||||
"ring0": lambda df: df[df["tier"] == "test"],
|
||||
"ring1": lambda df: df[df["tier"].isin(["dev", "it"])].sample(
|
||||
frac=min(percentage / 100, 1.0), random_state=42
|
||||
) if len(df[df["tier"].isin(["dev", "it"])]) > 0 else pd.DataFrame(),
|
||||
"ring2": lambda df: df[df["tier"] == "staging"],
|
||||
"ring3": lambda df: df[df["tier"] == "production"].sample(
|
||||
frac=0.6, random_state=42
|
||||
) if len(df[df["tier"] == "production"]) > 0 else pd.DataFrame(),
|
||||
"ring4": lambda df: df[df["tier"].isin(["production", "critical"])],
|
||||
}
|
||||
|
||||
selector = ring_map.get(ring_id)
|
||||
if selector:
|
||||
try:
|
||||
selected = selector(assets)
|
||||
return selected["hostname"].tolist() if not selected.empty else []
|
||||
except (KeyError, ValueError):
|
||||
return []
|
||||
return []
|
||||
|
||||
def export_plan(self, plan: dict, output_path: str):
|
||||
"""Export deployment plan to JSON."""
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(plan, f, indent=2, default=str)
|
||||
print(f"[+] Deployment plan exported to: {output_path}")
|
||||
|
||||
|
||||
def generate_compliance_report(summary: dict, compliance_df: pd.DataFrame,
|
||||
output_path: str):
|
||||
"""Generate HTML compliance report."""
|
||||
top_noncompliant = compliance_df.head(20)
|
||||
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Patch Compliance Report - {datetime.now().strftime('%Y-%m-%d')}</title>
|
||||
<style>
|
||||
body {{ font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }}
|
||||
.header {{ background: #1a1a2e; color: white; padding: 20px; border-radius: 8px; }}
|
||||
.metrics {{ display: flex; gap: 15px; margin: 20px 0; flex-wrap: wrap; }}
|
||||
.card {{ background: white; padding: 20px; border-radius: 8px; flex: 1; min-width: 200px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; }}
|
||||
.card h3 {{ margin: 0; font-size: 2em; }}
|
||||
.card p {{ margin: 5px 0 0; color: #666; }}
|
||||
.green {{ border-top: 4px solid #27ae60; }}
|
||||
.red {{ border-top: 4px solid #e74c3c; }}
|
||||
.orange {{ border-top: 4px solid #e67e22; }}
|
||||
table {{ width: 100%; border-collapse: collapse; background: white; margin: 15px 0;
|
||||
border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
|
||||
th {{ background: #2c3e50; color: white; padding: 12px; text-align: left; }}
|
||||
td {{ padding: 10px 12px; border-bottom: 1px solid #eee; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Patch Compliance Report</h1>
|
||||
<p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
|
||||
</div>
|
||||
<div class="metrics">
|
||||
<div class="card green"><h3>{summary['compliance_rate']}</h3><p>Compliance Rate</p></div>
|
||||
<div class="card orange"><h3>{summary['total_hosts']}</h3><p>Total Hosts</p></div>
|
||||
<div class="card red"><h3>{summary['total_missing_patches']}</h3><p>Missing Patches</p></div>
|
||||
<div class="card red"><h3>{summary['critical_patches_missing']}</h3><p>Critical Missing</p></div>
|
||||
</div>
|
||||
|
||||
<h2>Compliance by Tier</h2>
|
||||
<table>
|
||||
<tr><th>Tier</th><th>Total</th><th>Compliant</th><th>Rate</th><th>Avg Score</th></tr>
|
||||
{''.join(f"<tr><td>{tier}</td><td>{data['total']}</td><td>{data['compliant']}</td><td>{data['rate']}</td><td>{data['avg_score']}</td></tr>" for tier, data in summary['by_tier'].items())}
|
||||
</table>
|
||||
|
||||
<h2>Top Non-Compliant Hosts</h2>
|
||||
<table>
|
||||
<tr><th>Hostname</th><th>Tier</th><th>OS</th><th>Critical</th><th>High</th><th>Medium</th><th>Score</th></tr>
|
||||
{''.join(f"<tr><td>{r.hostname}</td><td>{r.tier}</td><td>{r.os}</td><td>{r.critical_missing}</td><td>{r.high_missing}</td><td>{r.medium_missing}</td><td>{r.compliance_score}</td></tr>" for r in top_noncompliant.itertuples())}
|
||||
</table>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
f.write(html)
|
||||
print(f"[+] Report saved to: {output_path}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Patch Management Workflow Automation")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
comp_parser = subparsers.add_parser("compliance", help="Calculate patch compliance")
|
||||
comp_parser.add_argument("--scan-csv", required=True, help="Vulnerability scan results CSV")
|
||||
comp_parser.add_argument("--asset-csv", required=True, help="Asset inventory CSV")
|
||||
comp_parser.add_argument("--output", default=None, help="Output compliance CSV")
|
||||
comp_parser.add_argument("--report", default=None, help="Output HTML report")
|
||||
|
||||
plan_parser = subparsers.add_parser("plan", help="Create deployment plan")
|
||||
plan_parser.add_argument("--patches", required=True, help="Patches CSV")
|
||||
plan_parser.add_argument("--assets", required=True, help="Assets CSV")
|
||||
plan_parser.add_argument("--rings", default=None, help="Rings config YAML")
|
||||
plan_parser.add_argument("--output", default="deployment_plan.json")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "compliance":
|
||||
tracker = PatchComplianceTracker()
|
||||
tracker.load_assets(args.asset_csv)
|
||||
tracker.load_scan_results(args.scan_csv)
|
||||
compliance = tracker.calculate_compliance()
|
||||
summary = tracker.get_summary()
|
||||
|
||||
print("\n=== Patch Compliance Summary ===")
|
||||
print(f"Total Hosts: {summary['total_hosts']}")
|
||||
print(f"Compliant: {summary['compliant_hosts']}")
|
||||
print(f"Compliance Rate: {summary['compliance_rate']}")
|
||||
print(f"Missing Patches: {summary['total_missing_patches']}")
|
||||
print(f"Critical Missing: {summary['critical_patches_missing']}")
|
||||
|
||||
if args.output:
|
||||
compliance.to_csv(args.output, index=False)
|
||||
print(f"[+] Compliance data saved to: {args.output}")
|
||||
|
||||
if args.report:
|
||||
generate_compliance_report(summary, compliance, args.report)
|
||||
|
||||
elif args.command == "plan":
|
||||
rings = None
|
||||
if args.rings:
|
||||
with open(args.rings) as f:
|
||||
rings = yaml.safe_load(f)
|
||||
|
||||
planner = DeploymentPlanner(rings)
|
||||
assets = pd.read_csv(args.assets)
|
||||
patches_df = pd.read_csv(args.patches)
|
||||
patches = patches_df.to_dict(orient="records")
|
||||
|
||||
plan = planner.create_deployment_plan(patches, assets)
|
||||
planner.export_plan(plan, args.output)
|
||||
|
||||
print(f"\n=== Deployment Plan ===")
|
||||
for ring in plan["rings"]:
|
||||
print(f" {ring['name']}: {ring['host_count']} hosts, "
|
||||
f"soak: {ring['soak_hours']}h, start: {ring['start_date'][:10]}")
|
||||
print(f"Estimated completion: {plan['estimated_completion'][:10]}")
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user