Claude 24f816b6a3
Consolidate 22 sibling repos into layered organism structure
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):

- brain/        LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
                dispatch, A51 channels, and the OSINT cluster
- knowledge/    LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
                MITRE ATT&CK data
- reference/    defensive threat-reference (C3, shhbruh doc) + AdaYaml parser

License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.

Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.

https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
2026-06-10 06:53:01 +00: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))