npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Vulnerability remediation SLAs define mandatory timeframes for patching or mitigating identified vulnerabilities based on severity, asset criticality, and exploit availability. Effective SLA programs drive accountability, ensure consistent remediation timelines, and provide measurable KPIs for vulnerability management maturity.
When to Use
- When deploying or configuring implementing vulnerability remediation sla 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 scanning program producing regular findings
- Asset inventory with criticality classifications
- Ticketing system (Jira, ServiceNow, etc.) for remediation tracking
- Executive sponsorship for SLA enforcement
- Cross-functional agreement from IT operations, development, and security
Core Concepts
SLA Framework Components
- Severity Classification: CVSS base score + threat context (EPSS, KEV)
- Asset Tiering: Business criticality and exposure level
- Remediation Timeframes: Maximum days to remediate by category
- Exception Process: Documented approval for SLA extensions
- Escalation Procedures: Actions when SLAs are breached
- Metrics and Reporting: KPIs for compliance tracking
Recommended SLA Matrix
| Severity | Tier 1 (Critical) | Tier 2 (Important) | Tier 3 (Standard) |
|---|---|---|---|
| Critical (CVSS 9.0-10.0) | 24-48 hours | 72 hours | 7 days |
| High (CVSS 7.0-8.9) | 7 days | 14 days | 30 days |
| Medium (CVSS 4.0-6.9) | 30 days | 45 days | 60 days |
| Low (CVSS 0.1-3.9) | 90 days | 90 days | 90 days |
| CISA KEV Listed | 24 hours | 48 hours | 7 days |
SLA Accelerators (Reduce SLA by 50%)
- Exploit code publicly available
- Active exploitation observed in the wild (CISA KEV)
- Internet-facing asset affected
- EPSS score > 0.5 (50% exploitation probability)
- Previous breach via similar vulnerability type
Workflow
Step 1: Define Asset Tiers
Tier 1 (Critical Assets):
- Customer-facing production systems
- Payment processing infrastructure
- Domain controllers and identity systems
- Core network infrastructure (firewalls, routers)
- Databases containing PII/PHI/PCI data
Tier 2 (Important Assets):
- Internal production applications
- Email and collaboration systems
- Development/staging environments with production data
- Backup and recovery infrastructure
- VPN and remote access gateways
Tier 3 (Standard Assets):
- End-user workstations
- Development/test environments
- Print servers and peripheral management
- Non-critical internal toolsStep 2: Establish SLA Policy Document
Key sections to include:
- Purpose and scope
- Roles and responsibilities (RACI matrix)
- Severity definitions and calculation method
- Remediation timeframes by severity and asset tier
- Exception request process and approval authority
- Escalation procedures for SLA breaches
- Metrics, reporting cadence, and governance
- Policy review and update schedule
Step 3: Integrate with Ticketing System
# ServiceNow / Jira integration for automatic ticket creation
# See process.py for full implementation
# Key fields for remediation tickets:
# - Vulnerability ID (CVE/Plugin ID)
# - Affected host(s)
# - Severity (CVSS + contextual factors)
# - Asset tier
# - SLA deadline (calculated from discovery date)
# - Assignment group
# - Remediation instructions
# - Verification criteriaStep 4: Configure Escalation Chain
SLA Status Action Notify
───────────────────────────────────────────────────────────
75% elapsed Warning email Asset owner
100% elapsed SLA breach notification Manager + CISO
100% + 7 days Executive escalation VP/CTO
100% + 30 days Risk acceptance required CISO approval
100% + 90 days Compensating controls mandatory Board reportStep 5: Establish Exception Process
Valid exception reasons:
- System cannot be patched without major downtime (scheduled maintenance window)
- No vendor patch available (apply compensating controls)
- Patch breaks critical functionality (require test results as evidence)
- End-of-life system pending decommission (document risk acceptance)
Exception requirements:
- Written justification with business impact
- Compensating controls documented and implemented
- Approved by asset owner AND security leadership
- Maximum exception duration: 90 days (renewable with re-approval)
- Tracked in vulnerability management platform
Key Performance Indicators (KPIs)
Primary Metrics
| KPI | Definition | Target |
|---|---|---|
| SLA Compliance Rate | % of vulns remediated within SLA | >90% |
| Mean Time to Remediate (MTTR) | Average days from discovery to fix | Critical: <3d, High: <10d |
| Vulnerability Backlog | Open vulnerabilities past SLA | <5% of total |
| Exception Rate | % of findings with active exceptions | <10% |
| Recurrence Rate | % of vulns that reappear after remediation | <5% |
Trending Metrics
- Month-over-month SLA compliance trend
- MTTR trend by severity
- Vulnerability density per asset (vulns/host)
- Patch coverage rate (% of assets scanned and compliant)
- Time to first response (acknowledgment of finding)
Best Practices
- Start with achievable SLAs and tighten over time as maturity improves
- Use automated ticketing to eliminate manual SLA tracking
- Provide remediation teams with clear fix instructions, not just CVE numbers
- Track SLA compliance at the team/department level for accountability
- Report SLA metrics to executive leadership monthly
- Include compensating controls as valid interim remediation
- Align SLAs with regulatory requirements (PCI DSS, HIPAA, SOX)
- Review and adjust SLAs annually based on threat landscape changes
Common Pitfalls
- Setting unrealistic SLAs that teams cannot meet (creates SLA fatigue)
- No executive enforcement of SLA breaches
- Treating all assets equally without tiering
- Not accounting for vulnerability context (EPSS, KEV) in SLA calculation
- Missing exception management process (leads to untracked risk)
- Measuring only compliance rate without analyzing root causes of breaches
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md4.7 KB
API Reference: Vulnerability Remediation SLA Tracking
Libraries Used
| Library | Purpose |
|---|---|
requests |
Fetch vulnerability data from scanner APIs |
json |
Parse vulnerability and asset data |
datetime |
Calculate SLA deadlines, time-to-remediation |
csv |
Export SLA compliance reports |
Installation
pip install requestsSLA Tiers
| Severity | CVSS Range | SLA Deadline | Description |
|---|---|---|---|
| Critical | 9.0 - 10.0 | 24 hours | Actively exploited or trivially exploitable |
| High | 7.0 - 8.9 | 72 hours | Remote code execution, privilege escalation |
| Medium | 4.0 - 6.9 | 30 days | Requires user interaction or local access |
| Low | 0.1 - 3.9 | 90 days | Informational, minimal impact |
Core Operations
Define SLA Configuration
from datetime import datetime, timedelta
SLA_TIERS = {
"critical": timedelta(hours=24),
"high": timedelta(hours=72),
"medium": timedelta(days=30),
"low": timedelta(days=90),
}
def get_sla_deadline(severity, discovery_date):
tier = severity.lower()
sla_window = SLA_TIERS.get(tier, timedelta(days=90))
return discovery_date + sla_windowCalculate SLA Status for a Vulnerability
def calculate_sla_status(vuln):
discovery = datetime.fromisoformat(vuln["discovery_date"])
deadline = get_sla_deadline(vuln["severity"], discovery)
now = datetime.now()
if vuln.get("remediated_date"):
remediated = datetime.fromisoformat(vuln["remediated_date"])
return {
"cve": vuln["cve"],
"status": "remediated",
"met_sla": remediated <= deadline,
"time_to_remediate_hours": (remediated - discovery).total_seconds() / 3600,
}
overdue = now > deadline
hours_remaining = (deadline - now).total_seconds() / 3600 if not overdue else 0
hours_overdue = (now - deadline).total_seconds() / 3600 if overdue else 0
return {
"cve": vuln["cve"],
"status": "overdue" if overdue else "open",
"severity": vuln["severity"],
"deadline": deadline.isoformat(),
"hours_remaining": round(hours_remaining, 1),
"hours_overdue": round(hours_overdue, 1),
}Fetch Vulnerabilities from Tenable
import requests
import os
TENABLE_URL = "https://cloud.tenable.com"
headers = {
"X-ApiKeys": f"accessKey={os.environ['TENABLE_ACCESS_KEY']};secretKey={os.environ['TENABLE_SECRET_KEY']}",
}
def get_open_vulnerabilities():
resp = requests.get(
f"{TENABLE_URL}/workbenches/vulnerabilities",
headers=headers,
params={"date_range": 90, "filter.0.filter": "severity", "filter.0.value": "4,3"},
timeout=60,
)
resp.raise_for_status()
return resp.json().get("vulnerabilities", [])Generate SLA Compliance Report
def generate_sla_report(vulnerabilities):
report = {
"total": len(vulnerabilities),
"by_status": {"open": 0, "overdue": 0, "remediated": 0},
"by_severity": {"critical": 0, "high": 0, "medium": 0, "low": 0},
"sla_compliance_rate": 0.0,
"overdue_vulns": [],
"mean_time_to_remediate": {},
}
remediated_times = {"critical": [], "high": [], "medium": [], "low": []}
for vuln in vulnerabilities:
status = calculate_sla_status(vuln)
report["by_status"][status["status"]] += 1
report["by_severity"][vuln["severity"].lower()] += 1
if status["status"] == "overdue":
report["overdue_vulns"].append(status)
if status["status"] == "remediated":
sev = vuln["severity"].lower()
remediated_times[sev].append(status["time_to_remediate_hours"])
total_with_deadline = report["by_status"]["remediated"] + report["by_status"]["overdue"]
if total_with_deadline > 0:
met_sla = sum(1 for v in vulnerabilities
if calculate_sla_status(v).get("met_sla", False))
report["sla_compliance_rate"] = round(met_sla / total_with_deadline * 100, 1)
for sev, times in remediated_times.items():
if times:
report["mean_time_to_remediate"][sev] = round(sum(times) / len(times), 1)
return reportOutput Format
{
"report_date": "2025-01-15",
"total": 245,
"by_status": {"open": 180, "overdue": 23, "remediated": 42},
"by_severity": {"critical": 5, "high": 28, "medium": 112, "low": 100},
"sla_compliance_rate": 87.5,
"mean_time_to_remediate": {
"critical": 18.5,
"high": 52.3,
"medium": 480.0,
"low": 1200.0
},
"overdue_vulns": [
{
"cve": "CVE-2024-21887",
"severity": "critical",
"hours_overdue": 48.5,
"deadline": "2025-01-13T10:00:00"
}
]
}standards.md1.1 KB
Standards and References - Vulnerability Remediation SLA
Regulatory SLA Requirements
- PCI DSS v4.0 Req 6.3.3: Address vulnerabilities by risk ranking (critical/high within 30 days)
- CISA BOD 22-01: Federal agencies must remediate KEV within specified timeframes
- NIST SP 800-40 Rev 4: Enterprise Patch Management Planning
- SOX: Timely remediation of IT control deficiencies
- HIPAA: Reasonable and appropriate security measures including patching
Industry Benchmarks
| Severity | CISA BOD 22-01 | PCI DSS | CIS Benchmark | Best Practice |
|---|---|---|---|---|
| Critical | 14 days (KEV) | 30 days | 48 hours | 24-48 hours |
| High | N/A | 30 days | 7 days | 7-14 days |
| Medium | N/A | 90 days | 30 days | 30 days |
| Low | N/A | Next cycle | 90 days | 90 days |
KPI Benchmarks (Industry Average)
| Metric | Average | Top Quartile | Best in Class |
|---|---|---|---|
| SLA Compliance | 65% | 85% | >95% |
| MTTR (Critical) | 15 days | 5 days | <2 days |
| MTTR (High) | 30 days | 14 days | <7 days |
| Vuln Backlog | 25% | 10% | <5% |
workflows.md1.5 KB
Workflows - Vulnerability Remediation SLA
Workflow 1: SLA Assignment and Tracking
Vulnerability Discovered
│
├──> Determine Severity (CVSS + EPSS + KEV)
├──> Determine Asset Tier (CMDB lookup)
├──> Calculate SLA Deadline
│
├──> Create Remediation Ticket (Auto)
│ ├──> Assign to responsible team
│ ├──> Set SLA deadline
│ └──> Include remediation instructions
│
├──> Monitor Progress
│ ├──> 50% elapsed: Status check
│ ├──> 75% elapsed: Warning notification
│ └──> 100% elapsed: Breach escalation
│
└──> Verify Remediation
├──> Re-scan target
├──> Confirm vulnerability resolved
└──> Close ticketWorkflow 2: SLA Breach Escalation
SLA Breached (100% elapsed)
│
├──> Day 0: Auto-notify asset owner + manager
├──> Day 7: Escalate to department head
├──> Day 14: Escalate to CISO
├──> Day 30: Require formal risk acceptance
└──> Day 90: Report to executive committeeWorkflow 3: Exception Management
Exception Request Submitted
│
├──> Validate justification
├──> Verify compensating controls
├──> Risk assessment review
│
├──> Approved → Set new deadline, document in system
└──> Denied → Original SLA enforced, escalateScripts 2
agent.py8.6 KB
#!/usr/bin/env python3
"""Vulnerability remediation SLA tracking agent.
Tracks vulnerability remediation against defined SLA targets based on
severity. Ingests vulnerability data from scanners (JSON/CSV format),
calculates SLA compliance, identifies overdue items, and generates
remediation priority reports.
"""
import argparse
import csv
import json
import os
import sys
from datetime import datetime, timezone, timedelta
DEFAULT_SLA_DAYS = {
"CRITICAL": 7,
"HIGH": 30,
"MEDIUM": 90,
"LOW": 180,
}
def load_vulnerabilities(source_path):
"""Load vulnerabilities from a JSON or CSV file."""
ext = os.path.splitext(source_path)[1].lower()
if ext == ".json":
with open(source_path, "r") as f:
data = json.load(f)
if isinstance(data, list):
return data
return data.get("vulnerabilities", data.get("findings", data.get("results", [])))
elif ext == ".csv":
vulns = []
with open(source_path, "r", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
vulns.append(row)
return vulns
else:
print(f"[!] Unsupported file format: {ext}", file=sys.stderr)
return []
def normalize_vulnerability(vuln):
"""Normalize vulnerability fields from various scanner formats."""
return {
"id": (vuln.get("id") or vuln.get("vulnerability_id") or
vuln.get("cve_id") or vuln.get("CVE") or vuln.get("plugin_id") or "unknown"),
"severity": (vuln.get("severity") or vuln.get("risk") or
vuln.get("Severity") or "MEDIUM").upper(),
"title": (vuln.get("title") or vuln.get("name") or
vuln.get("vulnerability_name") or vuln.get("Title") or "Unknown"),
"asset": (vuln.get("asset") or vuln.get("host") or
vuln.get("ip") or vuln.get("hostname") or "unknown"),
"discovered_date": (vuln.get("discovered_date") or vuln.get("first_found") or
vuln.get("discovered") or vuln.get("date_found") or
datetime.now(timezone.utc).isoformat()),
"status": (vuln.get("status") or vuln.get("state") or "open").lower(),
"remediation": (vuln.get("remediation") or vuln.get("fix") or
vuln.get("solution") or ""),
}
def calculate_sla_status(vulns, sla_days=None):
"""Calculate SLA compliance for each vulnerability."""
if sla_days is None:
sla_days = DEFAULT_SLA_DAYS
now = datetime.now(timezone.utc)
results = []
for vuln in vulns:
norm = normalize_vulnerability(vuln)
if norm["status"] not in ("open", "new", "active", "unresolved"):
norm["sla_status"] = "RESOLVED"
norm["sla_days_remaining"] = None
results.append(norm)
continue
severity = norm["severity"]
target_days = sla_days.get(severity, sla_days.get("MEDIUM", 90))
try:
disc_str = norm["discovered_date"]
if "T" in disc_str:
discovered = datetime.fromisoformat(disc_str.replace("Z", "+00:00"))
else:
discovered = datetime.strptime(disc_str[:10], "%Y-%m-%d").replace(tzinfo=timezone.utc)
except (ValueError, TypeError):
discovered = now
norm["parse_warning"] = "Could not parse discovered_date"
age_days = (now - discovered).days
sla_deadline = discovered + timedelta(days=target_days)
days_remaining = (sla_deadline - now).days
norm["age_days"] = age_days
norm["sla_target_days"] = target_days
norm["sla_deadline"] = sla_deadline.isoformat()
norm["sla_days_remaining"] = days_remaining
if days_remaining < 0:
norm["sla_status"] = "BREACHED"
norm["sla_overdue_days"] = abs(days_remaining)
elif days_remaining <= 7:
norm["sla_status"] = "AT_RISK"
else:
norm["sla_status"] = "ON_TRACK"
results.append(norm)
return results
def generate_metrics(results):
"""Generate SLA compliance metrics."""
open_vulns = [r for r in results if r.get("sla_status") != "RESOLVED"]
breached = [r for r in open_vulns if r.get("sla_status") == "BREACHED"]
at_risk = [r for r in open_vulns if r.get("sla_status") == "AT_RISK"]
on_track = [r for r in open_vulns if r.get("sla_status") == "ON_TRACK"]
compliance_rate = ((len(on_track) + len(at_risk)) / len(open_vulns) * 100) if open_vulns else 100.0
by_severity = {}
for r in open_vulns:
sev = r.get("severity", "MEDIUM")
by_severity.setdefault(sev, {"total": 0, "breached": 0, "at_risk": 0})
by_severity[sev]["total"] += 1
if r.get("sla_status") == "BREACHED":
by_severity[sev]["breached"] += 1
elif r.get("sla_status") == "AT_RISK":
by_severity[sev]["at_risk"] += 1
oldest_breach = None
if breached:
oldest = max(breached, key=lambda r: r.get("sla_overdue_days", 0))
oldest_breach = {
"id": oldest["id"],
"severity": oldest["severity"],
"overdue_days": oldest.get("sla_overdue_days", 0),
"asset": oldest["asset"],
}
return {
"total_open": len(open_vulns),
"breached": len(breached),
"at_risk": len(at_risk),
"on_track": len(on_track),
"resolved": len(results) - len(open_vulns),
"compliance_rate": round(compliance_rate, 1),
"by_severity": by_severity,
"oldest_breach": oldest_breach,
}
def format_summary(metrics, results):
"""Print SLA tracking summary."""
print(f"\n{'='*60}")
print(f" Vulnerability Remediation SLA Report")
print(f"{'='*60}")
print(f" Open Vulnerabilities : {metrics['total_open']}")
print(f" SLA Breached : {metrics['breached']}")
print(f" At Risk (<7 days) : {metrics['at_risk']}")
print(f" On Track : {metrics['on_track']}")
print(f" Resolved : {metrics['resolved']}")
print(f" Compliance Rate : {metrics['compliance_rate']}%")
print(f"\n By Severity:")
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
data = metrics["by_severity"].get(sev, {})
if data.get("total", 0) > 0:
print(f" {sev:10s}: {data['total']} open, {data['breached']} breached, {data['at_risk']} at-risk")
breached = [r for r in results if r.get("sla_status") == "BREACHED"]
if breached:
print(f"\n SLA Breached ({len(breached)}):")
for r in sorted(breached, key=lambda x: -x.get("sla_overdue_days", 0))[:15]:
print(f" [{r['severity']:8s}] {r['id']:20s} | {r['asset']:20s} | "
f"{r.get('sla_overdue_days', 0)}d overdue | {r['title'][:30]}")
if metrics.get("oldest_breach"):
ob = metrics["oldest_breach"]
print(f"\n Worst Breach: {ob['id']} ({ob['severity']}) on {ob['asset']} - "
f"{ob['overdue_days']} days overdue")
def main():
parser = argparse.ArgumentParser(description="Vulnerability remediation SLA tracking agent")
parser.add_argument("--source", required=True, help="Vulnerability data file (JSON or CSV)")
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("--sla-medium", type=int, default=90, help="SLA days for MEDIUM (default: 90)")
parser.add_argument("--sla-low", type=int, default=180, help="SLA days for LOW (default: 180)")
parser.add_argument("--output", "-o", help="Output JSON report")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
sla_days = {
"CRITICAL": args.sla_critical,
"HIGH": args.sla_high,
"MEDIUM": args.sla_medium,
"LOW": args.sla_low,
}
vulns = load_vulnerabilities(args.source)
print(f"[*] Loaded {len(vulns)} vulnerabilities from {args.source}")
results = calculate_sla_status(vulns, sla_days)
metrics = generate_metrics(results)
format_summary(metrics, results)
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"tool": "Vulnerability SLA Tracker",
"source": args.source,
"sla_targets": sla_days,
"metrics": metrics,
"vulnerabilities": results,
}
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()
process.py10.8 KB
#!/usr/bin/env python3
"""
Vulnerability Remediation SLA Tracking Engine
Calculates SLA deadlines, monitors compliance, generates breach
notifications, and produces executive reporting dashboards.
Requirements:
pip install pandas jinja2
Usage:
python process.py calculate --vulns vulns.csv --assets assets.csv --output sla_assignments.csv
python process.py monitor --sla-csv sla_assignments.csv --report sla_report.html
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
import pandas as pd
class SLACalculator:
"""Calculate remediation SLA deadlines based on severity and asset tier."""
DEFAULT_SLA_MATRIX = {
("Critical", "Tier1"): 2,
("Critical", "Tier2"): 3,
("Critical", "Tier3"): 7,
("High", "Tier1"): 7,
("High", "Tier2"): 14,
("High", "Tier3"): 30,
("Medium", "Tier1"): 30,
("Medium", "Tier2"): 45,
("Medium", "Tier3"): 60,
("Low", "Tier1"): 90,
("Low", "Tier2"): 90,
("Low", "Tier3"): 90,
}
SLA_ACCELERATORS = {
"in_cisa_kev": 0.5,
"exploit_available": 0.5,
"internet_facing": 0.5,
"epss_high": 0.5,
}
def __init__(self, sla_matrix: dict = None):
self.sla_matrix = sla_matrix or self.DEFAULT_SLA_MATRIX
def get_severity_label(self, cvss_score: float) -> str:
"""Map CVSS score to severity label."""
if cvss_score >= 9.0:
return "Critical"
elif cvss_score >= 7.0:
return "High"
elif cvss_score >= 4.0:
return "Medium"
elif cvss_score > 0:
return "Low"
return "Info"
def calculate_sla(self, severity: str, tier: str, accelerators: dict = None) -> int:
"""Calculate SLA days based on severity, tier, and accelerators."""
base_sla = self.sla_matrix.get((severity, tier), 90)
if accelerators:
for accel, factor in self.SLA_ACCELERATORS.items():
if accelerators.get(accel, False):
base_sla = int(base_sla * factor)
return max(base_sla, 1)
def assign_slas(self, vulns_df: pd.DataFrame, assets_df: pd.DataFrame) -> pd.DataFrame:
"""Assign SLA deadlines to all vulnerabilities."""
merged = vulns_df.merge(
assets_df[["hostname", "tier", "internet_facing"]],
on="hostname", how="left"
)
results = []
for _, row in merged.iterrows():
severity = row.get("severity", self.get_severity_label(float(row.get("cvss_score", 0))))
tier = row.get("tier", "Tier3")
accelerators = {
"in_cisa_kev": row.get("in_cisa_kev", False),
"exploit_available": row.get("exploit_available", False),
"internet_facing": row.get("internet_facing", False),
"epss_high": float(row.get("epss_score", 0)) > 0.5,
}
sla_days = self.calculate_sla(severity, tier, accelerators)
discovery_date = pd.to_datetime(row.get("discovery_date", datetime.now()))
deadline = discovery_date + timedelta(days=sla_days)
results.append({
**row.to_dict(),
"severity": severity,
"sla_days": sla_days,
"discovery_date": discovery_date.isoformat(),
"sla_deadline": deadline.isoformat(),
"days_remaining": (deadline - datetime.now()).days,
"sla_status": self._get_sla_status(deadline, row.get("remediated_date")),
})
return pd.DataFrame(results)
def _get_sla_status(self, deadline: datetime, remediated_date=None) -> str:
"""Determine SLA status."""
now = datetime.now()
if remediated_date and pd.notna(remediated_date):
rem_date = pd.to_datetime(remediated_date)
if isinstance(deadline, str):
deadline = pd.to_datetime(deadline)
return "compliant" if rem_date <= deadline else "breached_remediated"
if isinstance(deadline, str):
deadline = pd.to_datetime(deadline)
days_remaining = (deadline - now).days
if days_remaining < 0:
return "breached"
elif days_remaining <= 3:
return "critical"
elif days_remaining <= 7:
return "warning"
return "on_track"
class SLAMonitor:
"""Monitor SLA compliance and generate reports."""
def __init__(self, sla_df: pd.DataFrame):
self.sla_df = sla_df
def get_compliance_summary(self) -> dict:
"""Calculate overall SLA compliance metrics."""
total = len(self.sla_df)
status_counts = self.sla_df["sla_status"].value_counts().to_dict()
compliant = status_counts.get("compliant", 0) + status_counts.get("on_track", 0)
breached = status_counts.get("breached", 0) + status_counts.get("breached_remediated", 0)
warning = status_counts.get("warning", 0) + status_counts.get("critical", 0)
remediated = self.sla_df[self.sla_df["sla_status"].isin(["compliant", "breached_remediated"])]
if not remediated.empty:
mttr_data = remediated.copy()
mttr_data["disc"] = pd.to_datetime(mttr_data["discovery_date"])
mttr_data["rem"] = pd.to_datetime(mttr_data.get("remediated_date", datetime.now()))
avg_mttr = (mttr_data["rem"] - mttr_data["disc"]).dt.days.mean()
else:
avg_mttr = 0
return {
"total_vulns": total,
"compliant": compliant,
"compliance_rate": f"{compliant / max(total, 1) * 100:.1f}%",
"breached": breached,
"breach_rate": f"{breached / max(total, 1) * 100:.1f}%",
"at_risk": warning,
"avg_mttr_days": round(avg_mttr, 1),
"by_severity": self.sla_df.groupby("severity")["sla_status"].value_counts().to_dict(),
}
def get_breach_list(self) -> pd.DataFrame:
"""Get list of SLA-breached vulnerabilities."""
return self.sla_df[self.sla_df["sla_status"] == "breached"].sort_values("days_remaining")
def generate_report(self, output_path: str):
"""Generate SLA compliance HTML report."""
summary = self.get_compliance_summary()
breaches = self.get_breach_list().head(30)
by_sev = self.sla_df.groupby("severity").agg(
total=("sla_status", "count"),
compliant=("sla_status", lambda x: (x.isin(["compliant", "on_track"])).sum()),
breached=("sla_status", lambda x: (x.isin(["breached", "breached_remediated"])).sum()),
).reset_index()
by_sev["rate"] = (by_sev["compliant"] / by_sev["total"] * 100).round(1)
html = f"""<!DOCTYPE html>
<html>
<head>
<title>SLA Compliance Dashboard - {datetime.now().strftime('%Y-%m-%d')}</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }}
.header {{ background: #0f3460; 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: 180px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; }}
.card h3 {{ margin: 0; font-size: 2em; }}
.green {{ border-top: 4px solid #27ae60; }}
.red {{ border-top: 4px solid #e74c3c; }}
.yellow {{ border-top: 4px solid #f39c12; }}
table {{ width: 100%; border-collapse: collapse; background: white; margin: 15px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
th {{ background: #2c3e50; color: white; padding: 10px; text-align: left; }}
td {{ padding: 8px 10px; border-bottom: 1px solid #eee; }}
</style>
</head>
<body>
<div class="header">
<h1>Vulnerability Remediation SLA Dashboard</h1>
<p>Report Date: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
</div>
<div class="metrics">
<div class="card green"><h3>{summary['compliance_rate']}</h3><p>SLA Compliance</p></div>
<div class="card red"><h3>{summary['breached']}</h3><p>SLA Breaches</p></div>
<div class="card yellow"><h3>{summary['at_risk']}</h3><p>At Risk</p></div>
<div class="card"><h3>{summary['avg_mttr_days']}d</h3><p>Avg MTTR</p></div>
</div>
<h2>Compliance by Severity</h2>
<table>
<tr><th>Severity</th><th>Total</th><th>Compliant</th><th>Breached</th><th>Rate</th></tr>
{''.join(f"<tr><td>{r.severity}</td><td>{r.total}</td><td>{r.compliant}</td><td>{r.breached}</td><td>{r.rate}%</td></tr>" for r in by_sev.itertuples())}
</table>
<h2>Active SLA Breaches (Top 30)</h2>
<table>
<tr><th>Host</th><th>CVE</th><th>Severity</th><th>SLA Days</th><th>Days Overdue</th></tr>
{''.join(f"<tr><td>{r.hostname if hasattr(r,'hostname') else ''}</td><td>{r.cve if hasattr(r,'cve') else ''}</td><td>{r.severity}</td><td>{r.sla_days}</td><td>{abs(r.days_remaining)}</td></tr>" for r in breaches.itertuples())}
</table>
</body>
</html>"""
with open(output_path, "w", encoding="utf-8") as f:
f.write(html)
print(f"[+] SLA report saved to: {output_path}")
def main():
parser = argparse.ArgumentParser(description="Vulnerability Remediation SLA Engine")
subparsers = parser.add_subparsers(dest="command")
calc_p = subparsers.add_parser("calculate", help="Assign SLA deadlines")
calc_p.add_argument("--vulns", required=True, help="Vulnerabilities CSV")
calc_p.add_argument("--assets", required=True, help="Assets CSV")
calc_p.add_argument("--output", required=True, help="Output SLA CSV")
mon_p = subparsers.add_parser("monitor", help="Monitor SLA compliance")
mon_p.add_argument("--sla-csv", required=True, help="SLA assignments CSV")
mon_p.add_argument("--report", default="sla_report.html", help="HTML report output")
args = parser.parse_args()
if args.command == "calculate":
calc = SLACalculator()
vulns = pd.read_csv(args.vulns)
assets = pd.read_csv(args.assets)
result = calc.assign_slas(vulns, assets)
result.to_csv(args.output, index=False)
print(f"[+] SLA assignments saved to: {args.output}")
print(f" Total: {len(result)}, Breached: {len(result[result['sla_status']=='breached'])}")
elif args.command == "monitor":
sla_df = pd.read_csv(args.sla_csv)
monitor = SLAMonitor(sla_df)
summary = monitor.get_compliance_summary()
print(f"\n=== SLA Compliance Summary ===")
print(f"Compliance Rate: {summary['compliance_rate']}")
print(f"Breaches: {summary['breached']}")
print(f"Avg MTTR: {summary['avg_mttr_days']} days")
monitor.generate_report(args.report)
else:
parser.print_help()
if __name__ == "__main__":
main()