The KEV panel knew CISA (~1400 CVEs) and ENISA EUVD. VulnCheck KEV carries
~5200 and typically lists them days before CISA does — every record ships
CISA's own date, so the lead time is measurable, not a claim, and each row
now shows it.
Opt-in and silent by default: without VULNCHECK_API_TOKEN the source returns
{} and nothing about the install changes. Alerting is deliberately NOT opted
in either — kev_alert_sources stays "cisa,euvd", because ~4x the CVEs is the
operator's decision, not a side effect of updating.
No SDK: two GETs against a documented JSON API. The backup endpoint hands out
the whole index as one signed ZIP; the paginated index is only the fallback
and is capped at 600 documents on the community tier, which the log says out
loud so a partial catalog is never mistaken for the full one.
Attribution is a licence term, not decoration: "VulnCheck KEV" is named in the
KEV panel header and badges every row the source contributed to. Noted in
LICENSING.md so nobody strips it later.
Per-vulnerability scoring is untouched — kev_listed still comes from CISA
alone. Feeding 5200 CVEs into the URS bonus would move every score in the
fleet and belongs in its own decision.
226 lines
9.7 KiB
Bash
226 lines
9.7 KiB
Bash
# =============================================================================
|
|
# TrueVuln Environment Configuration
|
|
# =============================================================================
|
|
# Copy this file to .env and adjust values before starting:
|
|
# cp .env.example .env
|
|
#
|
|
# Designed to run behind a reverse proxy (Nginx Proxy Manager, Traefik, etc.)
|
|
# with HTTPS termination at the proxy level.
|
|
# =============================================================================
|
|
|
|
# --- Database ---
|
|
POSTGRES_USER=vulnmanager
|
|
POSTGRES_PASSWORD=changeme # Change this!
|
|
POSTGRES_DB=vulnmanager
|
|
POSTGRES_PORT=5432
|
|
|
|
# --- Application Ports ---
|
|
# These are the internal Docker ports. Map them in your reverse proxy.
|
|
BACKEND_PORT=8022
|
|
FRONTEND_PORT=3003
|
|
|
|
# --- Security (REQUIRED) ---
|
|
# Generate with: openssl rand -hex 32
|
|
JWT_SECRET_KEY=CHANGE-ME-GENERATE-WITH-openssl-rand-hex-32
|
|
|
|
# --- Environment ---
|
|
# 'production' disables Swagger docs and enables security headers.
|
|
# 'development' enables Swagger UI at /docs and relaxes some checks.
|
|
ENV=production
|
|
# Expose Swagger UI (/docs) + ReDoc (/redoc) on a production instance without
|
|
# switching to development — handy for ITSM/CMDB API integrators. Default off.
|
|
# The raw OpenAPI spec (/openapi.json) is always served regardless.
|
|
ENABLE_API_DOCS=false
|
|
|
|
# --- Cookies ---
|
|
# Set to true when using HTTPS (recommended, required behind HTTPS proxy)
|
|
AUTH_COOKIE_SECURE=true
|
|
AUTH_COOKIE_SAMESITE=lax
|
|
|
|
# --- Reverse Proxy ---
|
|
# Enable if running behind a reverse proxy to trust X-Forwarded-For headers
|
|
# for correct client IP in rate limiting and audit logs.
|
|
TRUST_PROXY_HEADERS=true
|
|
|
|
# --- Default Admin Account ---
|
|
# Created on first start only (if no admin exists in the database).
|
|
# The password MUST satisfy strength rules (>=8 chars, upper, lower, digit, special),
|
|
# otherwise the admin will NOT be created and login will be impossible.
|
|
# Change the password immediately after first login.
|
|
DEFAULT_ADMIN_USERNAME=admin
|
|
DEFAULT_ADMIN_PASSWORD=ChangeMe123!
|
|
DEFAULT_ADMIN_EMAIL=admin@vulnmanager.local
|
|
|
|
# Set to true to skip default admin creation entirely
|
|
# DISABLE_DEFAULT_ADMIN=false
|
|
|
|
# --- Emergency Admin Reset (optional) ---
|
|
# Uncomment and set a token to enable the /auth/setup-admin endpoint.
|
|
# SETUP_ADMIN_TOKEN=your-setup-token
|
|
# ALLOW_ADMIN_RESET=false
|
|
|
|
# --- Timezone ---
|
|
TIMEZONE=Europe/Zurich
|
|
|
|
# --- Dashboard URL ---
|
|
# Base URL used to build ALL links in email notifications (dashboard button,
|
|
# per-CVE / per-asset deep links, the CVE links inside the digest table).
|
|
# The backend can't know how your browser reaches the frontend, so you MUST set
|
|
# this to exactly how users open TrueVuln — otherwise links point at the
|
|
# fallback http://localhost:3000 and won't work.
|
|
# e.g. http://<host-or-ip>:${FRONTEND_PORT} or https://vuln.example.com behind a proxy
|
|
# Set it, then restart the backend (docker compose up -d).
|
|
DASHBOARD_URL=http://localhost:3003
|
|
|
|
# --- Microsoft Intune / Graph (optional) ---
|
|
# Configured in the UI: Settings → "Microsoft Intune (Graph API)" card
|
|
# (stored encrypted as the `intune_config` setting), NOT via env. Pulls
|
|
# Intune managed devices → assets + OS-EOL.
|
|
# App-only (client-credentials): register an Entra app, add the Application
|
|
# permission DeviceManagementManagedDevices.Read.All, grant admin consent,
|
|
# create a client secret. (Defender TVM phase: BOTH Machine.Read.All AND
|
|
# Vulnerability.Read.All on WindowsDefenderATP — the /api/machines call
|
|
# requires Machine.Read.All.) No env vars required.
|
|
|
|
# --- NVD API key (optional, recommended) ---
|
|
# Used to backfill each CVE's official published / lastModified date from
|
|
# the NVD CVE API (drives the "Newly Published" dashboard widget sort).
|
|
# Without a key NVD allows 5 requests / 30s; with one, 50 / 30s — so a
|
|
# fresh database fills in published dates ~10x faster. Free, request at:
|
|
# https://nvd.nist.gov/developers/request-an-api-key
|
|
# NVD_API_KEY=your-nvd-api-key
|
|
|
|
# --- VulnCheck KEV (optional) ---
|
|
# Third "actively exploited" catalog next to CISA KEV and ENISA EUVD. Carries
|
|
# ~5200 CVEs against CISA's ~1400 and usually lists them earlier; the KEV panel
|
|
# shows the lead time per CVE. Free community account -> API token at
|
|
# https://console.vulncheck.com/
|
|
# Free for commercial use as long as "VulnCheck KEV" stays attributed in the
|
|
# UI (the Advisories page does that automatically).
|
|
# Without a token the source stays silent and nothing changes.
|
|
# VULNCHECK_API_TOKEN=vulncheck_...
|
|
#
|
|
# To also alert on it, add "vulncheck" to the kev_alert_sources setting
|
|
# (default "cisa,euvd" — opting in is deliberate, it is ~4x the CVEs).
|
|
|
|
# --- OpenRouter AI remediation (optional) ---
|
|
# Enables the "AI Remediation" button on the CVE detail page — generates
|
|
# OS-aware fix steps/commands on demand. OpenAI-compatible; get a key at
|
|
# https://openrouter.ai/keys (free tier ~10 req/day across free models).
|
|
# Without a key the feature stays hidden.
|
|
# OPENROUTER_API_KEY=sk-or-...
|
|
# Model slug (OpenRouter uses dots, e.g. anthropic/claude-sonnet-4.6).
|
|
# Default openrouter/free auto-routes across available free models.
|
|
# OPENROUTER_MODEL=openrouter/free
|
|
# Optional comma-separated fallback models (route=fallback):
|
|
# OPENROUTER_FALLBACKS=openrouter/auto,anthropic/claude-sonnet-4.6
|
|
|
|
|
|
# =============================================================================
|
|
# Multi-Provider Authentication (LDAP / OIDC / SAML / TOTP-MFA)
|
|
# =============================================================================
|
|
# Default config = local only (backwards compatible). Enable providers
|
|
# below by listing them in AUTH_PROVIDERS and filling in the matching block.
|
|
# =============================================================================
|
|
|
|
# Comma-separated list of enabled providers. Local is always recommended
|
|
# as fallback for emergency admin access.
|
|
AUTH_PROVIDERS=local
|
|
# AUTH_PROVIDERS=local,ldap,oidc,saml
|
|
|
|
# Order in which credential-based providers are tried for username/password
|
|
# login. SSO providers (saml/oidc) are not in this chain — they have their
|
|
# own redirect endpoints.
|
|
AUTH_LOOKUP_ORDER=local,ldap
|
|
|
|
# Auto-create local user stub on first SSO/LDAP login. Re-evaluates role
|
|
# from external groups on every subsequent login.
|
|
AUTH_JIT_PROVISIONING=true
|
|
|
|
# Fallback role when no group-mapping rule matches.
|
|
# Values: admin, editor, readonly
|
|
AUTH_JIT_DEFAULT_ROLE=readonly
|
|
|
|
# Fernet key for encrypting LDAP bind-pw and TOTP secrets at rest.
|
|
# Generate with:
|
|
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
# CHANGE THIS — losing the key invalidates all stored TOTP secrets.
|
|
AUTH_PROVIDER_CRYPTO_KEY=CHANGE-ME-FERNET-KEY
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# LDAPS (Active Directory by default)
|
|
# -----------------------------------------------------------------------------
|
|
LDAP_HOST=ldaps.company.internal
|
|
LDAP_PORT=636
|
|
LDAP_USE_SSL=true
|
|
LDAP_USE_STARTTLS=false # mutually exclusive with USE_SSL
|
|
# CA bundle that signed the LDAPS server cert (required for strict validation).
|
|
LDAP_CA_CERT_PATH=/etc/ssl/certs/company-ca.pem
|
|
LDAP_VALIDATE_CERT=true # NEVER set to false in production
|
|
|
|
# Service account for the search-then-bind flow.
|
|
LDAP_BIND_DN=cn=svc-truevuln,ou=ServiceAccounts,dc=company,dc=local
|
|
# Bootstrap password — loaded once, encrypted, stored in DB.
|
|
# Remove from env AFTER the first successful start.
|
|
LDAP_BIND_PASSWORD_BOOTSTRAP=
|
|
|
|
# User search
|
|
LDAP_USER_SEARCH_BASE=ou=Users,dc=company,dc=local
|
|
# {username} is replaced by the login form input (filter-escaped).
|
|
LDAP_USER_SEARCH_FILTER=(&(objectClass=user)(sAMAccountName={username}))
|
|
LDAP_USER_ATTR_USERNAME=sAMAccountName
|
|
LDAP_USER_ATTR_EMAIL=mail
|
|
LDAP_USER_ATTR_GROUPS=memberOf
|
|
LDAP_USER_ATTR_GUID=objectGUID # AD stable identifier
|
|
LDAP_USER_ATTR_DISPLAY=displayName
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# OIDC (OpenID Connect) — Entra ID / Okta / Keycloak / Google
|
|
# -----------------------------------------------------------------------------
|
|
OIDC_PROVIDER_NAME=Entra ID
|
|
# Examples:
|
|
# Entra ID: https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
|
|
# Okta: https://<your-okta-domain>/.well-known/openid-configuration
|
|
# Keycloak: https://kc.company.com/realms/<realm>/.well-known/openid-configuration
|
|
# Google: https://accounts.google.com/.well-known/openid-configuration
|
|
OIDC_DISCOVERY_URL=
|
|
OIDC_CLIENT_ID=
|
|
OIDC_CLIENT_SECRET=
|
|
OIDC_REDIRECT_URI=https://truevuln.company.com/auth/oidc/callback
|
|
OIDC_SCOPES=openid profile email groups
|
|
|
|
# Claim names — defaults work for Entra ID / Keycloak. Adjust per IdP.
|
|
OIDC_CLAIM_USERNAME=preferred_username
|
|
OIDC_CLAIM_EMAIL=email
|
|
OIDC_CLAIM_GROUPS=groups
|
|
OIDC_CLAIM_DISPLAY=name
|
|
OIDC_CLAIM_SUBJECT=sub
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# SAML 2.0
|
|
# -----------------------------------------------------------------------------
|
|
SAML_PROVIDER_NAME=Single Sign-On
|
|
SAML_SP_ENTITY_ID=https://truevuln.company.com/auth/saml/metadata
|
|
SAML_SP_ACS_URL=https://truevuln.company.com/auth/saml/acs
|
|
SAML_SP_SLO_URL=https://truevuln.company.com/auth/saml/slo
|
|
|
|
# SP cert + key — generate a keypair specifically for this SP:
|
|
# openssl req -x509 -newkey rsa:2048 -nodes \
|
|
# -keyout sp.key -out sp.crt -days 730 \
|
|
# -subj "/CN=truevuln.company.com"
|
|
SAML_SP_CERT_PATH=/etc/truevuln/saml/sp.crt
|
|
SAML_SP_PRIVATE_KEY_PATH=/etc/truevuln/saml/sp.key
|
|
|
|
# IdP metadata source — exactly one of:
|
|
SAML_IDP_METADATA_URL=
|
|
# SAML_IDP_METADATA_PATH=/etc/truevuln/saml/idp-metadata.xml
|
|
|
|
# Attribute mapping (defaults work for most IdPs)
|
|
SAML_ATTR_USERNAME=urn:oid:0.9.2342.19200300.100.1.1
|
|
SAML_ATTR_EMAIL=urn:oid:1.2.840.113549.1.9.1
|
|
SAML_ATTR_GROUPS=http://schemas.xmlsoap.org/claims/Group
|
|
SAML_ATTR_DISPLAY=urn:oid:2.16.840.1.113730.3.1.241
|