mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-19 22:19:39 +03:00
Remove backed-up duplicate skills to fix validation pipeline
This commit is contained in:
@@ -1,61 +1,264 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Vulnerability SLA breach alerting agent."""
|
||||
import argparse, json
|
||||
from datetime import datetime, timezone
|
||||
"""Vulnerability SLA breach alerting agent.
|
||||
|
||||
Monitors vulnerability remediation timelines and generates alerts when
|
||||
SLA breaches occur or are imminent. Supports webhook notifications
|
||||
(Slack, Teams, PagerDuty), email alerts, and escalation workflows.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
def audit_config(target, token):
|
||||
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)})
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
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
|
||||
return findings
|
||||
DEFAULT_SLA_DAYS = {"CRITICAL": 7, "HIGH": 30, "MEDIUM": 90, "LOW": 180}
|
||||
|
||||
|
||||
def load_vulnerabilities(source_path):
|
||||
"""Load vulnerability data from JSON file."""
|
||||
with open(source_path, "r") as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, list):
|
||||
return data
|
||||
return data.get("vulnerabilities", data.get("findings", []))
|
||||
|
||||
|
||||
def check_sla_breaches(vulns, sla_days=None, warn_days_before=7):
|
||||
"""Check for SLA breaches and upcoming deadlines."""
|
||||
if sla_days is None:
|
||||
sla_days = DEFAULT_SLA_DAYS
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
breaches = []
|
||||
warnings = []
|
||||
|
||||
for vuln in vulns:
|
||||
status = (vuln.get("status") or vuln.get("state") or "open").lower()
|
||||
if status not in ("open", "new", "active", "unresolved"):
|
||||
continue
|
||||
|
||||
severity = (vuln.get("severity") or "MEDIUM").upper()
|
||||
target_days = sla_days.get(severity, 90)
|
||||
|
||||
disc_str = (vuln.get("discovered_date") or vuln.get("first_found") or
|
||||
vuln.get("discovered") or "")
|
||||
try:
|
||||
if "T" in disc_str:
|
||||
discovered = datetime.fromisoformat(disc_str.replace("Z", "+00:00"))
|
||||
elif disc_str:
|
||||
discovered = datetime.strptime(disc_str[:10], "%Y-%m-%d").replace(tzinfo=timezone.utc)
|
||||
else:
|
||||
continue
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
deadline = discovered + timedelta(days=target_days)
|
||||
days_remaining = (deadline - now).days
|
||||
|
||||
vuln_id = vuln.get("id") or vuln.get("cve_id") or vuln.get("vulnerability_id") or "unknown"
|
||||
asset = vuln.get("asset") or vuln.get("host") or vuln.get("ip") or "unknown"
|
||||
title = vuln.get("title") or vuln.get("name") or "Unknown"
|
||||
|
||||
alert_entry = {
|
||||
"id": vuln_id,
|
||||
"severity": severity,
|
||||
"asset": asset,
|
||||
"title": title[:80],
|
||||
"discovered": disc_str[:10],
|
||||
"deadline": deadline.isoformat()[:10],
|
||||
"days_remaining": days_remaining,
|
||||
"sla_target_days": target_days,
|
||||
}
|
||||
|
||||
if days_remaining < 0:
|
||||
alert_entry["alert_type"] = "BREACH"
|
||||
alert_entry["overdue_days"] = abs(days_remaining)
|
||||
breaches.append(alert_entry)
|
||||
elif days_remaining <= warn_days_before:
|
||||
alert_entry["alert_type"] = "WARNING"
|
||||
warnings.append(alert_entry)
|
||||
|
||||
breaches.sort(key=lambda x: -x.get("overdue_days", 0))
|
||||
warnings.sort(key=lambda x: x.get("days_remaining", 999))
|
||||
return breaches, warnings
|
||||
|
||||
|
||||
def send_slack_alert(webhook_url, breaches, warnings):
|
||||
"""Send SLA breach alert to Slack via webhook."""
|
||||
if not requests:
|
||||
print("[!] requests library required for Slack alerts", file=sys.stderr)
|
||||
return False
|
||||
|
||||
blocks = [
|
||||
{"type": "header", "text": {"type": "plain_text",
|
||||
"text": f"Vulnerability SLA Alert - {len(breaches)} Breaches, {len(warnings)} Warnings"}},
|
||||
]
|
||||
|
||||
if breaches:
|
||||
breach_text = "*SLA BREACHES (Immediate Action Required):*\n"
|
||||
for b in breaches[:10]:
|
||||
breach_text += (f"- [{b['severity']}] `{b['id']}` on {b['asset']} - "
|
||||
f"*{b['overdue_days']}d overdue*\n")
|
||||
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": breach_text}})
|
||||
|
||||
if warnings:
|
||||
warn_text = "*Approaching SLA Deadline:*\n"
|
||||
for w in warnings[:10]:
|
||||
warn_text += (f"- [{w['severity']}] `{w['id']}` on {w['asset']} - "
|
||||
f"{w['days_remaining']}d remaining\n")
|
||||
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": warn_text}})
|
||||
|
||||
payload = {"blocks": blocks}
|
||||
resp = requests.post(webhook_url, json=payload, timeout=15)
|
||||
if resp.status_code == 200:
|
||||
print(f"[+] Slack alert sent successfully")
|
||||
return True
|
||||
else:
|
||||
print(f"[!] Slack alert failed: {resp.status_code}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def send_teams_alert(webhook_url, breaches, warnings):
|
||||
"""Send SLA breach alert to Microsoft Teams via webhook."""
|
||||
if not requests:
|
||||
return False
|
||||
|
||||
facts = []
|
||||
for b in breaches[:5]:
|
||||
facts.append({"name": f"[BREACH] {b['id']}", "value": f"{b['severity']} - {b['overdue_days']}d overdue on {b['asset']}"})
|
||||
for w in warnings[:5]:
|
||||
facts.append({"name": f"[WARNING] {w['id']}", "value": f"{w['severity']} - {w['days_remaining']}d left on {w['asset']}"})
|
||||
|
||||
payload = {
|
||||
"@type": "MessageCard",
|
||||
"themeColor": "FF0000" if breaches else "FFA500",
|
||||
"summary": f"SLA Alert: {len(breaches)} breaches, {len(warnings)} warnings",
|
||||
"sections": [{
|
||||
"activityTitle": "Vulnerability SLA Alert",
|
||||
"facts": facts,
|
||||
}],
|
||||
}
|
||||
resp = requests.post(webhook_url, json=payload, timeout=15)
|
||||
return resp.status_code == 200
|
||||
|
||||
|
||||
def send_pagerduty_alert(routing_key, breaches):
|
||||
"""Send PagerDuty incident for critical SLA breaches."""
|
||||
if not requests or not breaches:
|
||||
return False
|
||||
|
||||
critical_breaches = [b for b in breaches if b["severity"] == "CRITICAL"]
|
||||
if not critical_breaches:
|
||||
return False
|
||||
|
||||
payload = {
|
||||
"routing_key": routing_key,
|
||||
"event_action": "trigger",
|
||||
"payload": {
|
||||
"summary": f"{len(critical_breaches)} CRITICAL vulnerability SLA breaches",
|
||||
"severity": "critical",
|
||||
"source": "vulnerability-sla-agent",
|
||||
"custom_details": {
|
||||
"breaches": critical_breaches[:5],
|
||||
"total_critical_breaches": len(critical_breaches),
|
||||
},
|
||||
},
|
||||
}
|
||||
resp = requests.post(
|
||||
"https://events.pagerduty.com/v2/enqueue",
|
||||
json=payload, timeout=15,
|
||||
)
|
||||
if resp.status_code == 202:
|
||||
print(f"[+] PagerDuty incident created")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def format_summary(breaches, warnings):
|
||||
"""Print alert summary."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Vulnerability SLA Breach Alert Report")
|
||||
print(f"{'='*60}")
|
||||
print(f" SLA Breaches : {len(breaches)}")
|
||||
print(f" SLA Warnings : {len(warnings)}")
|
||||
|
||||
if breaches:
|
||||
critical = sum(1 for b in breaches if b["severity"] == "CRITICAL")
|
||||
high = sum(1 for b in breaches if b["severity"] == "HIGH")
|
||||
print(f" Critical breaches: {critical}")
|
||||
print(f" High breaches : {high}")
|
||||
|
||||
print(f"\n Breached Vulnerabilities:")
|
||||
for b in breaches[:15]:
|
||||
print(f" [{b['severity']:8s}] {b['id']:20s} | {b['asset']:20s} | "
|
||||
f"{b['overdue_days']}d overdue (deadline: {b['deadline']})")
|
||||
|
||||
if warnings:
|
||||
print(f"\n Approaching Deadline:")
|
||||
for w in warnings[:10]:
|
||||
print(f" [{w['severity']:8s}] {w['id']:20s} | {w['asset']:20s} | "
|
||||
f"{w['days_remaining']}d remaining")
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Vulnerability SLA breach alerting agent")
|
||||
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("[*] Vulnerability SLA breach alerting agent")
|
||||
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="Vulnerability SLA breach alerting agent")
|
||||
parser.add_argument("--source", required=True, help="Vulnerability data JSON file")
|
||||
parser.add_argument("--sla-critical", type=int, default=7)
|
||||
parser.add_argument("--sla-high", type=int, default=30)
|
||||
parser.add_argument("--sla-medium", type=int, default=90)
|
||||
parser.add_argument("--sla-low", type=int, default=180)
|
||||
parser.add_argument("--warn-days", type=int, default=7, help="Warn N days before deadline")
|
||||
parser.add_argument("--slack-webhook", help="Slack webhook URL for alerts")
|
||||
parser.add_argument("--teams-webhook", help="Teams webhook URL for alerts")
|
||||
parser.add_argument("--pagerduty-key", help="PagerDuty routing key for critical breaches")
|
||||
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")
|
||||
|
||||
breaches, warnings = check_sla_breaches(vulns, sla_days, args.warn_days)
|
||||
format_summary(breaches, warnings)
|
||||
|
||||
alerts_sent = []
|
||||
if args.slack_webhook and (breaches or warnings):
|
||||
if send_slack_alert(args.slack_webhook, breaches, warnings):
|
||||
alerts_sent.append("slack")
|
||||
if args.teams_webhook and (breaches or warnings):
|
||||
if send_teams_alert(args.teams_webhook, breaches, warnings):
|
||||
alerts_sent.append("teams")
|
||||
if args.pagerduty_key and breaches:
|
||||
if send_pagerduty_alert(args.pagerduty_key, breaches):
|
||||
alerts_sent.append("pagerduty")
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "SLA Breach Alerting",
|
||||
"sla_targets": sla_days,
|
||||
"breaches": breaches,
|
||||
"warnings": warnings,
|
||||
"alerts_sent": alerts_sent,
|
||||
}
|
||||
|
||||
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