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.
19 KiB
VulnCheck — Architecture Overview
Stand: Mai 2026 (dev-Branch, post-Migration 022). Diese Doku spiegelt den
dev-Branch wider. Stablemainist eine Teilmenge — sieheREADME.mdvs.README.DEV.md.
1. System Architecture
┌──────────────────────────────────────┐
│ Browser (UI) │
│ Next.js 14 App Router (SSR + CSR) │
└──────────────────┬───────────────────┘
│ HTTPS/JSON + JWT (Cookie+Bearer)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Backend — FastAPI (Python 3.12) │
│ │
│ ┌──────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Routers │ │ Services │ │ Integrations (clients) │ │
│ │ (HTTP API) │──▶ (Business Logic)│──▶ Wazuh / Nessus / NVD / │ │
│ └──────────────┘ └─────────────────┘ │ EPSS / KEV / EUVD / │ │
│ │ Vulnrichment / cvelistV5 │ │
│ └─────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ APScheduler — Background Jobs │ │
│ │ - sync_schedules (every 60s, picks up DB changes) │ │
│ │ - SLA breach checker (hourly) │ │
│ │ - Daily threat-intel refresh (EPSS + KEV) │ │
│ │ - Nightly compliance SCA refresh (02:00 UTC) │ │
│ │ - Nightly Vulnrichment cascade (03:00 UTC) │ │
│ │ - Nightly URS recompute + snapshot prune (04:00 UTC) │ │
│ │ - User-defined scan schedules (Wazuh or Nessus, cron/interval) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└────────────────────┬──────────────────────────────┬──────────────────────┘
│ │
SQLAlchemy 2.x httpx clients
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ PostgreSQL 16 │ │ External Sources │
│ - alembic-managed │ │ - Wazuh Indexer/API │
│ - 22 migrations live │ │ - Nessus REST API │
│ - JSON columns: │ │ - NVD REST API │
│ sources, refs, │ │ - FIRST.org EPSS │
│ enrichment_sources │ │ - CISA KEV feed │
│ - Settings KV store │ │ - ENISA EUVD │
└────────────────────────┘ │ - cisagov/vulnrichment│
│ - cveproject/cvelistV5│
│ - SMTP server (mails) │
│ - LDAPS / OIDC / SAML │
└────────────────────────┘
Stack
| Layer | Tech |
|---|---|
| Frontend | Next.js 14 App Router, React 18, TypeScript, Tailwind CSS, Heroicons |
| Backend | FastAPI 0.115, Pydantic v2, Uvicorn |
| ORM | SQLAlchemy 2.x + Alembic |
| Database | PostgreSQL 16 |
| Scheduler | APScheduler 3.x (interval + cron triggers) |
| Auth | Local (bcrypt + TOTP), LDAPS (ldap3), OIDC (authlib + JWKS), SAML 2.0 (python3-saml) |
| AI | Infomaniak AI proxy (configurable via env) |
| HTTP client | httpx (sync + async) |
| Container | Docker Compose (backend, frontend, postgres) |
2. Security Architecture (OWASP Top 10 Mitigation)
| OWASP risk | Defence in VulnCheck |
|---|---|
| A01 Broken Access Control | RBAC enforced via require_role() dependency on every router; admin / editor / viewer scopes. Asset-ownership filter on vuln listings. |
| A02 Cryptographic Failures | TOTP secrets encrypted at rest with AUTH_PROVIDER_CRYPTO_KEY (Fernet). LDAP bind password same. bcrypt cost-12 for local passwords. JWT secret rotated per deployment. |
| A03 Injection | SQLAlchemy parameterised queries everywhere. CVE-IDs and search inputs sanitised. Wazuh-Indexer queries built via OpenSearch DSL, not string interpolation. |
| A04 Insecure Design | Strategy-Pattern auth (LocalAuthStrategy, LdapStrategy, OidcStrategy, SamlStrategy) — single orchestrator, single JWT path. JIT provisioning + role mapping configurable, no hard-coded admins. |
| A05 Security Misconfiguration | verify_ssl=True default for upstream integrations. CORS restricted to configured frontend host. CSP header on Next.js. |
| A06 Vulnerable Components | pip-audit + npm audit in CI. Dependabot on dev. |
| A07 Auth Failures | Failed-login counter on users table, lockout after threshold. Constant-time bcrypt verify for unknown usernames (pre-computed dummy hash). MFA gate before JWT issue. SAML strict signature + XSW check. |
| A08 Data Integrity | created_at/updated_at mixin on every audit-relevant row. AuditLog row per security event. Alembic migrations idempotent. |
| A09 Logging & Monitoring | Structured logger across backend, NotificationLog table, AuditLog table, /api/v1/audit/logs admin view. |
| A10 SSRF | All outbound integrations whitelist-URL; no user-controllable URLs. |
3. Component Details
3.1 Routers (app/routers/)
Each router groups a logical resource. All require JWT except /auth/* login endpoints.
| Router | Resource | Key endpoints |
|---|---|---|
auth.py |
Local login + MFA | /auth/login, /auth/verify, /auth/mfa/{setup,activate,disable}, /auth/providers |
auth_admin.py |
Provider config + role mapping | /auth/admin/role-mappings, /auth/admin/{ldap,oidc,saml}/test |
auth_oidc.py |
OIDC SSO | /auth/oidc/login, /auth/oidc/callback |
auth_saml.py |
SAML 2.0 SSO | /auth/saml/login, /auth/saml/acs, /auth/saml/metadata |
vulnerabilities.py |
Vulns, enrichment, overrides, sync | /vulnerabilities, /vulnerabilities/sync/wazuh, /vulnerabilities/enrich/*, /vulnerabilities/override/* |
nessus.py |
Tenable Nessus | /vulnerabilities/nessus/{test,scans,sync,scan-host,cleanup-pseudo-cves} |
assets.py |
Asset CRUD + assignment | /assets, /assets/{id}, /assets/bulk-update, /assets/{id}/assign |
scans.py |
Scan history + schedules | /scans, /scans/summary, /scans/clear, /scans/autoscan, /scans/schedules |
compliance.py |
SCA + URS | /compliance/summary, /compliance/assets, /compliance/{asset_id}, /compliance/refresh, /compliance/impacts/{import,stats}, /compliance/urs[/...] |
policies.py |
Security policies (SLA) | /policies, /policies/{id}, /policies/bulk-update |
groups.py |
User groups | /groups, /groups/{id}, /groups/default |
settings.py |
Key-value config | /settings, /settings/{key} |
notifications.py |
Mail log + test | /notifications/log, /notifications/test, /notifications/notify-critical |
reports.py |
CSV / PDF exports | /reports/csv, /reports/pdf |
audit.py |
Security audit trail | /audit/logs |
3.2 Services (app/services/)
Pure business logic, no FastAPI imports. Reusable from scheduler + routers.
| Service | Role |
|---|---|
enrichment_service.py |
EPSS / KEV / EUVD enrichment per CVE, with 24h cache in settings |
vuln_override_service.py |
3-stage CVSS / SSVC / fixed_version cascade (Vulnrichment → NVD → cvelistV5) |
override_jobs.py |
Async job tracker for the long-running "Correct CVSS" button |
nessus_sync.py |
Per-scan import, asset matching cascade, pseudo-CVE handling |
compliance_service.py |
Wazuh SCA pull + weighted-score computation per policy |
compliance_impact_import.py |
CIS-Benchmark CSV importer with loose-header matching |
urs_service.py |
AVS + ASS → URS formula, snapshot writes, daily prune |
email_service.py |
SMTP send, per-recipient templates, digest + single modes |
3.3 Integrations (app/integrations/)
Thin HTTP clients, no DB writes.
| Client | Purpose |
|---|---|
wazuh_client.py |
OpenSearch indexer DSL — paginated vuln + SCA fetch |
nessus_client.py |
REST API + plugin output parsing (fixed_version, VPR, host UUID) |
ai_client.py |
LLM dispatcher (currently routes to Infomaniak) |
infomaniak_ai_client.py |
Infomaniak chat-completion proxy |
3.4 Auth Stack — Strategy Pattern
POST /auth/login (username + password)
│
▼
┌────────────────────────────┐
│ AuthOrchestrator │ chain = AUTH_LOOKUP_ORDER
│ (env: local,ldap) │
└──┬─────────────────────────┘
│ on success
▼
┌────────────────────────────┐
│ MFA gate (if totp_enabled) │
└──┬─────────────────────────┘
│ OK
▼
JWT (httpOnly cookie + bearer in response body)
GET /auth/oidc/login ── PKCE redirect ── IdP
POST /auth/oidc/callback ── JWKS-validated id_token → orchestrator.complete_sso_login()
GET /auth/saml/login ── python3-saml AuthnRequest
POST /auth/saml/acs ── strict signature + XSW check → orchestrator.complete_sso_login()
JIT provisioning (auth.jit_provisioner) auto-creates User rows on first
SSO/LDAP login. RoleMapper re-evaluates role on every login from
external_groups, so AD-side membership changes apply immediately.
3.5 Scheduler (app/scheduler.py)
| Job ID | Cadence | What it does |
|---|---|---|
scheduler_sync |
every 60 s | Picks up DB changes to scan_schedules and re-registers jobs |
sla_breach_check |
every 1 h | SLA-overdue scan; honors sla_breach_enabled toggle + PolicyStatus.DISABLED skip; digest or single mode |
threat_intel_refresh |
every 24 h | Refreshes EPSS, KEV, EUVD across all open vulns |
compliance_sca_nightly |
02:00 UTC | Wazuh SCA pull for every linked asset |
vulnrichment_nightly |
03:00 UTC | 3-stage CVSS/SSVC/fixed_version cascade |
urs_nightly |
04:00 UTC | URS recompute + asset_risk_snapshots prune (>90d) |
| user-defined | per schedule | Per-scan_schedules row, routes to run_wazuh_vulnerability_sync or run_nessus_sync based on scanner_type |
4. Data Model
See DATABASE_SCHEMA.md for column-level detail. High-level entities and their main relationships:
User ─┐ Policy ── 1:N ──┐
│ M:N (user_groups) │
Group ┴── 1:N ──┐ │
│ │
▼ M:N (asset_groups) ▼
Asset ──── 1:N ────► Vulnerability ──── 1:N ──► AIAnalysis
│ │
│ 1:N │ 1:N
▼ ▼
Scan NotificationLog
│
└── 1:N ──► ComplianceResult ── 1:N ──► ComplianceCheck
ComplianceImpact (cis_id, benchmark) ◄── lookup for weighted_score
AssetRiskSnapshot (asset_id, snapshot_date, avs, ass, urs, severity)
AuditLog (user_id, event_type, …)
Setting (key, value) ◄── KV store: smtp, nessus_config,
enrichment flags, sla toggle,
cache for EPSS/KEV/EUVD JSON,
role-mapping JSON, etc.
Vulnerabilities carry a sources JSON list (["wazuh","nessus"]) so one row
holds the union per (cve_id, asset_id). Non-CVE Nessus plugins land under
pseudo-IDs NESSUS-PLUGIN-{plugin_id}.
5. API Endpoints (summary)
Full reference: see Router section above + Swagger UI at /docs. JWT auth required
on everything except /auth/login, /auth/oidc/*, /auth/saml/*, /auth/setup-admin.
| Group | Examples |
|---|---|
| Auth | POST /auth/login, POST /auth/verify (MFA), POST /auth/mfa/setup, POST /auth/oidc/login |
| Vulnerabilities | `GET /vulnerabilities?source=wazuh |
| Assets | GET /assets, POST /assets/{id}/assign, POST /assets/{id}/rescan |
| Scans | GET /scans/summary, POST /scans/autoscan, POST /scans/schedules |
| Compliance / URS | POST /compliance/refresh, POST /compliance/impacts/import (multipart), GET /compliance/urs?avs_mode=hybrid |
| Nessus | POST /vulnerabilities/nessus/test, POST /vulnerabilities/nessus/sync |
| Settings | PUT /settings/{key} (admin) |
Query parameters of note on GET /api/v1/vulnerabilities:
status, severity, source, cross_confirmed, kev_only, euvd_only,
eu_critical, in_any_catalog, in_both_catalogs, epss_min,
assigned_to_me, host, package, cve_id, sort_by
(cve_id|priority_score|cvss_score|cpr_score|detected_at|published_date|updated_at),
sort_order, limit, offset.
6. Deployment Architecture
docker-compose.yml
├── postgres (volume: pgdata)
├── backend (FastAPI + APScheduler) ── alembic upgrade head on start
└── frontend (Next.js standalone build, served via node)
Environment configuration via .env (gitignored). Required keys:
DATABASE_URL=postgresql://vulnmanager:…@postgres/vulnmanager
JWT_SECRET=… # rotate per deployment
AUTH_PROVIDER_CRYPTO_KEY=… # Fernet, encrypts TOTP + LDAP bind pw
# Wazuh
WAZUH_API_URL=https://…:55000
WAZUH_INDEXER_URL=https://…:9200
WAZUH_API_USER=…
WAZUH_API_PASS=…
# Optional
DASHBOARD_URL=http://localhost:3000 # used in mail links
INFOMANIAK_API_KEY=… # AI analysis
LDAP_BIND_PASSWORD_BOOTSTRAP=… # first-start LDAP setup
HTTPS_PROXY=… # for outbound enrichment
Optional outbound HTTPS endpoints (firewall whitelist if locked-down):
https://api.first.org(EPSS)https://www.cisa.gov(KEV)https://euvdservices.enisa.europa.eu(EUVD)https://raw.githubusercontent.com(Vulnrichment per-CVE + ZIPs)https://github.com(cvelistV5 ZIP)https://services.nvd.nist.gov(NVD REST)https://nessus.your-internal/...(Nessus)
Upgrade flow
git pull origin dev
docker compose build backend frontend
docker compose up -d backend frontend
docker compose exec backend alembic upgrade head
docker compose exec backend alembic current # expect: 022 (head)
7. Performance & Scaling
- Wazuh-Indexer pagination beyond the OpenSearch 10 000-hit cap: search-after / scroll in
WazuhClient.get_vulnerabilities. - Vulnrichment cascade auto-routes to ZIP snapshot when > 25 CVEs requested — avoids 25 × N raw fetches.
- cvelistV5 ZIP (~557 MB) is on-disk-cached 12 h at
/tmp/vulncheck-cvelistv5-cache.zip. - NVD stage caps at 100 CVEs per run (rate limit). >100 missing → falls through to cvelistV5 directly.
sources/enrichment_sourcesJSON columns avoid table-join chatter for per-vuln source filtering.- Indexed columns:
cve_id,asset_id,cvss_score,severity,epss_score,kev_listed,euvd_listed,exploitation_status,ssvc_technical_impact,ssvc_automatable,nessus_vpr_score,nessus_plugin_id,package_name,detected_at. - "Correct CVSS" runs as an async job (
override_jobs.py) so the UI returns immediately with a job_id and a polling progress card — full-DB corrections take 5-20 min.
8. Monitoring & Observability
- Structured logger to stdout;
docker compose logs backendis the primary view. - AuditLog captures security-relevant events (login success / failure, role change, MFA reset, password change, deletes).
- NotificationLog records every mail attempt (status, error, recipient).
/scans/summarysurfaces sync history with per-run vuln deltas./notifications/loglists outbound mail history with retry status.- APScheduler logger prints next_run + last_run per job.
Recommended Prometheus exporters: postgres_exporter, node_exporter,
and a thin /healthz endpoint on the FastAPI side (returns DB ping + scheduler
status). Not yet shipped — placeholder for future ops work.
9. Development Roadmap
See README.DEV.md "Out of scope" sections for the live backlog. Big-ticket
items currently parked:
- WebAuthn / FIDO2 second factor
- Per-framework URS breakdown (URS_CIS / URS_ISO27001 / URS_NIS2)
- SCIM v2 endpoint for IdP-driven provisioning
- endoflife.date integration for EOL/EOS package detection
- Real-time KEV push from Wazuh (currently poll-based)
- Two-way Nessus false-positive sync
- Concentration penalty on URS (≥ 3 high-impact failures → +10)
- Settings-UI toggle for
sla_breach_enabled(currently DB-only)