mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-01 16:47:42 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Vulnerability dashboard builder using DefectDojo API.
|
||||
|
||||
Queries DefectDojo REST API v2 for findings, products, and engagements
|
||||
to build vulnerability management dashboards and metrics.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import os
|
||||
import collections
|
||||
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
|
||||
class DefectDojoClient:
|
||||
"""Client for DefectDojo REST API v2."""
|
||||
|
||||
def __init__(self, url=None, api_key=None):
|
||||
self.url = (url or os.environ.get("DEFECTDOJO_URL", "http://localhost:8080")).rstrip("/")
|
||||
self.api_key = api_key or os.environ.get("DEFECTDOJO_API_KEY", "")
|
||||
self.headers = {
|
||||
"Authorization": "Token " + self.api_key,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
def _get(self, endpoint, params=None):
|
||||
if not HAS_REQUESTS or not self.api_key:
|
||||
return {"error": "requests not available or no API key"}
|
||||
try:
|
||||
resp = requests.get(
|
||||
self.url + "/api/v2/" + endpoint,
|
||||
headers=self.headers, params=params, timeout=15
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
return {"error": "HTTP {}".format(resp.status_code)}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def get_findings(self, severity=None, active=True, limit=100):
|
||||
params = {"active": active, "limit": limit}
|
||||
if severity:
|
||||
params["severity"] = severity
|
||||
return self._get("findings/", params)
|
||||
|
||||
def get_products(self, limit=100):
|
||||
return self._get("products/", {"limit": limit})
|
||||
|
||||
def get_engagements(self, product_id=None, limit=100):
|
||||
params = {"limit": limit}
|
||||
if product_id:
|
||||
params["product"] = product_id
|
||||
return self._get("engagements/", params)
|
||||
|
||||
def get_finding_count_by_severity(self):
|
||||
result = {}
|
||||
for sev in ["Critical", "High", "Medium", "Low", "Info"]:
|
||||
data = self._get("findings/", {"severity": sev, "active": True, "limit": 1})
|
||||
if isinstance(data, dict) and "count" in data:
|
||||
result[sev] = data["count"]
|
||||
return result
|
||||
|
||||
def import_scan(self, product_id, engagement_id, scan_type, file_path):
|
||||
if not HAS_REQUESTS or not self.api_key:
|
||||
return {"error": "requests not available or no API key"}
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
resp = requests.post(
|
||||
self.url + "/api/v2/import-scan/",
|
||||
headers={"Authorization": "Token " + self.api_key},
|
||||
data={
|
||||
"product": product_id,
|
||||
"engagement": engagement_id,
|
||||
"scan_type": scan_type,
|
||||
"active": True,
|
||||
"verified": False,
|
||||
},
|
||||
files={"file": f},
|
||||
timeout=60,
|
||||
)
|
||||
if resp.status_code in (200, 201):
|
||||
return resp.json()
|
||||
return {"error": "HTTP {}".format(resp.status_code)}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def build_dashboard_data(findings):
|
||||
"""Build dashboard metrics from findings list."""
|
||||
if not isinstance(findings, dict) or "results" not in findings:
|
||||
return {"error": "Invalid findings data"}
|
||||
|
||||
results = findings["results"]
|
||||
severity_counts = collections.Counter()
|
||||
product_counts = collections.Counter()
|
||||
age_sum = 0
|
||||
overdue_count = 0
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
for f in results:
|
||||
severity_counts[f.get("severity", "Unknown")] += 1
|
||||
product_counts[f.get("test", {}).get("engagement", {}).get("product", {}).get("name", "Unknown")] += 1
|
||||
if f.get("date"):
|
||||
try:
|
||||
created = datetime.datetime.fromisoformat(f["date"])
|
||||
if created.tzinfo is None:
|
||||
created = created.replace(tzinfo=datetime.timezone.utc)
|
||||
age = (now - created).days
|
||||
age_sum += age
|
||||
sla = {"Critical": 7, "High": 30, "Medium": 90, "Low": 180}.get(f.get("severity", ""), 999)
|
||||
if age > sla:
|
||||
overdue_count += 1
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
total = len(results)
|
||||
return {
|
||||
"total_active_findings": total,
|
||||
"by_severity": dict(severity_counts),
|
||||
"by_product": dict(product_counts.most_common(10)),
|
||||
"avg_age_days": round(age_sum / max(total, 1), 1),
|
||||
"overdue_count": overdue_count,
|
||||
"sla_compliance_pct": round((total - overdue_count) / max(total, 1) * 100, 1),
|
||||
}
|
||||
|
||||
|
||||
SUPPORTED_SCAN_TYPES = [
|
||||
"Nessus Scan", "Qualys Scan", "Burp REST API",
|
||||
"ZAP Scan", "Trivy Scan", "Snyk Scan",
|
||||
"Semgrep JSON Report", "SARIF", "Generic Findings Import",
|
||||
"Anchore Grype", "Nuclei Scan", "Checkov Scan",
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Vulnerability Dashboard with DefectDojo")
|
||||
print("REST API v2 queries, severity metrics, SLA tracking")
|
||||
print("=" * 60)
|
||||
print(" requests available: {}".format(HAS_REQUESTS))
|
||||
|
||||
client = DefectDojoClient()
|
||||
|
||||
print("\n--- Supported Scan Types ---")
|
||||
for st in SUPPORTED_SCAN_TYPES:
|
||||
print(" - {}".format(st))
|
||||
|
||||
print("\n--- API Endpoints ---")
|
||||
endpoints = [
|
||||
("GET", "/api/v2/findings/", "List findings"),
|
||||
("GET", "/api/v2/products/", "List products"),
|
||||
("GET", "/api/v2/engagements/", "List engagements"),
|
||||
("POST", "/api/v2/import-scan/", "Import scan results"),
|
||||
("POST", "/api/v2/reimport-scan/", "Re-import scan results"),
|
||||
]
|
||||
for method, path, desc in endpoints:
|
||||
print(" {} {:30s} {}".format(method, path, desc))
|
||||
|
||||
demo_findings = {
|
||||
"count": 5,
|
||||
"results": [
|
||||
{"severity": "Critical", "title": "SQL Injection", "date": "2025-01-10", "test": {"engagement": {"product": {"name": "WebApp"}}}},
|
||||
{"severity": "High", "title": "XSS", "date": "2025-01-15", "test": {"engagement": {"product": {"name": "WebApp"}}}},
|
||||
{"severity": "Medium", "title": "Missing Headers", "date": "2024-12-01", "test": {"engagement": {"product": {"name": "API"}}}},
|
||||
{"severity": "Low", "title": "Cookie flag", "date": "2025-02-01", "test": {"engagement": {"product": {"name": "API"}}}},
|
||||
{"severity": "Critical", "title": "RCE", "date": "2025-02-20", "test": {"engagement": {"product": {"name": "WebApp"}}}},
|
||||
],
|
||||
}
|
||||
|
||||
dashboard = build_dashboard_data(demo_findings)
|
||||
print("\n--- Dashboard ---")
|
||||
for k, v in dashboard.items():
|
||||
print(" {}: {}".format(k, v))
|
||||
|
||||
print("\n" + json.dumps({"findings_analyzed": demo_findings["count"]}, indent=2))
|
||||
Reference in New Issue
Block a user