fix(scores,reports): fix scores/history 500, disable unfinished report generation cleanly
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

- /scores/history was annotated -> dict but the service actually returns
  a list, so FastAPI's response validation raised an unhandled exception,
  surfacing as a raw 500 on every call. Fixed the annotation.
- The professional_reports.py endpoints (PDF/DOCX/HTML generation) back
  a Reports feature the frontend already hides entirely (unfinished).
  Hitting them directly fell through to whatever the render pipeline
  raised (e.g. missing weasyprint system deps) as an unhelpful 500.
  Added a REPORTS_ENABLED setting (off by default) so they now return a
  clean 503 instead, without removing the routes or their test coverage.
This commit is contained in:
kitos
2026-07-24 15:24:06 +02:00
parent 07403cbd9d
commit e951567ba1
5 changed files with 59 additions and 4 deletions
@@ -40,6 +40,18 @@ def _assert_safe_report_path(filepath: str) -> str:
raise HTTPException(status_code=500, detail="Report generation path error")
return filepath
def _assert_reports_enabled() -> None:
"""Raise a clean 503 while report generation is unfinished/disabled.
The frontend hides the Reports UI entirely; without this guard, hitting
these endpoints directly falls through to whatever the underlying
render pipeline raises (e.g. missing weasyprint system deps) as a raw,
unhelpful 500.
"""
if not settings.REPORTS_ENABLED:
raise HTTPException(status_code=503, detail="Report generation is not yet available on this platform")
# Assign router = APIRouter(prefix="/reports/generate", tags=["professional-reports"])
router = APIRouter(prefix="/reports/generate", tags=["professional-reports"])
@@ -72,6 +84,7 @@ def generate_purple_report(
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
) -> FileResponse:
"""Generate a Purple Team campaign assessment report."""
_assert_reports_enabled()
# Assign filepath = report_generation_service.generate_purple_campaign_report(
filepath = report_generation_service.generate_purple_campaign_report(
db, str(campaign_id), output_format=format,
@@ -102,6 +115,7 @@ def generate_coverage_report(
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
) -> FileResponse:
"""Generate an organization-wide MITRE ATT&CK coverage report."""
_assert_reports_enabled()
# Assign filepath = report_generation_service.generate_coverage_report(
filepath = report_generation_service.generate_coverage_report(
db, output_format=format,
@@ -132,6 +146,7 @@ def generate_executive_report(
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
) -> FileResponse:
"""Generate an executive security summary report."""
_assert_reports_enabled()
# Assign filepath = report_generation_service.generate_executive_summary(
filepath = report_generation_service.generate_executive_summary(
db, output_format=format,
@@ -162,6 +177,7 @@ def generate_quarterly_report(
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
) -> FileResponse:
"""Generate a quarterly security summary report."""
_assert_reports_enabled()
# Assign filepath = report_generation_service.generate_quarterly_summary(
filepath = report_generation_service.generate_quarterly_summary(
db, output_format=format,
@@ -194,6 +210,7 @@ def generate_technique_report(
user: User = Depends(get_current_user),
) -> FileResponse:
"""Generate a detailed report for one MITRE technique."""
_assert_reports_enabled()
# Assign filepath = report_generation_service.generate_technique_detail_report(
filepath = report_generation_service.generate_technique_detail_report(
db, str(technique_id), output_format=format,