"""System-level endpoints (admin only). Provides manual triggers for background operations such as the MITRE ATT&CK synchronisation, intel scanning, Atomic Red Team import, and scheduler health introspection. """ # Import logging import logging # Import APIRouter, Depends, Request from fastapi from fastapi import APIRouter, Depends, Request # Import Session from sqlalchemy.orm from sqlalchemy.orm import Session # Import get_db from app.database from app.database import get_db # Import require_role from app.dependencies.auth from app.dependencies.auth import require_role # Import scheduler from app.jobs.mitre_sync_job from app.jobs.mitre_sync_job import scheduler # Import limiter from app.limiter from app.limiter import limiter # Import User from app.models.user from app.models.user import User # Import import_atomic_red_team from app.services.atomic_import_service from app.services.atomic_import_service import import_atomic_red_team # Import scan_intel from app.services.intel_service from app.services.intel_service import scan_intel # Import sync_mitre from app.services.mitre_sync_service from app.services.mitre_sync_service import sync_mitre # Assign logger = logging.getLogger(__name__) logger = logging.getLogger(__name__) # Assign router = APIRouter(prefix="/system", tags=["system"]) router = APIRouter(prefix="/system", tags=["system"]) # Apply the @router.post decorator @router.post("/sync-mitre") # Apply the @limiter.limit decorator @limiter.limit("2/hour") # Define function trigger_mitre_sync def trigger_mitre_sync( # Entry: request request: Request, # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> dict: """Manually trigger a MITRE ATT&CK synchronisation. **Requires** the ``admin`` role. Returns a JSON object with the sync summary including the count of new and updated techniques. """ # Assign summary = sync_mitre(db) summary = sync_mitre(db) # Return { return { # Literal argument value "message": "MITRE sync completed", # Literal argument value "new": summary["created"], # Literal argument value "updated": summary["updated"], } # Apply the @router.post decorator @router.post("/run-intel-scan") # Define function trigger_intel_scan def trigger_intel_scan( # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> dict: """Manually trigger a threat-intelligence scan. **Requires** the ``admin`` role. Returns a JSON object with the scan summary including the count of new intel items found. """ # Assign summary = scan_intel(db) summary = scan_intel(db) # Return { return { # Literal argument value "message": "Intel scan completed", # Literal argument value "new_items": summary["new_items"], } # Apply the @router.post decorator @router.post("/import-atomic-tests") # Apply the @limiter.limit decorator @limiter.limit("2/hour") # Define function trigger_atomic_import def trigger_atomic_import( # Entry: request request: Request, # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> dict: """Trigger an import of Atomic Red Team tests as TestTemplates. **Requires** the ``admin`` role. Downloads the Atomic Red Team repository ZIP from GitHub, parses the YAML files, and creates/updates TestTemplate records. Running this endpoint multiple times is idempotent — duplicates are skipped. Returns a JSON object with import statistics. """ # Attempt the following; catch errors below try: # Assign summary = import_atomic_red_team(db) summary = import_atomic_red_team(db) # Handle Exception except Exception as exc: # Log error: "Atomic Red Team import failed: %s", exc, exc_info logger.error("Atomic Red Team import failed: %s", exc, exc_info=True) # Return { return { # Literal argument value "message": "Import failed. Check server logs for details.", } # Return { return { # Literal argument value "message": "Import completed", # Literal argument value "imported": summary["created"], # Literal argument value "skipped": summary["skipped_existing"], # Literal argument value "total_parsed": summary["total_tests_parsed"], } # Apply the @router.get decorator @router.get("/scheduler-status") # Define function scheduler_status def scheduler_status( # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> dict: """Return the current state of the background scheduler. **Requires** the ``admin`` role. """ # Assign jobs = scheduler.get_jobs() jobs = scheduler.get_jobs() # Return { return { # Literal argument value "running": scheduler.running, # Literal argument value "jobs": [ { # Literal argument value "id": job.id, # Literal argument value "name": job.name, # Literal argument value "next_run_time": str(job.next_run_time) if job.next_run_time else None, } for job in jobs ], }