Files
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

181 lines
6.7 KiB
Python

#!/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 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))