Commit Graph
3 Commits
Author SHA1 Message Date
vulncheck c952a42bc2 fix(eol): three-tier severity + EOL-SOON; nessus Essentials import fallback
Two tester findings.

1. Windows Server 2016 wrongly flagged as EOL (HIGH) though it still
   gets monthly CUs until 2027-01. Cause: is_eoas (active-support
   ended 2022) was treated the same as is_eol → MEDIUM/critical-ish
   finding even while security patches still flow.

   New three-tier model in _build_eol_status():
     - is_eol      : security support ENDED (eolFrom past, no ESU) →
                     HIGH, cvss 9.0, title "EOL".
     - is_eol_soon : eolFrom within EOL_SOON_DAYS (90) but future →
                     MEDIUM, cvss 5.5, title "EOL SOON (Nd)".
     - is_eoas only: mainstream support ended, security patches still
                     flow → LOW, cvss 3.0, title "end-of-active-support".
   Server 2016 (eoas 2022, eol 2027-01) now → LOW today, flips to
   MEDIUM "EOL SOON" ~90d before Jan 2027, HIGH after.
   _days_until() handles eolFrom given as a bool (endoflife quirk).
   Both check_eol + check_os_eol share _build_eol_status now.

2. Nessus "Scan + Import" failed with "Server disconnected without
   sending a response" on launch. That's the Nessus Essentials (free)
   API-launch limitation — it drops the connection and the scan never
   enters 'running'. The job now catches the launch NessusAPIError: if
   the scan already has completed/imported results it imports those
   (flagged via job.launch_warning + a clear stage message); otherwise
   it fails with an actionable hint ("this edition may not allow
   API-triggered scans — launch in the Nessus UI then Sync now").

eol-check loops (router + scheduler) updated to also surface
is_eol_soon findings.
2026-06-01 08:19:19 +02:00
vulncheck c922c9ea49 feat(nessus): scan-and-import — launch scan, poll, auto-import (Plan F)
Tester referenced PacketFence's Nessus scan-engine integration:
trigger a scan FROM the tool instead of manually launching in
Nessus then clicking Sync. VulnCheck already had scan-host (launch
only) + sync (import only) — Plan F chains them server-side.

Client
- NessusClient.get_scan_status(scan_id) → lower-cased status string.

Job (override_jobs.py — reuses the in-memory tracker)
- start_nessus_scan_import_job(user_id, asset_id?, scan_id?):
  spawns a worker thread, returns job_id.
- worker: resolve scan_id (explicit → default_scan_ids[0] → first
  visible) → launch_scan (alt_targets = asset IP when asset_id set)
  → poll get_scan_status every 15s up to 1h → on completed/imported
  run run_nessus_sync(scan_ids=[id]). Terminal states
  completed/imported/canceled/aborted/empty; canceled/aborted abort
  the import. Timeout raises with the last seen status.

Endpoints
- POST /nessus/scan-and-import  {asset_id?, scan_id?} → {job_id}
- GET  /nessus/scan-and-import/status/{job_id} → live job state
  (stage / nessus_status / polls / result / error)

Frontend
- Settings → Tenable Nessus row gains a purple "Scan + Import"
  button next to "Sync now". Polls the status endpoint every 5s,
  surfaces the stage string inline, terminal state shows the
  created/merged/patched summary. User can navigate away — work is
  server-side.

Out of scope (later):
- Per-asset rescan button on the vuln detail / asset page wired to
  the same job (currently only the global Settings trigger).
- Scan-template picker UI (still uses default_scan_ids[0] / first
  visible scan).
2026-05-31 14:09:41 +02:00
vulncheck 5b18310cf8 feat(override): async job + status endpoint for vulnrichment correction
The synchronous /override/vulnrichment endpoint blocks the browser
request for the full duration of the correction — 23 minutes on the
tester's instance, well past every reasonable client timeout. Result:
'backend connection failed' modal and zero feedback about whether
the work actually ran.

New endpoints (sync one kept untouched for single-CVE backward compat):

  POST /override/vulnrichment/start
       Returns {job_id, status: 'queued', poll_url} immediately.
       Spawns a daemon thread that does the heavy lift.

  GET  /override/vulnrichment/status/{job_id}
       Returns the live snapshot: status, stage, total, done,
       updated/checked/not_found, error if any.

  GET  /override/vulnrichment/jobs
       Recent jobs ringbuffer for an admin overview.

State lives in an in-memory dict (override_jobs.py) — survives
requests, not backend restarts. That's fine; a restart would have
killed the worker thread anyway and the user re-clicks. Ringbuffer
caps at 50 entries, oldest finished gets evicted.

Worker owns its own SessionLocal() so it does not collide with the
trigger request's DB session. Stages narrate what's currently
happening ('downloading vulnrichment ZIP snapshot', 'fetching N
CVEs from github raw', etc.) so the frontend can show a meaningful
progress message even though we don't get per-CVE callbacks from
inside correct_vulnerability_scores.

Frontend modal that polls the status endpoint comes in the next commit.
2026-05-17 11:41:49 +02:00