8f98bdd273
- ruff.toml: select E/W/F/I/N rules, line-length=120, drop legacy ignores - Auto-fix: sort 82 import blocks (isort), remove 29 unused imports, strip 6 trailing-whitespace blank lines in docstrings - main.py: move setup_logging and settings imports to top (E402) - errors.py: noqa N818 on DDD exception names (96 call sites, safe) - intel_service.py: noqa N817 for universal ET alias - atomic/elastic/sigma import services: move _MAX_UNCOMPRESSED_SIZE and _MAX_ENTRIES to module level (N806) - compliance_import_service.py: move SAMPLE_CONTROLS / CIS_CONTROLS to module level; wrap long description strings (N806 + E501) - snapshot_service.py: move STATUS_ORDER dict to module level (N806) - sigma_import_service.py: remove dead dedup_key expression (F841) - threat_actor_import_service.py: remove dead stix_to_actor expression (F841) - data_source.py, seed_demo.py, campaign_scheduler_service.py, lolbas_import_service.py: wrap lines exceeding 120 chars (E501) - d3fend_import_service.py: per-file E501 ignore (data file with long strings) All 439 unit tests pass. ruff check app/ → All checks passed!
119 lines
3.7 KiB
Python
119 lines
3.7 KiB
Python
"""Professional report generation endpoints — PDF, DOCX, HTML output."""
|
|
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, Query, Request
|
|
from fastapi.responses import FileResponse
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.dependencies.auth import get_current_user, require_any_role
|
|
from app.limiter import limiter
|
|
from app.models.user import User
|
|
from app.services import report_generation_service
|
|
|
|
router = APIRouter(prefix="/reports/generate", tags=["professional-reports"])
|
|
|
|
_MEDIA_TYPES = {
|
|
"pdf": "application/pdf",
|
|
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
"html": "text/html",
|
|
}
|
|
|
|
|
|
@router.get("/purple-campaign/{campaign_id}")
|
|
@limiter.limit("5/minute")
|
|
def generate_purple_report(
|
|
request: Request,
|
|
campaign_id: UUID,
|
|
format: str = Query("pdf", pattern="^(pdf|docx|html)$"),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
|
|
):
|
|
"""Generate a Purple Team campaign assessment report."""
|
|
filepath = report_generation_service.generate_purple_campaign_report(
|
|
db, str(campaign_id), output_format=format,
|
|
)
|
|
return FileResponse(
|
|
filepath,
|
|
media_type=_MEDIA_TYPES[format],
|
|
filename=f"purple_report.{format}",
|
|
)
|
|
|
|
|
|
@router.get("/coverage-summary")
|
|
@limiter.limit("5/minute")
|
|
def generate_coverage_report(
|
|
request: Request,
|
|
format: str = Query("pdf", pattern="^(pdf|docx|html)$"),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
|
|
):
|
|
"""Generate an organization-wide MITRE ATT&CK coverage report."""
|
|
filepath = report_generation_service.generate_coverage_report(
|
|
db, output_format=format,
|
|
)
|
|
return FileResponse(
|
|
filepath,
|
|
media_type=_MEDIA_TYPES[format],
|
|
filename=f"coverage_report.{format}",
|
|
)
|
|
|
|
|
|
@router.get("/executive-summary")
|
|
@limiter.limit("5/minute")
|
|
def generate_executive_report(
|
|
request: Request,
|
|
format: str = Query("pdf", pattern="^(pdf|docx|html)$"),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
|
|
):
|
|
"""Generate an executive security summary report."""
|
|
filepath = report_generation_service.generate_executive_summary(
|
|
db, output_format=format,
|
|
)
|
|
return FileResponse(
|
|
filepath,
|
|
media_type=_MEDIA_TYPES[format],
|
|
filename=f"executive_summary.{format}",
|
|
)
|
|
|
|
|
|
@router.get("/quarterly-summary")
|
|
@limiter.limit("5/minute")
|
|
def generate_quarterly_report(
|
|
request: Request,
|
|
format: str = Query("pdf", pattern="^(pdf|docx|html)$"),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(require_any_role("red_lead", "blue_lead", "viewer")),
|
|
):
|
|
"""Generate a quarterly security summary report."""
|
|
filepath = report_generation_service.generate_quarterly_summary(
|
|
db, output_format=format,
|
|
)
|
|
return FileResponse(
|
|
filepath,
|
|
media_type=_MEDIA_TYPES[format],
|
|
filename=f"quarterly_summary.{format}",
|
|
)
|
|
|
|
|
|
@router.get("/technique/{technique_id}")
|
|
@limiter.limit("5/minute")
|
|
def generate_technique_report(
|
|
request: Request,
|
|
technique_id: UUID,
|
|
format: str = Query("pdf", pattern="^(pdf|docx|html)$"),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""Generate a detailed report for one MITRE technique."""
|
|
filepath = report_generation_service.generate_technique_detail_report(
|
|
db, str(technique_id), output_format=format,
|
|
)
|
|
return FileResponse(
|
|
filepath,
|
|
media_type=_MEDIA_TYPES[format],
|
|
filename=f"technique_{technique_id}.{format}",
|
|
)
|