Files
vulncheck/PROJECT_OVERVIEW.md
T
vulncheck 0c34321a00 docs: refresh ARCHITECTURE / DATABASE_SCHEMA / PROJECT_OVERVIEW
Previous docs dated Feb 1-2 — pre multi-provider auth, pre Nessus,
pre compliance/URS. Full rewrite covering dev-branch state at
migration 022:

- ARCHITECTURE.md: current stack table, scheduler job list, auth
  strategy diagram, OWASP mitigation matrix, deployment + perf notes.
- DATABASE_SCHEMA.md: all 20+ tables with columns, indexes, enums,
  FK CASCADE semantics, full migration 001-022 history.
- PROJECT_OVERVIEW.md: feature list (5 threat-intel sources, multi-
  scanner, multi-auth, compliance/URS), workflow examples, debugging
  commands, current limitations.

README.DEV.md still ahead of these — keeps the long-form feature
deep-dives.
2026-05-24 13:54:47 +02:00

14 KiB
Raw Blame History

VulnCheck — Project Overview

Vulnerability Management Dashboard for a Swiss-school IT-Security setup. Wazuh agents + Nessus scanner + CISA/ENISA/EPSS threat intel + CIS-Benchmark compliance + Unified Risk Score, all behind a single FastAPI + Next.js stack.

Stand: Mai 2026, dev-Branch, alembic head 022.


🎯 Summary

VulnCheck collects vulnerability findings from multiple scanners (Wazuh, Nessus), enriches them with several free public threat-intel sources, scores the result on a unified 0-100 risk scale, and surfaces them in a role-based web UI with SLA-tracking, mail notifications, compliance evidence (Wazuh SCA

  • CIS-Benchmark impact weights), and an LLM-backed remediation analysis.

Designed for internal small-to-mid IT teams (5-100 hosts). Single-server deployable via Docker Compose. Postgres-only persistence.


📁 Project Structure

vulnerability-dashboard/
├── app/                              # FastAPI backend
│   ├── routers/                      # HTTP API by resource
│   │   ├── auth.py, auth_admin.py
│   │   ├── auth_oidc.py, auth_saml.py
│   │   ├── vulnerabilities.py        # vulns, sync, enrichment, overrides
│   │   ├── nessus.py                 # Tenable Nessus
│   │   ├── assets.py, scans.py, policies.py, groups.py
│   │   ├── compliance.py             # SCA + URS endpoints
│   │   ├── notifications.py, reports.py, settings.py, audit.py
│   ├── services/                     # business logic, scheduler-reusable
│   │   ├── enrichment_service.py
│   │   ├── vuln_override_service.py  # 3-stage CVSS cascade
│   │   ├── override_jobs.py          # async job tracker
│   │   ├── nessus_sync.py
│   │   ├── compliance_service.py, compliance_impact_import.py
│   │   ├── urs_service.py
│   │   └── email_service.py
│   ├── integrations/                 # thin HTTP clients
│   │   ├── wazuh_client.py
│   │   ├── nessus_client.py
│   │   ├── ai_client.py, infomaniak_ai_client.py
│   ├── auth/                         # strategy-pattern auth
│   │   ├── orchestrator.py
│   │   ├── strategies/               # local, ldap, oidc, saml
│   │   ├── jit_provisioner.py, role_mapper.py
│   ├── models/                       # SQLAlchemy
│   │   └── vulnerability.py, asset.py, user.py, group.py, ...
│   ├── schemas/                      # Pydantic v2 request/response
│   ├── scheduler.py                  # APScheduler
│   ├── main.py                       # FastAPI app + middleware
│   └── deps.py                       # auth dependency, DB session
├── alembic/
│   └── versions/                     # 001 .. 022 migrations
├── alembic.ini
├── frontend/
│   ├── app/                          # Next.js 14 App Router
│   │   ├── page.tsx                  # Dashboard (2-widget Recent Critical + Newly Published)
│   │   ├── vulnerabilities/page.tsx  # main list
│   │   ├── vulnerabilities/[id]/     # detail
│   │   ├── assets/, compliance/, scans/, notifications/, settings/
│   ├── components/
│   ├── lib/api.ts                    # axios + auth header
│   ├── types/index.ts
│   └── public/logo.png
├── docs/
│   └── TROUBLESHOOTING.md
├── docker-compose.yml
├── ARCHITECTURE.md
├── DATABASE_SCHEMA.md
├── README.md                         # stable main feature set
└── README.DEV.md                     # dev-branch additions (this branch)

🚀 Quick Start

# 1. Clone
git clone <repo> && cd vulnerability-dashboard

# 2. Configure .env (see ARCHITECTURE.md §6 for full list)
cp .env.example .env
$EDITOR .env  # set DATABASE_URL, JWT_SECRET, AUTH_PROVIDER_CRYPTO_KEY,
              # WAZUH_API_URL/USER/PASS, WAZUH_INDEXER_URL

# 3. Start
docker compose up -d

# 4. First-start bootstrap creates the admin via /auth/setup-admin (run once)
curl -X POST http://localhost:8000/api/v1/auth/setup-admin \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"<strong-pass>","email":"you@example.com"}'

# 5. Open
#    UI:    http://localhost:3000
#    API:   http://localhost:8000/docs

docker compose up -d runs alembic upgrade head automatically on backend boot — no separate migration step needed for fresh deploys.


📊 Implemented Features

Threat-intel enrichment (5 sources)

  • EPSS — FIRST.org probability of exploitation, refreshed every 24 h
  • CISA KEV — known-exploited catalog, ~1500 CVEs, ransomware-use flag
  • ENISA EUVD — EU exploited + ENISA-flagged critical, ~1600 CVEs
  • CISA Vulnrichment — authoritative per-CVE CVSSv3.1 corrections + SSVC decision points (Exploitation / Technical Impact / Automatable). Parses CNA + ADP containers.
  • NVD REST API — 2nd-stage fallback for CVEs Vulnrichment hasn't analysed yet (rate-limit bounded ≤ 100 per run)
  • cvelistV5 — 3rd-stage backstop, exhaustive ~250 k CVEs, 12 h on-disk ZIP cache

All sources free, no API key required.

Scoring

  • Priority Score (0-100) — CVSS + exploit-bonus (KEV/EUVD/EPSS/SSVC/Wazuh/Nessus, capped via max) + SSVC technical-impact/automatable add-on + asset-criticality multiplier + age bonus
  • CPR Score (0-100) — JacquesKruger percentile-weighted blend: CVSS×10 × 0.6 + EPSS_percentile × 100 × 0.4
  • Tenable VPR (0-10) — surfaced as parallel reference, not folded into Priority/CPR
  • URS (0-100) — Unified Risk Score combining AVS (CPR-derived) + ASS (impact-weighted SCA) × criticality multiplier, daily snapshots feed trend arrow

Multi-scanner ingest

  • Wazuh — agent-based vulnerability detection + SCA compliance
  • Nessus — REST-API import alongside Wazuh on the same (cve_id, asset_id) row; pseudo-CVE pseudo-IDs for non-CVE plugins; per-host scan rows
  • Manual overrides — 3-stage CVSS/SSVC/fixed_version cascade triggered by "Correct CVSS" button or 03:00 UTC nightly job

Multi-provider authentication

  • Local — username + password (bcrypt cost-12) + optional TOTP MFA
  • LDAPS — search-then-bind, encrypted bind password
  • OIDC — PKCE + JWKS, Entra ID / Okta / Keycloak / Google
  • SAML 2.0 — python3-saml, strict signature + XSW protection
  • JIT provisioning + group-to-role mapping editable in UI

Compliance + URS

  • Wazuh SCA pulled per agent and stored as compliance_results + compliance_checks
  • CIS-Benchmark impact CSV upload weighted into per-policy weighted_score
  • Unified Risk Score per asset with daily snapshots and 7-day trend arrow

SLA tracking + notifications

  • Per-severity SLA days configurable globally + per policy
  • Hourly SLA breach checker → digest or single mode mails
  • Policy DISABLED status suppresses mails for that policy's assets
  • Master kill-switch via sla_breach_enabled setting
  • Per-vuln 24 h throttle prevents repeat-pinging
  • New-vuln digest after every Wazuh/Nessus sync

UI features

  • 2-widget dashboard ("Recent Critical" + "Newly Published")
  • Vulns list with VPR badge, EPSS / KEV / EUVD / SSVC pills, cross-scanner confirmation, badge legend popover
  • Per-vuln detail with threat-intel sections + Priority/CPR breakdown
  • Compliance page with per-asset modal and URS table
  • Settings UI for SMTP, Nessus, LDAP/OIDC/SAML, role mappings, mail templates

🔧 Configuration

.env keys at a glance:

# Database
DATABASE_URL=postgresql://vulnmanager:<pw>@postgres/vulnmanager

# JWT
JWT_SECRET=<random-256-bit>
JWT_ALGORITHM=HS256
JWT_EXPIRY_MINUTES=60

# Auth crypto (Fernet, encrypts TOTP + LDAP pw + SMTP pw at rest)
AUTH_PROVIDER_CRYPTO_KEY=<fernet-key>

# Wazuh
WAZUH_API_URL=https://wazuh-manager:55000
WAZUH_INDEXER_URL=https://wazuh-indexer:9200
WAZUH_API_USER=vulncheck-readonly
WAZUH_API_PASS=<pw>

# Optional
DASHBOARD_URL=http://localhost:3000          # used in mail links
INFOMANIAK_API_KEY=<key>                      # AI analysis
LDAP_BIND_PASSWORD_BOOTSTRAP=<pw>             # one-shot for first LDAP setup
HTTPS_PROXY=http://proxy:8080                 # for outbound enrichment

All UI-relevant config (SMTP, Nessus, LDAP, OIDC, SAML, role mappings, mail templates) lives in the settings table — no env-var redeploy needed.


📡 API Overview

See ARCHITECTURE.md §5 for the endpoint reference and /docs (Swagger UI) on the running backend for the full schema. Curl-friendly examples in README.DEV.md.


🔒 RBAC Permissions

Role Capabilities
admin Everything — user management, provider config, settings, role mappings, audit log
editor Sync, override, enrich, assign, defer, mark FP, schedule scans, compliance refresh, impact import
viewer Read-only — list / search vulns, assets, scans, reports, exports

Enforced by require_role() dependency on every router. JWT carries the role claim; group→role mapping re-evaluates on each SSO/LDAP login.


🗄️ Database Schema (PostgreSQL)

See DATABASE_SCHEMA.md for column-level detail. 20+ tables, 22 alembic migrations. Key tables:

  • users, groups, user_groups
  • assets, asset_groups
  • vulnerabilities
  • scans, scan_schedules
  • policies
  • notification_logs, audit_logs
  • compliance_results, compliance_checks, compliance_impacts, asset_risk_snapshots
  • settings (KV store), ai_analyses, ai_reports

🎨 Workflow Examples

Daily admin morning routine

  1. Dashboard widget shows URS trend + Critical / Newly Published CVEs
  2. SLA-breach mail already arrived (00:0008:00 stack)
  3. Click "Correct CVSS" once a week to refresh Vulnrichment-corrected scores
  4. Compliance widget shows worst 5 assets — drill in via per-asset modal
  5. Investigate "Recent Critical" rows — KEV / EUVD badges flag externally-confirmed exploit risk

Adding a Nessus scan

  1. Settings → Integrations → Tenable Nessus → Configure
  2. Paste Base URL + API access/secret keys → Test connection
  3. Pick default scan IDs → Save
  4. Sync now (or wait for next scheduled run)
  5. Vulns list now shows Wazuh + Nessus + cross-confirmed rows

Updating a Vulnrichment-corrected score for a single CVE

TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"…"}' | jq -r .access_token)

curl -X POST "http://localhost:8000/api/v1/vulnerabilities/override/vulnrichment?dry_run=false&cve_ids=CVE-2026-8390" \
  -H "Authorization: Bearer $TOKEN"

Response stats include not_found (CVEs CISA hasn't analysed yet — silently skipped).

Disabling SLA mails system-wide

docker compose exec postgres psql -U vulnmanager -d vulnmanager -c "
INSERT INTO settings (key, value) VALUES ('sla_breach_enabled','false')
ON CONFLICT (key) DO UPDATE SET value='false';"

Re-enable:

UPDATE settings SET value='true' WHERE key='sla_breach_enabled';

🚧 Known Limitations

  • sla_breach_enabled toggle has no UI yet — DB-only (Settings page improvement on backlog)
  • CIRCL aggregator not used — we hit CISA + ENISA directly
  • No real-time KEV push — we poll the CISA feed (24 h cache)
  • No two-way Nessus FP sync — marking false-positive in VulnCheck doesn't update Nessus
  • Wazuh-side fix-version field doesn't exist in the indexer schema — relying entirely on Vulnrichment/NVD/cvelistV5 cascade for PATCH AVAILABLE
  • Per-framework URS breakdown schema-ready but UI hidden
  • No WebAuthn / FIDO2 — TOTP is the only second factor

🛠️ Development

Run tests

docker compose exec backend pytest tests/

Smoke test covers auth, RBAC, vuln CRUD, sync mocking. Coverage is partial — integration paths (Wazuh, Nessus, enrichment HTTP) rely on manual + tester verification.

Add a new vuln column

  1. Edit app/models/vulnerability.py — add the Column(...)
  2. Edit app/schemas/vulnerability.py (or wherever the Pydantic schemas live)
  3. Generate migration:
    docker compose exec backend alembic revision --autogenerate -m "add foo column"
    
  4. Review the generated file (autogenerate misses index intent sometimes)
  5. alembic upgrade head
  6. Update _build_vuln_response() in app/routers/vulnerabilities.py if it should be in the API response

Add a new threat-intel source

  1. New service module in app/services/ (e.g. mysource_service.py)
  2. Cache feed JSON in settings table for 24 h
  3. Wire into enrichment_service.refresh_threat_intel_enrichment()
  4. Add columns to vulnerabilities table via alembic
  5. Surface in _build_vuln_response() + frontend list/detail

📈 Performance Optimisations

  • Wazuh indexer pagination beyond OpenSearch 10 000-hit cap (search-after)
  • Vulnrichment cascade auto-routes to ZIP when > 25 CVEs requested
  • cvelistV5 ZIP cached on disk 12 h
  • NVD stage capped at 100 CVEs per run (rate-limit safe)
  • Async "Correct CVSS" — long-running cascade returns a job_id and a progress card
  • Indexed columns on every filter / sort path
  • sources / enrichment_sources JSON columns avoid join-heavy queries

📚 Further Documentation

  • ARCHITECTURE.md — system topology, security model, scheduler jobs
  • DATABASE_SCHEMA.md — every column, every enum, every migration
  • CODEBASE_KNOWLEDGE_BASE.md — module-by-module walk-through for new contributors
  • README.DEV.md — dev-branch feature deep-dives (auth, Nessus, compliance, URS)
  • docs/TROUBLESHOOTING.md — common production issues and fixes

🐛 Debugging

# Backend logs (tail and follow)
docker compose logs -f backend

# All services
docker compose logs -f

# Postgres logs
docker compose logs -f postgres

# Inside backend container
docker compose exec backend bash
docker compose exec backend python3 -c "from app.services.enrichment_service import refresh_threat_intel_enrichment; refresh_threat_intel_enrichment()"

# DB shell
docker compose exec postgres psql -U vulnmanager -d vulnmanager

docs/TROUBLESHOOTING.md lists symptom → cause → fix patterns for the recurring production issues (alembic head conflicts, MFA setup 500s, LDAP bind decryption failures, missing SLA mails, etc.).


📧 Support

Internal IT-Security team. See README.md for project owner contact.