[split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
178
backend-core/security_mock_data.py
Normal file
178
backend-core/security_mock_data.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user