Skip to content

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 (/apilocalhost: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 /scan and POST /watch use separate minute buckets.
  • POST /scan/validate is exempt (safe for CI and stack checks).
  • Tune via RATE_LIMIT_SCAN_PER_MIN and RATE_LIMIT_WATCH_PER_MIN in .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 needs id and url.
  • 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: ScanResponsevulns[], 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.

روال گام‌به‌گام

  1. Call GET /api/capabilities to read maxStackTools and translateLocales.
  2. Optionally call POST /api/scan/validate with the same body you plan to scan.
  3. POST /api/scan with stack and source options.
  4. Store vulns and meta.sources_updated_at if you need incremental UI badges.
  5. 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.

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.

Try requests in the API Explorer tab.