feat: Phase 4 - MITRE ATT&CK sync and scheduled job (T-018, T-019)
- Add MITRE sync service via TAXII 2.0 with GitHub fallback - Upsert attack-pattern objects into techniques table (691 techniques) - Detect name/description changes and flag review_required on re-sync - Add APScheduler background job running every 24h - Add POST /system/sync-mitre endpoint (admin only) - Add GET /system/scheduler-status endpoint (admin only) - Configure logging for scheduler and sync visibility - Update README with new endpoints and project structure
This commit is contained in:
0
backend/app/jobs/__init__.py
Normal file
0
backend/app/jobs/__init__.py
Normal file
53
backend/app/jobs/mitre_sync_job.py
Normal file
53
backend/app/jobs/mitre_sync_job.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Scheduled job for periodic MITRE ATT&CK synchronisation.
|
||||
|
||||
Uses APScheduler's ``BackgroundScheduler`` to run :func:`sync_mitre` every
|
||||
24 hours. The job manages its own database session (created on entry,
|
||||
closed in ``finally``) so it is fully independent from FastAPI's
|
||||
request-scoped sessions.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
from app.database import SessionLocal
|
||||
from app.services.mitre_sync_service import sync_mitre
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Module-level scheduler instance
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
scheduler = BackgroundScheduler()
|
||||
|
||||
|
||||
def _run_mitre_sync() -> None:
|
||||
"""Execute a MITRE sync inside its own DB session."""
|
||||
logger.info("Scheduled MITRE sync job starting...")
|
||||
db = SessionLocal()
|
||||
try:
|
||||
summary = sync_mitre(db)
|
||||
logger.info("Scheduled MITRE sync job finished — %s", summary)
|
||||
except Exception:
|
||||
logger.exception("Scheduled MITRE sync job failed")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def start_scheduler() -> None:
|
||||
"""Register the MITRE sync job and start the background scheduler.
|
||||
|
||||
The job runs every **24 hours**. It does **not** fire immediately on
|
||||
startup — the first execution happens 24 h after the application boots.
|
||||
"""
|
||||
scheduler.add_job(
|
||||
_run_mitre_sync,
|
||||
trigger="interval",
|
||||
hours=24,
|
||||
id="mitre_sync",
|
||||
name="MITRE ATT&CK sync (every 24h)",
|
||||
replace_existing=True,
|
||||
)
|
||||
scheduler.start()
|
||||
logger.info("MITRE sync scheduler started (interval=24h)")
|
||||
Reference in New Issue
Block a user