feat(phase-38): automatic intelligence — OSINT enrichment + stale coverage detection
Tarea 4.1 — OSINT Enrichment:
- Add OsintItem model with source_type, severity, CVSS metadata, review flag
- Add Alembic migration b022 with osint_items table and optimized indexes
- Add osint_enrichment_service with NVD API integration, deduplication, rate limiting
- Add OSINT router: GET /osint/items, /osint/summary, /osint/technique/{id}
- Add POST /osint/items/{id}/review to mark items as reviewed
- Add POST /osint/enrich/{technique_id} for manual single-technique enrichment
- Techniques with new CVEs are automatically flagged review_required=True
- Register weekly enrichment job in APScheduler
- Add NVD_API_KEY config setting for optional increased rate limits
Tarea 4.2 — Stale Coverage Detection:
- Add stale_detection_service that flags techniques with no validated test
in the last N days, or never-validated but with a coverage status
- Configurable threshold via STALE_THRESHOLD_DAYS setting (default 365)
- Register daily stale detection job in APScheduler
- Only flags techniques not already marked review_required
This commit is contained in:
@@ -21,6 +21,8 @@ from app.services.notification_service import cleanup_old_notifications
|
||||
from app.services.snapshot_service import create_snapshot, cleanup_old_snapshots
|
||||
from app.services.campaign_scheduler_service import check_and_run_recurring_campaigns
|
||||
from app.jobs.jira_sync_job import sync_all_jira_links
|
||||
from app.services.osint_enrichment_service import enrich_all_techniques
|
||||
from app.services.stale_detection_service import detect_stale_coverage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -108,6 +110,32 @@ def _run_intel_scan() -> None:
|
||||
db.close()
|
||||
|
||||
|
||||
def _run_osint_enrichment() -> None:
|
||||
"""Execute weekly OSINT enrichment inside its own DB session."""
|
||||
logger.info("Scheduled OSINT enrichment job starting...")
|
||||
db = SessionLocal()
|
||||
try:
|
||||
total = enrich_all_techniques(db)
|
||||
logger.info("OSINT enrichment finished — %d new items", total)
|
||||
except Exception:
|
||||
logger.exception("OSINT enrichment job failed")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def _run_stale_detection() -> None:
|
||||
"""Execute daily stale coverage detection inside its own DB session."""
|
||||
logger.info("Scheduled stale coverage detection starting...")
|
||||
db = SessionLocal()
|
||||
try:
|
||||
count = detect_stale_coverage(db)
|
||||
logger.info("Stale detection finished — %d techniques flagged", count)
|
||||
except Exception:
|
||||
logger.exception("Stale coverage detection job failed")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scheduler bootstrap
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -173,9 +201,26 @@ def start_scheduler() -> None:
|
||||
name="Jira link sync (hourly)",
|
||||
replace_existing=True,
|
||||
)
|
||||
scheduler.add_job(
|
||||
_run_osint_enrichment,
|
||||
trigger="interval",
|
||||
weeks=1,
|
||||
id="osint_enrichment",
|
||||
name="OSINT enrichment (weekly)",
|
||||
replace_existing=True,
|
||||
)
|
||||
scheduler.add_job(
|
||||
_run_stale_detection,
|
||||
trigger="interval",
|
||||
hours=24,
|
||||
id="stale_detection",
|
||||
name="Stale coverage detection (daily)",
|
||||
replace_existing=True,
|
||||
)
|
||||
scheduler.start()
|
||||
logger.info(
|
||||
"Background scheduler started — mitre_sync (24h), intel_scan (7d), "
|
||||
"notification_cleanup (24h), weekly_snapshot (Sundays 00:00), "
|
||||
"recurring_campaigns (daily), jira_sync (1h)"
|
||||
"recurring_campaigns (daily), jira_sync (1h), "
|
||||
"osint_enrichment (weekly), stale_detection (daily)"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user