HTTP API reference¶
The CVE Intelligence Panel exposes a stateless JSON REST API on the Node/Express server. The UI uses the same endpoints via Vite proxy in development (/api → localhost:3001) and same-origin paths in production. Integrators may call the API from scripts, CI gates, or internal dashboards without loading the React client.
This chapter describes every public route, request validation, response shapes, error codes, and operational limits. For interactive trials without a running server, use the API Explorer tab (browser mock). For machine-readable contracts, fetch GET /api/openapi.json.
API design principles¶
The API follows common product conventions: JSON bodies, UTF-8, no session cookies (suitable for reverse proxies and API gateways), and explicit scan modes (full vs watch) reflected in meta.mode. Each scan is independent; the server does not persist your stack or findings unless you store responses client-side.
Two URL prefixes are supported:
| Prefix | Purpose |
|---|---|
/api/* |
Legacy-compatible paths used by the shipped UI |
/api/v1/* |
Versioned surface (same handlers, stable for integrations) |
New integrations should prefer /api/v1 and treat /api as an alias during transition.
Discovery and metadata¶
Before scanning, clients typically call discovery endpoints to learn limits, available sources, and runtime configuration.
Health — GET /api/health (also /api/v1/health)¶
Returns liveness plus non-secret capability flags. Use this for load-balancer health checks and startup probes.
Response fields (summary):
| Field | Meaning |
|---|---|
ok |
Always true when the process is up |
version |
App semver from package.json (e.g. 1.1.0) |
sources |
IDs of built-in feeds |
nvdApiKey / githubToken |
Whether env credentials are set (not the values) |
scanDays |
Look-back window from SCAN_DAYS (default 60) |
translate |
Whether server-side translation is allowed |
limits |
Numeric caps (stack size, batch sizes) |
Detailed probe: GET /api/health?detailed=true (same on /api/v1/health) adds uptime, per-source reachability, cache size/backend, optional air-gap mirror flags, translation provider, and alert config:
| Field | Meaning |
|---|---|
uptime |
Process uptime in seconds |
sources |
Object map of source ID → { reachable, lastOkAt, … } |
cache |
{ size, backend } — memory or Redis |
airgap |
Present when AIRGAPPED=true — mirror URL health |
translation.activeProvider |
DeepL, LibreTranslate, or GoogleGTX |
alerts.webhookConfigured |
true when any notification channel env is set (Slack, Discord, Telegram, email, generic webhook, or legacy ALERT_WEBHOOK_URL) |
alerts.minSeverity |
NOTIFICATION_MIN_SEVERITY or ALERT_MIN_SEVERITY (default HIGH) |
Prometheus metrics — GET /metrics¶
Root path (not under /api). Returns Prometheus text when METRICS_ENABLED=true (default). Set METRICS_PROTECT=true to require the same API_SECRET as API routes. See Self-hosted metrics and docs/self-hosted/METRICS.md.
Capabilities — GET /api/capabilities¶
Returns structured product limits and feature flags (max tools per scan, translation locales, scan modes). Prefer this over parsing health when building forms or CI validators.
Source catalog — GET /api/sources¶
Returns the built-in source registry (NVD, OSV, GitHub, CISA KEV, RSS feeds) with kind, fullScan, and watchScan flags, plus default RSS URLs. Custom feeds are not listed here; clients supply them per request in customFeeds.
OpenAPI — GET /api/openapi.json¶
Serves the OpenAPI 3.1 document (server/openapi/spec.json) for codegen, Postman import, or contract tests.
Request validation and errors¶
All POST bodies must be JSON objects. Validation failures return HTTP 400 with:
{
"error": "Human-readable message",
"code": "VALIDATION_ERROR",
"details": { "max": 50, "received": 72 }
}
Upstream failures (NVD rate limit, RSS timeout) return 502 or 429 with error text and optional code (SCAN_FAILED, WATCH_FAILED). The UI surfaces error as a string; integrators may also read code and details.
Application rate limit (middleware): When a client exceeds per-IP scan or watch quotas, the server returns HTTP 429:
{
"error": "Too many scan requests. Try again later.",
"code": "RATE_LIMITED",
"retryAfterSec": 42
}
POST /scanandPOST /watchuse separate minute buckets.POST /scan/validateis exempt (safe for CI and stack checks).- Tune via
RATE_LIMIT_SCAN_PER_MINandRATE_LIMIT_WATCH_PER_MINin.env.
API key (optional): When API_SECRET is set, protected routes require X-Api-Key or Authorization: Bearer. Wrong or missing credentials return 401:
{ "error": "Unauthorized", "code": "AUTH_REQUIRED" }
GET /api/health and GET /api/v1/health stay unauthenticated for load balancers. Browser UI: set build-time VITE_API_KEY to the same value (single-tenant internal deployments).
RBAC (v1, when API_SECRET is set): Routes under /api/v1/* enforce roles from server API_ROLE (default admin). Allowed roles per route are listed in OpenAPI x-requiredRoles. Wrong role returns 403:
{ "error": "Insufficient role for this operation", "code": "FORBIDDEN" }
Roles: admin (full), scanner (scan/watch/validate), viewer (read metadata + translate), auditor (read metadata + scan history). Legacy /api/* routes do not enforce RBAC today — prefer /api/v1 for authenticated deployments.
Multi-tenant header (v1): Send X-Tenant-Id: <slug> on versioned calls when using PostgreSQL (DATABASE_URL). Omit to use the default tenant. Required for tenant stack CRUD and tenant-scoped scan history.
Stack rules:
stack— required non-empty array of tool name strings (trimmed), max 50 tools.customFeeds— optional, max 20 entries; each needsidandurl.enabledBuiltin— optional map of source ID → boolean; omitted keys default to enabled.
Validate without scanning — POST /api/scan/validate¶
Validates stack, enabledBuiltin, and customFeeds without calling external CVE providers. Use this in wizards or CI to reject bad input before an expensive full scan.
Example request:
{
"stack": ["Redis", "HAProxy"],
"enabledBuiltin": { "NVD": true, "TuxCare": false },
"customFeeds": [{ "id": "custom:team-feed", "name": "Team RSS", "url": "https://example.com/cve.rss" }]
}
Example response:
{
"valid": true,
"stack": ["Redis", "HAProxy"],
"toolCount": 2,
"customFeedCount": 1,
"message": "Stack and source options are valid; run POST /api/scan for findings."
}
Full scan — POST /api/scan¶
Runs a full scan: NVD, OSV, GitHub Advisories, CISA KEV, built-in RSS, and enabled custom RSS feeds. Results are merged by CVE ID, enriched with KEV exploitation flags, optionally translated, then returned in one payload.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
stack |
string[] |
yes | Tool names to match |
translate |
boolean |
no | If true, translate titles/descriptions |
locale |
fa | ar | ru | zh | fr |
no | Target language when translate is true |
enabledBuiltin |
object |
no | Per-source toggles |
customFeeds |
array |
no | Extra RSS/Atom feeds (custom:… IDs) |
Example:
{
"stack": ["Redis", "HAProxy", "Kubernetes"],
"translate": true,
"locale": "fa",
"enabledBuiltin": { "NVD": true, "TheHackerNews": true }
}
Response: ScanResponse — vulns[], summary, search_date, meta (sources_checked, sources_updated_at, duration_ms, mode: "full").
Each vulnerability includes id, tool, title, severity, optional affected_versions, fixed_version, sources[], url, patch_available, exploited_in_wild, optional epss (0–1) and riskScore (0–10) when EPSS enrichment is enabled, optional cwe[] and compliance_controls[] when compliance mapping is enabled, and optional translations / title_fa when enriched.
Scan sequence (integrator view)¶
sequenceDiagram
participant Client
participant API as Express API
participant Feeds as External feeds
Client->>API: POST /api/scan { stack, translate, locale }
API->>Feeds: Parallel NVD/OSV/GitHub/KEV/RSS
Feeds-->>API: Raw findings
API->>API: Merge, KEV enrich, optional translate
API-->>Client: 200 { vulns, summary, meta }
شرح نمودار¶
The client sends one JSON payload. The server fans out to configured sources, normalizes records into a common vulnerability shape, deduplicates by CVE ID, and applies optional translation for the first N items (controlled by TRANSLATE_MAX_ITEMS). The response includes timing metadata so clients can show progress or audit SLAs.
روال گامبهگام¶
- Call
GET /api/capabilitiesto readmaxStackToolsandtranslateLocales. - Optionally call
POST /api/scan/validatewith the same body you plan to scan. POST /api/scanwithstackand source options.- Store
vulnsandmeta.sources_updated_atif you need incremental UI badges. - On 429, back off and retry NVD-heavy scans less frequently.
Watch — POST /api/watch¶
Runs watch mode: OSV, GitHub, KEV, and RSS without NVD for lower latency. The body matches scan plus optional knownIds (CVE IDs already seen). The response adds newVulns and hasNew for alerting pipelines.
Example:
{
"stack": ["Redis"],
"knownIds": ["CVE-2024-1234", "CVE-2023-9999"],
"translate": false
}
Response: Same as scan plus newVulns[], hasNew: boolean, meta.mode: "watch".
When newVulns is non-empty and notification env vars are set, the server dispatches configured channels via NotificationService (does not affect the JSON response).
Translate — POST /api/translate¶
Batch-translates English title / description fields to a UI locale using MyMemory / LibreTranslate / Ollama (see Configuration). Used when the UI language changes after a scan or for items beyond server scan translation limits.
Request:
{
"locale": "ru",
"items": [
{
"id": "CVE-2024-1",
"tool": "Redis",
"title": "Heap buffer overflow",
"description": "…"
}
]
}
Response: { "locale": "ru", "items": [{ "id", "tool", "title", "description" }] }
Max items per request: TRANSLATE_BATCH_MAX (default 40).
Versioned API (/api/v1 only)¶
These routes exist only under /api/v1. Legacy /api/* covers scan, watch, validate, translate, and metadata aliases only.
Scan stream (SSE) — POST /api/v1/scan/stream¶
Same JSON body as full scan. Response is Server-Sent Events (text/event-stream): progress events during upstream fetches, then a final complete (with full scan JSON) or error event. Roles: admin, scanner.
Scan history — GET /api/v1/scans/history¶
Query limit (1–200, default 50). Returns { enabled, tenant, items[] } from PostgreSQL metadata. 503 with DATABASE_DISABLED when DATABASE_URL is unset. Roles: admin, scanner, viewer, auditor.
Scan trends — GET /api/v1/scans/trends¶
Query days (1–365, default 30). Returns { enabled, tenant, days, points[] }. Same database requirement and roles as history.
Tenants and saved stacks¶
| Method | Path | Roles | Description |
|---|---|---|---|
POST |
/api/v1/tenants |
admin |
Create tenant { slug, name } |
GET |
/api/v1/tenants/stacks |
read roles | List saved stacks for X-Tenant-Id |
POST |
/api/v1/tenants/stacks |
admin |
Create { name, tools, settings? } |
GET |
/api/v1/tenants/stacks/:id |
read roles | Get stack by UUID |
PUT |
/api/v1/tenants/stacks/:id |
admin |
Update stack |
DELETE |
/api/v1/tenants/stacks/:id |
admin |
Delete stack |
All tenant/stack routes return 503 when PostgreSQL is disabled.
Kubernetes discovery — GET /api/v1/discovery/kubernetes¶
Lists Deployment container images mapped to preset stack tools. Requires K8S_DISCOVERY_ENABLED=true. Roles: admin, scanner. 503 K8S_DISCOVERY_DISABLED when opt-in flag is off.
Endpoint summary¶
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Liveness + flags (?detailed=true for ops snapshot) |
| GET | /metrics |
Prometheus metrics (when enabled) |
| GET | /api/capabilities |
Limits and features |
| GET | /api/sources |
Built-in source catalog |
| GET | /api/openapi.json |
OpenAPI 3.1 spec |
| POST | /api/scan/validate |
Validate stack/sources only |
| POST | /api/scan |
Full scan |
| POST | /api/watch |
Watch poll + diff |
| POST | /api/translate |
Batch translation |
| POST | /api/v1/scan/stream |
Full scan over SSE (v1 only) |
| GET | /api/v1/scans/history |
Tenant scan history (Postgres) |
| GET | /api/v1/scans/trends |
Tenant scan trends (Postgres) |
| POST | /api/v1/tenants |
Create tenant (Postgres) |
| GET/POST | /api/v1/tenants/stacks |
List / create saved stacks |
| GET/PUT/DELETE | /api/v1/tenants/stacks/:id |
Stack CRUD by id |
| GET | /api/v1/discovery/kubernetes |
K8s image → tool discovery |
Legacy /api/* aliases exist for the core scan/metadata rows (not v1-only routes).
Security and deployment notes¶
The API is intended for trusted networks (localhost, internal VLAN, or authenticated reverse proxy). Optional shared-secret auth: set API_SECRET on the server; clients send X-Api-Key or Authorization: Bearer. Health probes (GET /api/health, GET /api/v1/health) stay open. Do not expose without TLS on the public internet. Rate limits are inherited from upstream providers (especially NVD); set NVD_API_KEY for higher throughput.
Related chapters¶
- Configuration — environment variables
- Architecture — server module layout
- Scan and watch — product semantics of modes
Try requests in the API Explorer tab.