mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-07 19:20:17 +03:00
Expand 39 api-reference stubs with real tool docs, expand 15 agent.py boilerplate stubs
This commit is contained in:
@@ -13,6 +13,14 @@ license: Apache-2.0
|
||||
## 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.
|
||||
|
||||
|
||||
## When to Use
|
||||
|
||||
- When deploying or configuring implementing patch management workflow 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
|
||||
- Vulnerability scan results identifying missing patches
|
||||
- Patch management tools (WSUS, SCCM/MECM, Ansible, Intune, Jamf)
|
||||
|
||||
@@ -1,61 +1,284 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Patch management workflow automation."""
|
||||
import argparse, json
|
||||
from datetime import datetime, timezone
|
||||
"""Patch management workflow agent.
|
||||
|
||||
Audits system patch compliance by checking installed package versions
|
||||
against known vulnerabilities, tracking patch SLA adherence, and
|
||||
generating remediation reports. Supports Linux (apt/yum) and basic
|
||||
CVE cross-referencing via the CISA KEV catalog.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
def audit_config(target, token):
|
||||
|
||||
def check_apt_updates():
|
||||
"""Check for available security updates on Debian/Ubuntu systems."""
|
||||
findings = []
|
||||
if not requests: return [{"error": "requests required"}]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/status", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
if not data.get("enabled", True):
|
||||
findings.append({"check": "Service Status", "status": "DISABLED", "severity": "CRITICAL"})
|
||||
elif resp.status_code == 401:
|
||||
findings.append({"check": "Authentication", "status": "UNAUTHORIZED", "severity": "HIGH"})
|
||||
except requests.RequestException as e:
|
||||
findings.append({"error": str(e)})
|
||||
print("[*] Checking apt security updates...")
|
||||
|
||||
# Update package lists
|
||||
result = subprocess.run(
|
||||
["apt-get", "update", "-qq"],
|
||||
capture_output=True, text=True, timeout=120,
|
||||
)
|
||||
|
||||
# List upgradable packages
|
||||
result = subprocess.run(
|
||||
["apt", "list", "--upgradable"],
|
||||
capture_output=True, text=True, timeout=60,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return [{"check": "apt updates", "status": "ERROR", "severity": "HIGH",
|
||||
"detail": result.stderr[:200]}]
|
||||
|
||||
for line in result.stdout.strip().splitlines():
|
||||
if "Listing..." in line:
|
||||
continue
|
||||
parts = line.split("/")
|
||||
if len(parts) >= 2:
|
||||
pkg_name = parts[0]
|
||||
is_security = "security" in line.lower()
|
||||
version_info = line.split()
|
||||
current = version_info[-1] if len(version_info) > 3 else "unknown"
|
||||
available = version_info[1] if len(version_info) > 1 else "unknown"
|
||||
findings.append({
|
||||
"package": pkg_name,
|
||||
"current_version": current,
|
||||
"available_version": available,
|
||||
"is_security_update": is_security,
|
||||
"severity": "CRITICAL" if is_security else "MEDIUM",
|
||||
})
|
||||
|
||||
security_count = sum(1 for f in findings if f.get("is_security_update"))
|
||||
print(f"[+] Found {len(findings)} updates ({security_count} security)")
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
|
||||
def check_yum_updates():
|
||||
"""Check for available security updates on RHEL/CentOS systems."""
|
||||
findings = []
|
||||
if not requests: return []
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/compliance", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
for item in resp.json().get("checks", []):
|
||||
if item.get("status") != "PASS":
|
||||
findings.append({"check": item.get("name"), "status": item.get("status"),
|
||||
"severity": item.get("severity", "MEDIUM")})
|
||||
except requests.RequestException:
|
||||
pass
|
||||
print("[*] Checking yum/dnf security updates...")
|
||||
|
||||
for pkg_mgr in ["dnf", "yum"]:
|
||||
result = subprocess.run(
|
||||
[pkg_mgr, "check-update", "--security", "-q"],
|
||||
capture_output=True, text=True, timeout=120,
|
||||
)
|
||||
if result.returncode in (0, 100):
|
||||
break
|
||||
else:
|
||||
return [{"check": "yum/dnf", "status": "ERROR", "severity": "HIGH",
|
||||
"detail": "Neither yum nor dnf available"}]
|
||||
|
||||
for line in result.stdout.strip().splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3:
|
||||
findings.append({
|
||||
"package": parts[0],
|
||||
"available_version": parts[1],
|
||||
"repository": parts[2] if len(parts) > 2 else "",
|
||||
"is_security_update": True,
|
||||
"severity": "HIGH",
|
||||
})
|
||||
|
||||
print(f"[+] Found {len(findings)} security updates")
|
||||
return findings
|
||||
|
||||
|
||||
def check_windows_updates():
|
||||
"""Check for pending Windows updates via PowerShell."""
|
||||
findings = []
|
||||
print("[*] Checking Windows Update status...")
|
||||
|
||||
ps_script = (
|
||||
"Get-HotFix | Sort-Object InstalledOn -Descending | "
|
||||
"Select-Object -First 20 HotFixID, Description, InstalledOn | "
|
||||
"ConvertTo-Json"
|
||||
)
|
||||
result = subprocess.run(
|
||||
["powershell", "-Command", ps_script],
|
||||
capture_output=True, text=True, timeout=120,
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
try:
|
||||
hotfixes = json.loads(result.stdout)
|
||||
if isinstance(hotfixes, dict):
|
||||
hotfixes = [hotfixes]
|
||||
for hf in hotfixes:
|
||||
findings.append({
|
||||
"hotfix_id": hf.get("HotFixID", ""),
|
||||
"description": hf.get("Description", ""),
|
||||
"installed_on": str(hf.get("InstalledOn", "")),
|
||||
"status": "installed",
|
||||
})
|
||||
if hotfixes:
|
||||
latest = hotfixes[0]
|
||||
installed_date = latest.get("InstalledOn", "")
|
||||
print(f"[+] Latest patch: {latest.get('HotFixID', 'N/A')} ({installed_date})")
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def check_kev_exposure(package_cves):
|
||||
"""Cross-reference package CVEs against CISA KEV catalog."""
|
||||
if not requests:
|
||||
return []
|
||||
|
||||
kev_url = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
|
||||
try:
|
||||
resp = requests.get(kev_url, timeout=30)
|
||||
resp.raise_for_status()
|
||||
kev_data = resp.json()
|
||||
kev_cves = {v["cveID"] for v in kev_data.get("vulnerabilities", [])}
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
exposed = []
|
||||
for cve_id in package_cves:
|
||||
if cve_id.upper() in kev_cves:
|
||||
exposed.append({
|
||||
"cve_id": cve_id,
|
||||
"in_kev": True,
|
||||
"severity": "CRITICAL",
|
||||
"description": "Actively exploited vulnerability (CISA KEV)",
|
||||
})
|
||||
return exposed
|
||||
|
||||
|
||||
def assess_patch_sla(findings, sla_days=None):
|
||||
"""Assess patch compliance against SLA targets."""
|
||||
if sla_days is None:
|
||||
sla_days = {"CRITICAL": 7, "HIGH": 30, "MEDIUM": 90, "LOW": 180}
|
||||
|
||||
sla_findings = []
|
||||
for f in findings:
|
||||
severity = f.get("severity", "MEDIUM")
|
||||
target_days = sla_days.get(severity, 90)
|
||||
sla_findings.append({
|
||||
"package": f.get("package", f.get("hotfix_id", "unknown")),
|
||||
"severity": severity,
|
||||
"sla_target_days": target_days,
|
||||
"in_sla": True, # Would need install date to determine
|
||||
"recommendation": f"Patch within {target_days} days per SLA policy",
|
||||
})
|
||||
return sla_findings
|
||||
|
||||
|
||||
def format_summary(findings, kev_findings, sla_findings, platform):
|
||||
"""Print patch management summary."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Patch Management Audit Report")
|
||||
print(f"{'='*60}")
|
||||
print(f" Platform : {platform}")
|
||||
print(f" Pending Updates : {len(findings)}")
|
||||
|
||||
security = sum(1 for f in findings if f.get("is_security_update"))
|
||||
print(f" Security Updates: {security}")
|
||||
print(f" KEV Matches : {len(kev_findings)}")
|
||||
|
||||
severity_counts = {}
|
||||
for f in findings:
|
||||
sev = f.get("severity", "MEDIUM")
|
||||
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
||||
|
||||
print(f"\n By Severity:")
|
||||
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
||||
count = severity_counts.get(sev, 0)
|
||||
if count > 0:
|
||||
print(f" {sev:10s}: {count}")
|
||||
|
||||
if kev_findings:
|
||||
print(f"\n CISA KEV Exposed CVEs (IMMEDIATE ACTION):")
|
||||
for k in kev_findings:
|
||||
print(f" {k['cve_id']}: {k['description']}")
|
||||
|
||||
if findings:
|
||||
print(f"\n Pending Updates:")
|
||||
for f in findings[:20]:
|
||||
pkg = f.get("package", f.get("hotfix_id", "unknown"))
|
||||
sev = f.get("severity", "MEDIUM")
|
||||
sec = " [SECURITY]" if f.get("is_security_update") else ""
|
||||
print(f" [{sev:8s}] {pkg}{sec}")
|
||||
|
||||
return severity_counts
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Patch management workflow automation")
|
||||
p.add_argument("--target", required=True, help="Target URL")
|
||||
p.add_argument("--token", required=True, help="API token")
|
||||
p.add_argument("--output", "-o", help="Output JSON report")
|
||||
p.add_argument("--verbose", "-v", action="store_true")
|
||||
a = p.parse_args()
|
||||
print("[*] Patch management workflow automation")
|
||||
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}
|
||||
report["findings"].extend(audit_config(a.target, a.token))
|
||||
report["findings"].extend(check_compliance(a.target, a.token))
|
||||
high = sum(1 for f in report["findings"] if f.get("severity") in ("HIGH", "CRITICAL"))
|
||||
report["risk_level"] = "HIGH" if high else "MEDIUM" if report["findings"] else "LOW"
|
||||
print(f"[*] {len(report['findings'])} findings, risk: {report['risk_level']}")
|
||||
if a.output:
|
||||
with open(a.output, "w") as f: json.dump(report, f, indent=2)
|
||||
else:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Patch management workflow audit agent"
|
||||
)
|
||||
parser.add_argument("--platform", choices=["auto", "apt", "yum", "windows"],
|
||||
default="auto", help="Package manager to check")
|
||||
parser.add_argument("--cves", nargs="+", help="CVE IDs to check against KEV")
|
||||
parser.add_argument("--sla-critical", type=int, default=7, help="SLA days for critical (default: 7)")
|
||||
parser.add_argument("--sla-high", type=int, default=30, help="SLA days for high (default: 30)")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report path")
|
||||
parser.add_argument("--verbose", "-v", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
findings = []
|
||||
platform = args.platform
|
||||
|
||||
if platform == "auto":
|
||||
if sys.platform == "win32":
|
||||
platform = "windows"
|
||||
elif os.path.isfile("/usr/bin/apt"):
|
||||
platform = "apt"
|
||||
elif os.path.isfile("/usr/bin/yum") or os.path.isfile("/usr/bin/dnf"):
|
||||
platform = "yum"
|
||||
else:
|
||||
print("[!] Could not detect package manager", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if platform == "apt":
|
||||
findings = check_apt_updates()
|
||||
elif platform == "yum":
|
||||
findings = check_yum_updates()
|
||||
elif platform == "windows":
|
||||
findings = check_windows_updates()
|
||||
|
||||
kev_findings = []
|
||||
if args.cves:
|
||||
kev_findings = check_kev_exposure(args.cves)
|
||||
|
||||
sla_days = {"CRITICAL": args.sla_critical, "HIGH": args.sla_high, "MEDIUM": 90, "LOW": 180}
|
||||
sla_findings = assess_patch_sla(findings, sla_days)
|
||||
|
||||
severity_counts = format_summary(findings, kev_findings, sla_findings, platform)
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "Patch Management Audit",
|
||||
"platform": platform,
|
||||
"pending_updates": findings,
|
||||
"kev_exposure": kev_findings,
|
||||
"sla_assessment": sla_findings,
|
||||
"severity_counts": severity_counts,
|
||||
"risk_level": (
|
||||
"CRITICAL" if kev_findings or severity_counts.get("CRITICAL", 0) > 0
|
||||
else "HIGH" if severity_counts.get("HIGH", 0) > 0
|
||||
else "MEDIUM" if findings
|
||||
else "LOW"
|
||||
),
|
||||
}
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
elif args.verbose:
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user