feat(scans): nessus sync now writes per-host scan history rows
Tester noticed that scheduled + manual Nessus syncs left no trace in
the Scan-Jobs page — only Wazuh autoscan was visible there. Made
it impossible to verify Nessus scheduler runs after the fact.
Changes:
- New enum value ScanType.NESSUS + idempotent migration 014.
- run_nessus_sync (services/nessus_sync.py) now opens a Scan row
per host at the top of the per-host loop:
scan_type = NESSUS
status = RUNNING → COMPLETED / FAILED
started_at = now
asset_id = matched VulnCheck asset
and closes it after the backfill, setting
vulnerabilities_found = len(seen_cves_for_asset)
completed_at = now
The skip-backfill defensive branch (host returned 0 findings)
marks the row COMPLETED with an explanatory error_message so the
operator sees the run happened but produced nothing.
- Scheduler path inherits this for free — app/scheduler.py already
calls run_nessus_sync for scanner_type='nessus' schedules.
- Existing /scans/summary endpoint groups consecutive Scan rows of
the same type into a "run", so the UI shows one entry per Nessus
sync (covering all hosts).
Migration required on deploy:
docker compose exec backend alembic upgrade head # 013 → 014
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
"""Add NESSUS value to scans.scan_type enum
|
||||
|
||||
Revision ID: 014
|
||||
Revises: 013
|
||||
Create Date: 2026-05-18 10:00:00.000000
|
||||
|
||||
run_nessus_sync now writes one Scan row per host so Nessus syncs
|
||||
appear in the Scan-Jobs history (previously only Wazuh did). Needs
|
||||
a new enum value 'nessus' on scans.scan_type.
|
||||
|
||||
Idempotent — the ADD VALUE IF NOT EXISTS guard means re-running this
|
||||
migration on a schema where the value already lives is a no-op.
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision = '014'
|
||||
down_revision = '013'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Postgres enum ALTERs must run outside a transaction in older
|
||||
# versions. ADD VALUE IF NOT EXISTS is supported on 9.6+ and is
|
||||
# transaction-safe on 12+ which is our minimum target.
|
||||
op.execute("ALTER TYPE scantype ADD VALUE IF NOT EXISTS 'nessus'")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Postgres has no DROP VALUE for enums. The downgrade is a no-op;
|
||||
# an existing 'nessus' value is harmless even after the model
|
||||
# stops emitting it.
|
||||
pass
|
||||
@@ -12,6 +12,7 @@ class ScanType(str, Enum):
|
||||
FULL = "full"
|
||||
SYSCOLLECTOR = "syscollector"
|
||||
MANUAL = "manual"
|
||||
NESSUS = "nessus"
|
||||
|
||||
|
||||
class ScanStatus(str, Enum):
|
||||
|
||||
@@ -30,6 +30,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.integrations.nessus_client import NessusClient
|
||||
from app.models.asset import Asset, AssetSource, AssetStatus
|
||||
from app.models.scan import Scan, ScanStatus, ScanType
|
||||
from app.models.setting import Setting
|
||||
from app.models.vulnerability import (
|
||||
Vulnerability,
|
||||
@@ -231,6 +232,19 @@ def run_nessus_sync(
|
||||
continue
|
||||
stats["hosts_synced"] += 1
|
||||
|
||||
# Record one Scan row per host so Nessus runs appear in
|
||||
# the Scan-Jobs history (previously only Wazuh autoscan
|
||||
# populated this table). Status updated to COMPLETED /
|
||||
# FAILED at the end of this host's loop iteration.
|
||||
scan_row = Scan(
|
||||
asset_id=asset.id,
|
||||
scan_type=ScanType.NESSUS,
|
||||
status=ScanStatus.RUNNING,
|
||||
started_at=datetime.now(),
|
||||
)
|
||||
db.add(scan_row)
|
||||
host_vulns_found = 0
|
||||
|
||||
# Track cves we observe on THIS asset in THIS sync run.
|
||||
# Used for backfill (drop nessus source on disappeared findings).
|
||||
seen_cves_for_asset: set = set()
|
||||
@@ -469,6 +483,10 @@ def run_nessus_sync(
|
||||
asset.hostname, scan_id,
|
||||
)
|
||||
asset.last_scan = datetime.now()
|
||||
scan_row.status = ScanStatus.COMPLETED
|
||||
scan_row.completed_at = datetime.now()
|
||||
scan_row.vulnerabilities_found = 0
|
||||
scan_row.error_message = "Host returned 0 findings (skipped backfill)"
|
||||
continue
|
||||
stale_nessus_vulns = (
|
||||
db.query(Vulnerability)
|
||||
@@ -489,6 +507,10 @@ def run_nessus_sync(
|
||||
stats["vulns_marked_patched"] += 1
|
||||
|
||||
asset.last_scan = datetime.now()
|
||||
# Close out the per-host scan row.
|
||||
scan_row.status = ScanStatus.COMPLETED
|
||||
scan_row.completed_at = datetime.now()
|
||||
scan_row.vulnerabilities_found = len(seen_cves_for_asset)
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user