mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-30 07:56:52 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
---
|
||||
name: implementing-vulnerability-sla-breach-alerting
|
||||
description: Build automated alerting for vulnerability remediation SLA breaches with severity-based timelines, escalation workflows, and compliance reporting dashboards.
|
||||
domain: cybersecurity
|
||||
subdomain: vulnerability-management
|
||||
tags: [vulnerability-sla, remediation-tracking, alerting, compliance, sla-breach, vulnerability-management, escalation]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Implementing Vulnerability SLA Breach Alerting
|
||||
|
||||
## Overview
|
||||
|
||||
Vulnerability remediation SLAs define maximum timeframes for addressing security findings based on severity. This skill covers building an automated alerting system that tracks remediation timelines, detects SLA breaches, sends escalation notifications, and generates compliance reports. Industry-standard SLA targets are: Critical (24-48 hours), High (15-30 days), Medium (60 days), Low (90 days).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.9+ with `requests`, `pandas`, `jinja2`, `smtplib` libraries
|
||||
- Vulnerability management platform with API access (DefectDojo, Qualys, Tenable)
|
||||
- SMTP server or webhook endpoint (Slack, Microsoft Teams, PagerDuty)
|
||||
- Database for SLA tracking (PostgreSQL or SQLite)
|
||||
|
||||
## SLA Policy Definition
|
||||
|
||||
### Standard SLA Tiers
|
||||
|
||||
| Severity | Remediation SLA | Grace Period | Escalation Level |
|
||||
|----------|----------------|--------------|-----------------|
|
||||
| Critical (CVSS 9.0-10.0) | 48 hours | 12 hours | VP Engineering + CISO |
|
||||
| High (CVSS 7.0-8.9) | 15 days | 5 days | Director of Engineering |
|
||||
| Medium (CVSS 4.0-6.9) | 60 days | 14 days | Team Lead |
|
||||
| Low (CVSS 0.1-3.9) | 90 days | 30 days | Asset Owner |
|
||||
|
||||
### SLA Configuration File
|
||||
|
||||
```yaml
|
||||
# sla_policy.yaml
|
||||
sla_tiers:
|
||||
critical:
|
||||
cvss_min: 9.0
|
||||
cvss_max: 10.0
|
||||
remediation_days: 2
|
||||
grace_period_days: 0.5
|
||||
escalation_contacts:
|
||||
- ciso@company.com
|
||||
- vp-engineering@company.com
|
||||
pagerduty_severity: critical
|
||||
high:
|
||||
cvss_min: 7.0
|
||||
cvss_max: 8.9
|
||||
remediation_days: 15
|
||||
grace_period_days: 5
|
||||
escalation_contacts:
|
||||
- security-director@company.com
|
||||
pagerduty_severity: high
|
||||
medium:
|
||||
cvss_min: 4.0
|
||||
cvss_max: 6.9
|
||||
remediation_days: 60
|
||||
grace_period_days: 14
|
||||
escalation_contacts:
|
||||
- team-lead@company.com
|
||||
pagerduty_severity: warning
|
||||
low:
|
||||
cvss_min: 0.1
|
||||
cvss_max: 3.9
|
||||
remediation_days: 90
|
||||
grace_period_days: 30
|
||||
escalation_contacts:
|
||||
- asset-owner@company.com
|
||||
pagerduty_severity: info
|
||||
|
||||
notification_channels:
|
||||
slack:
|
||||
webhook_url: "${SLACK_WEBHOOK_URL}"
|
||||
channel: "#vulnerability-alerts"
|
||||
email:
|
||||
smtp_host: smtp.company.com
|
||||
smtp_port: 587
|
||||
from_address: vuln-alerts@company.com
|
||||
pagerduty:
|
||||
api_key: "${PAGERDUTY_API_KEY}"
|
||||
service_id: "${PAGERDUTY_SERVICE_ID}"
|
||||
|
||||
alert_schedules:
|
||||
approaching_breach:
|
||||
percentage_elapsed: 80
|
||||
frequency_hours: 24
|
||||
at_breach:
|
||||
notification: immediate
|
||||
escalation: true
|
||||
post_breach:
|
||||
frequency_hours: 12
|
||||
escalation_increase: true
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Database Schema for SLA Tracking
|
||||
|
||||
```sql
|
||||
CREATE TABLE vulnerability_sla (
|
||||
id SERIAL PRIMARY KEY,
|
||||
cve_id VARCHAR(20) NOT NULL,
|
||||
finding_id VARCHAR(100) NOT NULL,
|
||||
asset_hostname VARCHAR(255),
|
||||
severity VARCHAR(20) NOT NULL,
|
||||
cvss_score DECIMAL(3,1),
|
||||
discovered_at TIMESTAMP NOT NULL,
|
||||
sla_deadline TIMESTAMP NOT NULL,
|
||||
remediated_at TIMESTAMP,
|
||||
status VARCHAR(20) DEFAULT 'open',
|
||||
owner_email VARCHAR(255),
|
||||
escalation_level INTEGER DEFAULT 0,
|
||||
last_alert_sent TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sla_status ON vulnerability_sla(status);
|
||||
CREATE INDEX idx_sla_deadline ON vulnerability_sla(sla_deadline);
|
||||
CREATE INDEX idx_sla_severity ON vulnerability_sla(severity);
|
||||
```
|
||||
|
||||
### Step 2: SLA Breach Detection Logic
|
||||
|
||||
```python
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import yaml
|
||||
|
||||
def load_sla_policy(policy_path="sla_policy.yaml"):
|
||||
with open(policy_path, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def get_sla_tier(cvss_score, policy):
|
||||
for tier_name, tier in policy["sla_tiers"].items():
|
||||
if tier["cvss_min"] <= cvss_score <= tier["cvss_max"]:
|
||||
return tier_name, tier
|
||||
return "low", policy["sla_tiers"]["low"]
|
||||
|
||||
def calculate_sla_deadline(discovered_at, cvss_score, policy):
|
||||
tier_name, tier = get_sla_tier(cvss_score, policy)
|
||||
deadline = discovered_at + timedelta(days=tier["remediation_days"])
|
||||
return deadline, tier_name
|
||||
|
||||
def check_sla_status(discovered_at, sla_deadline, remediated_at=None):
|
||||
now = datetime.now(timezone.utc)
|
||||
if remediated_at:
|
||||
if remediated_at <= sla_deadline:
|
||||
return "remediated_within_sla"
|
||||
return "remediated_breach"
|
||||
if now > sla_deadline:
|
||||
overdue_days = (now - sla_deadline).days
|
||||
return f"breached_{overdue_days}d_overdue"
|
||||
remaining = sla_deadline - now
|
||||
total_sla = sla_deadline - discovered_at
|
||||
pct_elapsed = ((total_sla - remaining) / total_sla) * 100
|
||||
if pct_elapsed >= 80:
|
||||
return "approaching_breach"
|
||||
return "within_sla"
|
||||
```
|
||||
|
||||
### Step 3: Notification Dispatch
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
def send_slack_alert(webhook_url, vuln_data, sla_status):
|
||||
color = {"breached": "#FF0000", "approaching_breach": "#FFA500", "within_sla": "#36A64F"}
|
||||
status_color = color.get("breached" if "breached" in sla_status else sla_status, "#808080")
|
||||
payload = {
|
||||
"attachments": [{
|
||||
"color": status_color,
|
||||
"title": f"Vulnerability SLA Alert: {vuln_data['cve_id']}",
|
||||
"fields": [
|
||||
{"title": "Severity", "value": vuln_data["severity"], "short": True},
|
||||
{"title": "CVSS", "value": str(vuln_data["cvss_score"]), "short": True},
|
||||
{"title": "Asset", "value": vuln_data["asset_hostname"], "short": True},
|
||||
{"title": "SLA Status", "value": sla_status, "short": True},
|
||||
{"title": "Deadline", "value": vuln_data["sla_deadline"].strftime("%Y-%m-%d %H:%M UTC"), "short": True},
|
||||
{"title": "Owner", "value": vuln_data.get("owner_email", "Unassigned"), "short": True},
|
||||
],
|
||||
}]
|
||||
}
|
||||
requests.post(webhook_url, json=payload, timeout=10)
|
||||
|
||||
def send_pagerduty_alert(api_key, service_id, vuln_data, severity):
|
||||
payload = {
|
||||
"routing_key": api_key,
|
||||
"event_action": "trigger",
|
||||
"payload": {
|
||||
"summary": f"SLA Breach: {vuln_data['cve_id']} on {vuln_data['asset_hostname']}",
|
||||
"severity": severity,
|
||||
"source": vuln_data["asset_hostname"],
|
||||
"custom_details": {
|
||||
"cve_id": vuln_data["cve_id"],
|
||||
"cvss_score": vuln_data["cvss_score"],
|
||||
"sla_deadline": vuln_data["sla_deadline"].isoformat(),
|
||||
}
|
||||
}
|
||||
}
|
||||
requests.post(
|
||||
"https://events.pagerduty.com/v2/enqueue",
|
||||
json=payload, timeout=10
|
||||
)
|
||||
|
||||
def send_email_alert(smtp_config, to_addresses, vuln_data, sla_status):
|
||||
msg = MIMEMultipart("alternative")
|
||||
msg["Subject"] = f"[SLA {sla_status.upper()}] {vuln_data['cve_id']} - {vuln_data['severity']}"
|
||||
msg["From"] = smtp_config["from_address"]
|
||||
msg["To"] = ", ".join(to_addresses)
|
||||
body = f"""
|
||||
Vulnerability SLA Alert
|
||||
|
||||
CVE: {vuln_data['cve_id']}
|
||||
Severity: {vuln_data['severity']} (CVSS {vuln_data['cvss_score']})
|
||||
Asset: {vuln_data['asset_hostname']}
|
||||
SLA Deadline: {vuln_data['sla_deadline'].strftime('%Y-%m-%d %H:%M UTC')}
|
||||
Status: {sla_status}
|
||||
Owner: {vuln_data.get('owner_email', 'Unassigned')}
|
||||
|
||||
Please take immediate action to remediate this vulnerability.
|
||||
"""
|
||||
msg.attach(MIMEText(body, "plain"))
|
||||
with smtplib.SMTP(smtp_config["smtp_host"], smtp_config["smtp_port"]) as server:
|
||||
server.starttls()
|
||||
server.send_message(msg)
|
||||
```
|
||||
|
||||
### Step 4: Scheduled SLA Check Runner
|
||||
|
||||
```bash
|
||||
# Run SLA breach check every hour via cron
|
||||
echo "0 * * * * cd /opt/vuln-sla && python3 scripts/process.py --check-sla" | crontab -
|
||||
|
||||
# Manual check
|
||||
python3 scripts/process.py --check-sla --policy sla_policy.yaml
|
||||
|
||||
# Generate SLA compliance report
|
||||
python3 scripts/process.py --report --period monthly --output sla_report.html
|
||||
```
|
||||
|
||||
## SLA Metrics Dashboard
|
||||
|
||||
### Key Performance Indicators
|
||||
|
||||
```python
|
||||
def calculate_sla_metrics(db_connection, period_start, period_end):
|
||||
metrics = {
|
||||
"total_findings": 0,
|
||||
"remediated_within_sla": 0,
|
||||
"sla_breach_count": 0,
|
||||
"mean_time_to_remediate": {},
|
||||
"sla_compliance_rate": 0.0,
|
||||
"current_overdue": 0,
|
||||
}
|
||||
# Query findings in period grouped by severity
|
||||
query = """
|
||||
SELECT severity, COUNT(*) as total,
|
||||
SUM(CASE WHEN remediated_at <= sla_deadline THEN 1 ELSE 0 END) as within_sla,
|
||||
AVG(EXTRACT(EPOCH FROM (COALESCE(remediated_at, NOW()) - discovered_at))/86400) as avg_days
|
||||
FROM vulnerability_sla
|
||||
WHERE discovered_at BETWEEN %s AND %s
|
||||
GROUP BY severity
|
||||
"""
|
||||
return metrics
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Vulnerability Management SLAs Guide](https://hostedscan.com/blog/vulnerability-management-slas-guide)
|
||||
- [NIST SP 800-40 Rev 4 - Patch Management](https://csrc.nist.gov/publications/detail/sp/800-40/rev-4/final)
|
||||
- [PagerDuty Events API v2](https://developer.pagerduty.com/api-reference/a7d81b0e9200f-send-an-event-to-pager-duty)
|
||||
- [Slack Incoming Webhooks](https://api.slack.com/messaging/webhooks)
|
||||
@@ -0,0 +1,87 @@
|
||||
# Vulnerability SLA Policy Template
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
This policy establishes remediation timelines for security vulnerabilities based on severity classification, defines escalation procedures for SLA breaches, and specifies reporting requirements for compliance tracking.
|
||||
|
||||
## 2. Scope
|
||||
|
||||
This policy applies to all information systems, applications, and infrastructure components managed by [Organization Name].
|
||||
|
||||
## 3. SLA Definitions
|
||||
|
||||
| Severity | CVSS Range | Remediation Timeline | Grace Period | Escalation Path |
|
||||
|----------|-----------|---------------------|--------------|-----------------|
|
||||
| Critical | 9.0 - 10.0 | 48 hours | 12 hours | Asset Owner -> Security Director -> CISO |
|
||||
| High | 7.0 - 8.9 | 15 calendar days | 5 days | Asset Owner -> Team Lead -> Security Director |
|
||||
| Medium | 4.0 - 6.9 | 60 calendar days | 14 days | Asset Owner -> Team Lead |
|
||||
| Low | 0.1 - 3.9 | 90 calendar days | 30 days | Asset Owner |
|
||||
|
||||
## 4. Exception Process
|
||||
|
||||
### 4.1 Exception Request Requirements
|
||||
- CVE identifier and affected system details
|
||||
- Business justification for extension
|
||||
- Compensating controls implemented
|
||||
- Proposed new remediation date
|
||||
- Risk acceptance signature from system owner and CISO
|
||||
|
||||
### 4.2 Maximum Exception Duration
|
||||
- Critical: 14 days maximum extension
|
||||
- High: 30 days maximum extension
|
||||
- Medium: 60 days maximum extension
|
||||
- Low: 90 days maximum extension
|
||||
|
||||
## 5. Alerting Configuration
|
||||
|
||||
### 5.1 Notification Schedule
|
||||
```
|
||||
80% SLA elapsed -> Warning to asset owner (email + Slack)
|
||||
100% SLA elapsed -> Breach alert (email + Slack + PagerDuty for Critical/High)
|
||||
SLA + 24 hours -> Escalation Level 1 (team lead)
|
||||
SLA + 72 hours -> Escalation Level 2 (director)
|
||||
SLA + 7 days -> Escalation Level 3 (CISO)
|
||||
```
|
||||
|
||||
### 5.2 Notification Channels
|
||||
- **Email**: All severity levels
|
||||
- **Slack**: High and Critical severity
|
||||
- **PagerDuty**: Critical severity SLA breaches only
|
||||
- **Jira**: Automatic ticket creation for all findings
|
||||
|
||||
## 6. Reporting Requirements
|
||||
|
||||
### 6.1 Weekly Report
|
||||
- Count of open findings by severity
|
||||
- Count of SLA breaches by severity
|
||||
- Top 5 assets with most open findings
|
||||
- Remediation velocity trend
|
||||
|
||||
### 6.2 Monthly Report
|
||||
- Overall SLA compliance rate by severity
|
||||
- Mean time to remediate by severity
|
||||
- Exception count and approval rate
|
||||
- Quarter-over-quarter improvement trends
|
||||
|
||||
### 6.3 Executive Dashboard
|
||||
- Overall compliance percentage
|
||||
- Risk exposure trend (critical/high open count over time)
|
||||
- Team/business unit comparison
|
||||
- Regulatory compliance status (PCI, SOC2, HIPAA)
|
||||
|
||||
## 7. Compliance Mapping
|
||||
|
||||
| Regulation | Requirement | SLA Alignment |
|
||||
|-----------|------------|---------------|
|
||||
| PCI DSS 4.0 | Req 6.3.3 | Critical/High within 30 days |
|
||||
| SOC 2 | CC7.1 | Evidence of SLA tracking and remediation |
|
||||
| HIPAA | 164.312(a)(1) | Risk-based remediation timeline |
|
||||
| CISA BOD 22-01 | KEV remediation | 14 days for KEV-listed CVEs |
|
||||
| NIST CSF 2.0 | ID.RA-01 | Risk-ranked vulnerability management |
|
||||
|
||||
## 8. Roles and Responsibilities
|
||||
|
||||
- **Asset Owner**: Remediate within SLA, request exceptions when needed
|
||||
- **Security Team**: Monitor SLA compliance, manage alerting system
|
||||
- **Team Lead**: Review team SLA metrics, escalate blockers
|
||||
- **CISO**: Approve critical exceptions, review monthly metrics
|
||||
@@ -0,0 +1,58 @@
|
||||
# Standards and References - Vulnerability SLA Breach Alerting
|
||||
|
||||
## Primary Standards
|
||||
|
||||
### NIST SP 800-40 Rev 4
|
||||
- **Title**: Guide to Enterprise Patch Management Planning
|
||||
- **URL**: https://csrc.nist.gov/publications/detail/sp/800-40/rev-4/final
|
||||
- **Relevance**: Defines organizational patch management lifecycle and remediation timelines
|
||||
|
||||
### CISA Binding Operational Directive 22-01
|
||||
- **Title**: Reducing the Significant Risk of Known Exploited Vulnerabilities
|
||||
- **URL**: https://www.cisa.gov/binding-operational-directive-22-01
|
||||
- **SLA Mandate**: Federal agencies must remediate KEV-listed vulnerabilities within specified timeframes (typically 14 days for new additions)
|
||||
|
||||
### PCI DSS v4.0 Requirement 6.3
|
||||
- **Title**: Security Vulnerabilities Are Identified and Addressed
|
||||
- **URL**: https://docs-prv.pcisecuritystandards.org/PCI%20DSS/Standard/PCI-DSS-v4_0.pdf
|
||||
- **SLA Requirement**: Critical and high-severity vulnerabilities must be patched within 30 days of release; risk-ranked approach for all others
|
||||
|
||||
### SOC 2 Type II - CC7.1
|
||||
- **Title**: Detection and Monitoring of Security Events
|
||||
- **Relevance**: Requires evidence of vulnerability management program with defined remediation timelines and tracking
|
||||
|
||||
### ISO 27001:2022 - Control A.8.8
|
||||
- **Title**: Management of Technical Vulnerabilities
|
||||
- **Relevance**: Requires timely identification and remediation of technical vulnerabilities with defined response timelines
|
||||
|
||||
## Industry SLA Benchmarks
|
||||
|
||||
### SANS Vulnerability Management Maturity
|
||||
- **Critical**: 24-48 hours
|
||||
- **High**: 7-30 days
|
||||
- **Medium**: 30-90 days
|
||||
- **Low**: 90-180 days
|
||||
|
||||
### CIS Controls v8 - Control 7
|
||||
- **Title**: Continuous Vulnerability Management
|
||||
- **URL**: https://www.cisecurity.org/controls/continuous-vulnerability-management
|
||||
- **Implementation Group 1**: Remediate detected vulnerabilities monthly
|
||||
- **Implementation Group 2**: Automated remediation tracking with SLA enforcement
|
||||
- **Implementation Group 3**: Real-time SLA monitoring with automated escalation
|
||||
|
||||
## Integration APIs
|
||||
|
||||
### PagerDuty Events API v2
|
||||
- **URL**: https://developer.pagerduty.com/api-reference/a7d81b0e9200f-send-an-event-to-pager-duty
|
||||
- **Endpoint**: https://events.pagerduty.com/v2/enqueue
|
||||
|
||||
### Slack Incoming Webhooks
|
||||
- **URL**: https://api.slack.com/messaging/webhooks
|
||||
- **Rate Limit**: 1 message per second per webhook
|
||||
|
||||
### Microsoft Teams Incoming Webhook
|
||||
- **URL**: https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook
|
||||
|
||||
### Jira REST API
|
||||
- **URL**: https://developer.atlassian.com/cloud/jira/platform/rest/v3/
|
||||
- **Relevance**: Create and track remediation tickets with SLA metadata
|
||||
@@ -0,0 +1,72 @@
|
||||
# Workflows - Vulnerability SLA Breach Alerting
|
||||
|
||||
## Workflow 1: SLA Assignment on New Findings
|
||||
|
||||
### Trigger
|
||||
New vulnerability findings imported from scanner.
|
||||
|
||||
### Steps
|
||||
1. Parse incoming vulnerability data (CVE ID, CVSS score, affected asset)
|
||||
2. Look up asset criticality from CMDB to determine if SLA should be tightened
|
||||
3. Calculate SLA tier based on CVSS score and asset criticality
|
||||
4. Compute SLA deadline: `discovered_at + remediation_days`
|
||||
5. Insert SLA record into tracking database
|
||||
6. Assign finding owner based on asset ownership mapping
|
||||
7. Send initial notification to asset owner with SLA deadline
|
||||
|
||||
## Workflow 2: Hourly SLA Breach Check
|
||||
|
||||
### Trigger
|
||||
Cron job running every hour.
|
||||
|
||||
### Steps
|
||||
1. Query all open vulnerability SLA records
|
||||
2. For each record, calculate current SLA status:
|
||||
- **within_sla**: Less than 80% of SLA window elapsed
|
||||
- **approaching_breach**: 80-100% of SLA window elapsed
|
||||
- **breached**: Past SLA deadline
|
||||
3. For approaching_breach findings (first notification):
|
||||
- Send Slack/Teams warning to asset owner
|
||||
- Send email notification to asset owner and team lead
|
||||
4. For breached findings:
|
||||
- Send immediate Slack alert to security team channel
|
||||
- Trigger PagerDuty incident for critical/high severity
|
||||
- Send escalation email to management chain
|
||||
- Update escalation_level in database
|
||||
5. For post-breach findings (already breached, escalation increase):
|
||||
- Every 12 hours, increase escalation level
|
||||
- Level 1: Team lead notification
|
||||
- Level 2: Director notification
|
||||
- Level 3: VP/CISO notification
|
||||
|
||||
## Workflow 3: Remediation Confirmation
|
||||
|
||||
### Trigger
|
||||
Vulnerability scanner re-scan confirms finding resolved.
|
||||
|
||||
### Steps
|
||||
1. Match resolved finding to SLA record
|
||||
2. Record remediation timestamp
|
||||
3. Calculate if remediation was within SLA
|
||||
4. Update SLA record status to `remediated_within_sla` or `remediated_breach`
|
||||
5. Close any associated PagerDuty incidents
|
||||
6. Send confirmation notification to asset owner
|
||||
7. Update metrics dashboard
|
||||
|
||||
## Workflow 4: Monthly SLA Compliance Report
|
||||
|
||||
### Trigger
|
||||
First business day of each month.
|
||||
|
||||
### Steps
|
||||
1. Query all SLA records for the previous month
|
||||
2. Calculate metrics by severity tier:
|
||||
- Total findings per tier
|
||||
- SLA compliance rate per tier
|
||||
- Mean time to remediate per tier
|
||||
- Count of currently overdue findings
|
||||
3. Identify top 10 assets with most SLA breaches
|
||||
4. Identify teams with lowest compliance rates
|
||||
5. Generate HTML report with charts
|
||||
6. Email report to security leadership
|
||||
7. Update executive dashboard
|
||||
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Vulnerability SLA Breach Alerting System.
|
||||
|
||||
Tracks vulnerability remediation timelines, detects SLA breaches,
|
||||
and dispatches notifications through multiple channels.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import smtplib
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
|
||||
DB_PATH = os.environ.get("SLA_DB_PATH", "vulnerability_sla.db")
|
||||
|
||||
SLA_TIERS = {
|
||||
"critical": {"cvss_min": 9.0, "cvss_max": 10.0, "days": 2},
|
||||
"high": {"cvss_min": 7.0, "cvss_max": 8.9, "days": 15},
|
||||
"medium": {"cvss_min": 4.0, "cvss_max": 6.9, "days": 60},
|
||||
"low": {"cvss_min": 0.1, "cvss_max": 3.9, "days": 90},
|
||||
}
|
||||
|
||||
|
||||
def init_db(db_path=DB_PATH):
|
||||
"""Initialize SQLite database for SLA tracking."""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS vulnerability_sla (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
cve_id TEXT NOT NULL,
|
||||
finding_id TEXT NOT NULL,
|
||||
asset_hostname TEXT,
|
||||
severity TEXT NOT NULL,
|
||||
cvss_score REAL,
|
||||
discovered_at TEXT NOT NULL,
|
||||
sla_deadline TEXT NOT NULL,
|
||||
remediated_at TEXT,
|
||||
status TEXT DEFAULT 'open',
|
||||
owner_email TEXT,
|
||||
escalation_level INTEGER DEFAULT 0,
|
||||
last_alert_sent TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
|
||||
def get_severity_tier(cvss_score):
|
||||
"""Map CVSS score to severity tier."""
|
||||
for tier_name, tier in SLA_TIERS.items():
|
||||
if tier["cvss_min"] <= cvss_score <= tier["cvss_max"]:
|
||||
return tier_name, tier["days"]
|
||||
return "low", 90
|
||||
|
||||
|
||||
def import_findings(conn, csv_path):
|
||||
"""Import vulnerability findings from CSV and assign SLA deadlines."""
|
||||
imported = 0
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
cve_id = row.get("cve_id", "").strip()
|
||||
cvss = float(row.get("cvss_score", 0))
|
||||
if not cve_id or cvss == 0:
|
||||
continue
|
||||
discovered = row.get("discovered_at", datetime.now(timezone.utc).isoformat())
|
||||
discovered_dt = datetime.fromisoformat(discovered.replace("Z", "+00:00"))
|
||||
severity, sla_days = get_severity_tier(cvss)
|
||||
deadline = discovered_dt + timedelta(days=sla_days)
|
||||
|
||||
conn.execute(
|
||||
"""INSERT INTO vulnerability_sla
|
||||
(cve_id, finding_id, asset_hostname, severity, cvss_score,
|
||||
discovered_at, sla_deadline, owner_email, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'open')""",
|
||||
(
|
||||
cve_id,
|
||||
row.get("finding_id", f"{cve_id}_{row.get('host', 'unknown')}"),
|
||||
row.get("host", "unknown"),
|
||||
severity,
|
||||
cvss,
|
||||
discovered_dt.isoformat(),
|
||||
deadline.isoformat(),
|
||||
row.get("owner_email", ""),
|
||||
),
|
||||
)
|
||||
imported += 1
|
||||
conn.commit()
|
||||
print(f"[+] Imported {imported} findings with SLA deadlines")
|
||||
return imported
|
||||
|
||||
|
||||
def check_sla_breaches(conn):
|
||||
"""Check all open findings for SLA status and return categorized results."""
|
||||
now = datetime.now(timezone.utc)
|
||||
cursor = conn.execute(
|
||||
"SELECT * FROM vulnerability_sla WHERE status = 'open'"
|
||||
)
|
||||
columns = [d[0] for d in cursor.description]
|
||||
breached = []
|
||||
approaching = []
|
||||
within_sla = []
|
||||
|
||||
for row in cursor.fetchall():
|
||||
record = dict(zip(columns, row))
|
||||
deadline = datetime.fromisoformat(record["sla_deadline"])
|
||||
discovered = datetime.fromisoformat(record["discovered_at"])
|
||||
if deadline.tzinfo is None:
|
||||
deadline = deadline.replace(tzinfo=timezone.utc)
|
||||
if discovered.tzinfo is None:
|
||||
discovered = discovered.replace(tzinfo=timezone.utc)
|
||||
|
||||
if now > deadline:
|
||||
overdue_days = (now - deadline).days
|
||||
record["sla_status"] = f"breached_{overdue_days}d_overdue"
|
||||
record["overdue_days"] = overdue_days
|
||||
breached.append(record)
|
||||
else:
|
||||
total_window = (deadline - discovered).total_seconds()
|
||||
elapsed = (now - discovered).total_seconds()
|
||||
pct = (elapsed / total_window * 100) if total_window > 0 else 0
|
||||
if pct >= 80:
|
||||
record["sla_status"] = "approaching_breach"
|
||||
record["pct_elapsed"] = round(pct, 1)
|
||||
approaching.append(record)
|
||||
else:
|
||||
record["sla_status"] = "within_sla"
|
||||
record["pct_elapsed"] = round(pct, 1)
|
||||
within_sla.append(record)
|
||||
|
||||
return {"breached": breached, "approaching": approaching, "within_sla": within_sla}
|
||||
|
||||
|
||||
def send_slack_notification(webhook_url, findings, alert_type):
|
||||
"""Send SLA alert to Slack channel."""
|
||||
if not webhook_url or not findings:
|
||||
return
|
||||
color_map = {"breached": "#FF0000", "approaching": "#FFA500", "within_sla": "#36A64F"}
|
||||
for finding in findings[:10]:
|
||||
payload = {
|
||||
"attachments": [
|
||||
{
|
||||
"color": color_map.get(alert_type, "#808080"),
|
||||
"title": f"SLA {alert_type.upper()}: {finding['cve_id']}",
|
||||
"fields": [
|
||||
{"title": "Severity", "value": finding["severity"], "short": True},
|
||||
{"title": "CVSS", "value": str(finding["cvss_score"]), "short": True},
|
||||
{"title": "Asset", "value": finding["asset_hostname"], "short": True},
|
||||
{"title": "Deadline", "value": finding["sla_deadline"][:16], "short": True},
|
||||
{"title": "Owner", "value": finding.get("owner_email", "Unassigned"), "short": True},
|
||||
{"title": "Status", "value": finding["sla_status"], "short": True},
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
requests.post(webhook_url, json=payload, timeout=10)
|
||||
except requests.RequestException as e:
|
||||
print(f"[-] Slack notification failed: {e}")
|
||||
|
||||
|
||||
def send_email_notification(smtp_config, findings, alert_type):
|
||||
"""Send SLA alert via email."""
|
||||
if not smtp_config or not findings:
|
||||
return
|
||||
recipients = set()
|
||||
for f in findings:
|
||||
if f.get("owner_email"):
|
||||
recipients.add(f["owner_email"])
|
||||
if not recipients:
|
||||
return
|
||||
|
||||
body_lines = [f"Vulnerability SLA {alert_type.upper()} Report", "=" * 50, ""]
|
||||
for f in findings:
|
||||
body_lines.extend([
|
||||
f"CVE: {f['cve_id']}",
|
||||
f"Severity: {f['severity']} (CVSS {f['cvss_score']})",
|
||||
f"Asset: {f['asset_hostname']}",
|
||||
f"Deadline: {f['sla_deadline'][:16]}",
|
||||
f"Status: {f['sla_status']}",
|
||||
"-" * 40,
|
||||
])
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg["Subject"] = f"[VULN SLA {alert_type.upper()}] {len(findings)} findings require attention"
|
||||
msg["From"] = smtp_config.get("from_address", "vuln-alerts@company.com")
|
||||
msg["To"] = ", ".join(recipients)
|
||||
msg.attach(MIMEText("\n".join(body_lines), "plain"))
|
||||
|
||||
try:
|
||||
with smtplib.SMTP(smtp_config["host"], smtp_config.get("port", 587)) as server:
|
||||
server.starttls()
|
||||
if smtp_config.get("username"):
|
||||
server.login(smtp_config["username"], smtp_config["password"])
|
||||
server.send_message(msg)
|
||||
print(f"[+] Email sent to {len(recipients)} recipients")
|
||||
except Exception as e:
|
||||
print(f"[-] Email notification failed: {e}")
|
||||
|
||||
|
||||
def generate_compliance_report(conn, output_path, period_days=30):
|
||||
"""Generate SLA compliance report."""
|
||||
cutoff = (datetime.now(timezone.utc) - timedelta(days=period_days)).isoformat()
|
||||
cursor = conn.execute(
|
||||
"""SELECT severity,
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN remediated_at IS NOT NULL
|
||||
AND remediated_at <= sla_deadline THEN 1 ELSE 0 END) as within_sla,
|
||||
SUM(CASE WHEN remediated_at IS NOT NULL
|
||||
AND remediated_at > sla_deadline THEN 1 ELSE 0 END) as breached_remediated,
|
||||
SUM(CASE WHEN status = 'open'
|
||||
AND datetime('now') > sla_deadline THEN 1 ELSE 0 END) as currently_overdue
|
||||
FROM vulnerability_sla
|
||||
WHERE discovered_at >= ?
|
||||
GROUP BY severity
|
||||
ORDER BY CASE severity
|
||||
WHEN 'critical' THEN 1
|
||||
WHEN 'high' THEN 2
|
||||
WHEN 'medium' THEN 3
|
||||
WHEN 'low' THEN 4 END""",
|
||||
(cutoff,),
|
||||
)
|
||||
|
||||
report = {
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"period_days": period_days,
|
||||
"tiers": [],
|
||||
}
|
||||
total_findings = 0
|
||||
total_compliant = 0
|
||||
for row in cursor.fetchall():
|
||||
severity, total, within_sla, breached_remediated, currently_overdue = row
|
||||
compliance_rate = (within_sla / total * 100) if total > 0 else 0
|
||||
report["tiers"].append({
|
||||
"severity": severity,
|
||||
"total": total,
|
||||
"within_sla": within_sla,
|
||||
"breached_remediated": breached_remediated,
|
||||
"currently_overdue": currently_overdue,
|
||||
"compliance_rate": round(compliance_rate, 1),
|
||||
})
|
||||
total_findings += total
|
||||
total_compliant += within_sla
|
||||
|
||||
report["overall_compliance"] = round(
|
||||
(total_compliant / total_findings * 100) if total_findings > 0 else 0, 1
|
||||
)
|
||||
report["total_findings"] = total_findings
|
||||
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"[+] Compliance report written to {output_path}")
|
||||
print(f" Overall SLA Compliance: {report['overall_compliance']}%")
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Vulnerability SLA Breach Alerting System")
|
||||
parser.add_argument("--import-findings", help="Import findings from CSV")
|
||||
parser.add_argument("--check-sla", action="store_true", help="Check for SLA breaches")
|
||||
parser.add_argument("--report", action="store_true", help="Generate compliance report")
|
||||
parser.add_argument("--output", default="sla_compliance_report.json", help="Report output path")
|
||||
parser.add_argument("--period", type=int, default=30, help="Report period in days")
|
||||
parser.add_argument("--slack-webhook", help="Slack webhook URL for notifications")
|
||||
parser.add_argument("--db", default=DB_PATH, help="Database path")
|
||||
args = parser.parse_args()
|
||||
|
||||
conn = init_db(args.db)
|
||||
|
||||
if args.import_findings:
|
||||
import_findings(conn, args.import_findings)
|
||||
|
||||
if args.check_sla:
|
||||
results = check_sla_breaches(conn)
|
||||
print(f"\n[*] SLA Check Results:")
|
||||
print(f" Breached: {len(results['breached'])}")
|
||||
print(f" Approaching: {len(results['approaching'])}")
|
||||
print(f" Within SLA: {len(results['within_sla'])}")
|
||||
|
||||
if args.slack_webhook:
|
||||
if results["breached"]:
|
||||
send_slack_notification(args.slack_webhook, results["breached"], "breached")
|
||||
if results["approaching"]:
|
||||
send_slack_notification(args.slack_webhook, results["approaching"], "approaching")
|
||||
|
||||
if args.report:
|
||||
generate_compliance_report(conn, args.output, args.period)
|
||||
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user