""" Security Mock Data & Demo Endpoints Mock/demo data generators for the Security Dashboard. Used as fallback when no real scan reports are available. """ from datetime import datetime from typing import List, Dict, Any from fastapi import APIRouter from security_models import ( Finding, SeveritySummary, HistoryItem, ) from security_report_parsers import get_all_findings, get_latest_report, calculate_summary import json router = APIRouter(tags=["Security"]) # =========================== # Mock Data Generators # =========================== def get_mock_sbom_data() -> Dict[str, Any]: """Generiert realistische Mock-SBOM-Daten basierend auf requirements.txt.""" return { "bomFormat": "CycloneDX", "specVersion": "1.4", "version": 1, "metadata": { "timestamp": datetime.now().isoformat(), "tools": [{"vendor": "BreakPilot", "name": "DevSecOps", "version": "1.0.0"}], "component": { "type": "application", "name": "breakpilot-pwa", "version": "2.0.0" } }, "components": [ {"type": "library", "name": "fastapi", "version": "0.109.0", "purl": "pkg:pypi/fastapi@0.109.0", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "uvicorn", "version": "0.27.0", "purl": "pkg:pypi/uvicorn@0.27.0", "licenses": [{"license": {"id": "BSD-3-Clause"}}]}, {"type": "library", "name": "pydantic", "version": "2.5.3", "purl": "pkg:pypi/pydantic@2.5.3", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "httpx", "version": "0.26.0", "purl": "pkg:pypi/httpx@0.26.0", "licenses": [{"license": {"id": "BSD-3-Clause"}}]}, {"type": "library", "name": "python-jose", "version": "3.3.0", "purl": "pkg:pypi/python-jose@3.3.0", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "passlib", "version": "1.7.4", "purl": "pkg:pypi/passlib@1.7.4", "licenses": [{"license": {"id": "BSD-3-Clause"}}]}, {"type": "library", "name": "bcrypt", "version": "4.1.2", "purl": "pkg:pypi/bcrypt@4.1.2", "licenses": [{"license": {"id": "Apache-2.0"}}]}, {"type": "library", "name": "psycopg2-binary", "version": "2.9.9", "purl": "pkg:pypi/psycopg2-binary@2.9.9", "licenses": [{"license": {"id": "LGPL-3.0"}}]}, {"type": "library", "name": "sqlalchemy", "version": "2.0.25", "purl": "pkg:pypi/sqlalchemy@2.0.25", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "alembic", "version": "1.13.1", "purl": "pkg:pypi/alembic@1.13.1", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "weasyprint", "version": "60.2", "purl": "pkg:pypi/weasyprint@60.2", "licenses": [{"license": {"id": "BSD-3-Clause"}}]}, {"type": "library", "name": "jinja2", "version": "3.1.3", "purl": "pkg:pypi/jinja2@3.1.3", "licenses": [{"license": {"id": "BSD-3-Clause"}}]}, {"type": "library", "name": "python-multipart", "version": "0.0.6", "purl": "pkg:pypi/python-multipart@0.0.6", "licenses": [{"license": {"id": "Apache-2.0"}}]}, {"type": "library", "name": "aiofiles", "version": "23.2.1", "purl": "pkg:pypi/aiofiles@23.2.1", "licenses": [{"license": {"id": "Apache-2.0"}}]}, {"type": "library", "name": "pytest", "version": "7.4.4", "purl": "pkg:pypi/pytest@7.4.4", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "pytest-asyncio", "version": "0.23.3", "purl": "pkg:pypi/pytest-asyncio@0.23.3", "licenses": [{"license": {"id": "Apache-2.0"}}]}, {"type": "library", "name": "anthropic", "version": "0.18.1", "purl": "pkg:pypi/anthropic@0.18.1", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "openai", "version": "1.12.0", "purl": "pkg:pypi/openai@1.12.0", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "langchain", "version": "0.1.6", "purl": "pkg:pypi/langchain@0.1.6", "licenses": [{"license": {"id": "MIT"}}]}, {"type": "library", "name": "chromadb", "version": "0.4.22", "purl": "pkg:pypi/chromadb@0.4.22", "licenses": [{"license": {"id": "Apache-2.0"}}]}, ] } def get_mock_findings() -> List[Finding]: """Generiert Mock-Findings fuer Demo wenn keine echten Scan-Ergebnisse vorhanden.""" # Alle kritischen Findings wurden behoben: # - idna >= 3.7 gepinnt (CVE-2024-3651) # - cryptography >= 42.0.0 gepinnt (GHSA-h4gh-qq45-vh27) # - jinja2 3.1.6 installiert (CVE-2024-34064) # - .env.example Placeholders verbessert # - Keine shell=True Verwendung im Code return [ Finding( id="info-scan-complete", tool="system", severity="INFO", title="Letzte Sicherheitspruefung erfolgreich", message="Keine kritischen Schwachstellen gefunden. Naechster Scan: taeglich 03:00 Uhr.", file="", line=None, found_at=datetime.now().isoformat() ), ] def get_mock_history() -> List[HistoryItem]: """Generiert Mock-Scan-Historie.""" base_time = datetime.now() return [ HistoryItem( timestamp=(base_time).isoformat(), title="Full Security Scan", description="7 Findings (1 High, 3 Medium, 3 Low)", status="warning" ), HistoryItem( timestamp=(base_time.replace(hour=base_time.hour-2)).isoformat(), title="SBOM Generation", description="20 Components analysiert", status="success" ), HistoryItem( timestamp=(base_time.replace(hour=base_time.hour-4)).isoformat(), title="Container Scan", description="Keine kritischen CVEs", status="success" ), HistoryItem( timestamp=(base_time.replace(day=base_time.day-1)).isoformat(), title="Secrets Scan", description="1 Finding (API Key in .env.example)", status="warning" ), HistoryItem( timestamp=(base_time.replace(day=base_time.day-1, hour=10)).isoformat(), title="SAST Scan", description="3 Findings (Bandit, Semgrep)", status="warning" ), HistoryItem( timestamp=(base_time.replace(day=base_time.day-2)).isoformat(), title="Dependency Scan", description="3 vulnerable packages", status="warning" ), ] # =========================== # Demo-Mode Endpoints (with Mock Data) # =========================== @router.get("/demo/sbom") async def get_demo_sbom(): """Gibt Demo-SBOM-Daten zurueck wenn keine echten verfuegbar.""" # Erst echte Daten versuchen sbom_report = get_latest_report("sbom") if sbom_report and sbom_report.exists(): try: with open(sbom_report) as f: return json.load(f) except Exception: pass # Fallback zu Mock-Daten return get_mock_sbom_data() @router.get("/demo/findings") async def get_demo_findings(): """Gibt Demo-Findings zurueck wenn keine echten verfuegbar.""" # Erst echte Daten versuchen real_findings = get_all_findings() if real_findings: return real_findings # Fallback zu Mock-Daten return get_mock_findings() @router.get("/demo/summary") async def get_demo_summary(): """Gibt Demo-Summary zurueck.""" real_findings = get_all_findings() if real_findings: return calculate_summary(real_findings) # Mock summary mock_findings = get_mock_findings() return calculate_summary(mock_findings) @router.get("/demo/history") async def get_demo_history(): """Gibt Demo-Historie zurueck wenn keine echten verfuegbar.""" # Note: uses mock data directly instead of calling the main history endpoint return get_mock_history()