docs(README.DEV): document Nessus override + SSVC + migrations 011/012
- Merge logic block reflects new Nessus-wins behaviour (cvss/severity override during sync, not max-merge) - Adds Manual Overrides section covering vuln_override_service.py + /override/check, /override/nessus, /override/vulnrichment endpoints - Schema table grows nessus_vpr_score (011) and exploitation_status (012) - Verification block updated to alembic head 012 and the new scores_overridden stat in sync response Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+39
-6
@@ -683,12 +683,15 @@ reports.
|
||||
| `vulnerabilities.nessus_finding_uuid` | string | per-host-per-plugin stable id |
|
||||
| `vulnerabilities.first_detected_by` | string | which scanner first reported (wazuh/nessus/manual) |
|
||||
| `vulnerabilities.cve_id` | widened 20 → 50 | room for NESSUS-PLUGIN-* pseudo-IDs |
|
||||
| `vulnerabilities.nessus_vpr_score` | float indexed (mig. 011) | Tenable VPR 0–10 alongside our own priority |
|
||||
| `vulnerabilities.exploitation_status` | string indexed (mig. 012) | SSVC: `none|poc|active|widespread` from Vulnrichment |
|
||||
| `assets.nessus_host_uuid` | string indexed | pin Nessus host after first match |
|
||||
| `scan_schedules.scanner_type` | string default 'wazuh' | route scheduled syncs to wazuh or nessus |
|
||||
|
||||
Migration 010 backfills `sources='["wazuh"]'` and
|
||||
`first_detected_by='wazuh'` on every existing vulnerability — no data
|
||||
loss, zero behaviour change until Nessus is configured.
|
||||
loss, zero behaviour change until Nessus is configured. Migrations 011
|
||||
+ 012 are idempotent (`ADD COLUMN IF NOT EXISTS`).
|
||||
|
||||
## Asset matching
|
||||
|
||||
@@ -710,20 +713,49 @@ the VulnCheck inventory.
|
||||
existing = find(cve, asset)
|
||||
if existing:
|
||||
existing.add_source("nessus")
|
||||
existing.severity = max(existing.severity, nessus_severity)
|
||||
existing.cvss_score = max(existing.cvss_score, nessus_cvss)
|
||||
# Nessus wins when it provides a value (typical case: Wazuh dropped
|
||||
# a placeholder 10.0 on the row, Nessus has the real per-plugin score).
|
||||
if nessus_cvss is not None:
|
||||
existing.cvss_score = nessus_cvss
|
||||
if nessus_severity and nessus_severity != "none":
|
||||
existing.severity = nessus_severity
|
||||
else:
|
||||
existing.severity = max(existing.severity, nessus_severity)
|
||||
existing.nessus_plugin_id = plugin_id
|
||||
existing.nessus_vpr_score = vpr_score # always latest
|
||||
if existing.status == patched:
|
||||
existing.status = open # Nessus sees it again → reopen
|
||||
else:
|
||||
create new with sources=["nessus"], first_detected_by="nessus"
|
||||
```
|
||||
|
||||
Override safety: if Nessus has no CVSS for a CVE (`None`), the existing
|
||||
Wazuh value is preserved — no data wipe. Same for severity = `none`.
|
||||
|
||||
When a previously-Nessus-flagged CVE is missing from this scan run, we
|
||||
**only drop** `"nessus"` from the sources list. If the list empties
|
||||
(no scanner sees it anymore), status flips to patched. Wazuh findings
|
||||
are never removed by a Nessus sync.
|
||||
|
||||
## Manual overrides (CISA Vulnrichment + Nessus)
|
||||
|
||||
`app/services/vuln_override_service.py` provides on-demand correction of
|
||||
CVSS/severity/exploitation data from authoritative feeds. Used when
|
||||
Wazuh-only findings need their placeholder 10.0 scores fixed without
|
||||
waiting for a Nessus sync.
|
||||
|
||||
- `POST /api/v1/vulnerabilities/override/check` — dry-run, lists rows
|
||||
that would change
|
||||
- `POST /api/v1/vulnerabilities/override/nessus/{asset_id}` — pull from
|
||||
Nessus plugin output for one asset
|
||||
- `POST /api/v1/vulnerabilities/override/vulnrichment?dry_run=…` — pull
|
||||
CISA Vulnrichment JSON feed (all CVEs)
|
||||
|
||||
Detects "Wazuh placeholder 10.0" + score diffs ≥ 1.0 between current
|
||||
and verified value. Writes new `vulnerabilities.exploitation_status`
|
||||
(SSVC: `none|poc|active|widespread`) — rendered as colored badge in
|
||||
the Vulns list when value ≠ `none`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Role | Purpose |
|
||||
@@ -795,8 +827,8 @@ Settings → Integrations gains a **Tenable Nessus** row with:
|
||||
# 1. Migrate
|
||||
git pull origin dev
|
||||
docker compose build backend frontend && docker compose up -d backend frontend
|
||||
docker compose exec backend alembic upgrade head # 009 → 010
|
||||
docker compose exec backend alembic current # 010 (head)
|
||||
docker compose exec backend alembic upgrade head # 009 → 012
|
||||
docker compose exec backend alembic current # 012 (head)
|
||||
|
||||
# 2. Backfill check
|
||||
docker compose exec postgres psql -U vulnmanager -d vulnmanager -c "
|
||||
@@ -815,7 +847,8 @@ curl -X POST http://<host>/api/v1/vulnerabilities/nessus/sync \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
# returns {scans_processed, hosts_synced, vulns_created, vulns_merged,
|
||||
# vulns_marked_patched, unmatched_hosts[], notifications:{…}}
|
||||
# scores_overridden, vulns_marked_patched, unmatched_hosts[],
|
||||
# notifications:{…}}
|
||||
|
||||
# 5. Cross-confirmation listing (CVEs in both scanners)
|
||||
curl -s "http://<host>/api/v1/vulnerabilities?cross_confirmed=true&limit=5" \
|
||||
|
||||
Reference in New Issue
Block a user