Skip to content

Scan and watch

Scanning is the core loop of CVE Intelligence Panel. This chapter contrasts full scan and watch, documents server behavior, and provides diagrams for operators.

Why two modes exist

NVD offers broad coverage but enforces rate limits. Polling NVD every two minutes would stall or fail. Watch therefore skips NVD and queries faster sources. Full scan is for complete inventory when latency is acceptable.

Operators typically run a full scan daily (or on demand) and keep watch enabled between scans.

Mode comparison

Aspect Full scan Watch
Endpoint POST /api/scan POST /api/watch
NVD Yes No
KEV prefetch Yes Yes (for flags)
Client knownIds Not required Dedupes “new” findings
meta.mode full watch

Full scan flow

flowchart LR
  classDef ui fill:#e9edf5,stroke:#00baba,color:#253343
  classDef api fill:#f3fcfc,stroke:#008c8c,color:#253343
  classDef ext fill:#fff7ed,stroke:#eda232,color:#253343

  UI[Browser]:::ui --> S[POST /api/scan]:::api
  S --> KEV[Load KEV set]:::api
  S --> GH[GitHub cache]:::api
  S --> RSS[RSS feeds]:::api
  S --> Loop[Per stack tool]:::api
  Loop --> NVD[NVD query]:::ext
  Loop --> OSV[OSV query]:::ext
  Loop --> GHA[GitHub advisories]:::ext
  S --> M[merge + KEV enrich]:::api
  M --> T[Optional translate]:::api
  T --> R[JSON response]:::api

Diagram walkthrough

The server loads shared catalogs once, then for each tool queries NVD (full mode), OSV, and GitHub in parallel and attaches RSS keyword matches. Results merge by CVE id; KEV membership sets exploited_in_wild. Optional translation fills translations / title_fa. meta.sources_updated_at records successful fetch times per source.

Step-by-step (full scan)

  1. User clicks Full scan; client sends stack and translate flag.
  2. Server validates stack array and starts timers.
  3. CISA KEV JSON, GitHub advisory pages (cached), and RSS feeds load in parallel.
  4. For each tool, NVD/OSV/GitHub run with look-back SCAN_DAYS.
  5. mergeVulnerabilities deduplicates and merges sources tags.
  6. enrichWithKev marks exploited CVEs.
  7. If translate: true, Persian fields may be filled on scan; other locales can be requested later via /api/translate.
  8. Summary counts and vuln array return; client persists via src/lib/scanCache.ts (stackKey must match current stack).

Client scan cache

The browser stores the last successful scan in cve-radar:last-scan with a stack key (normalized, sorted tool names). On reload, loadLastScan restores vulns and summary when the key matches; otherwise the cache is ignored. persistLastScan runs after full scan and when watch returns new items.

This avoids a mandatory full scan on every browser session while keeping results scoped to the active stack.

Watch sequence

sequenceDiagram
  participant B as Browser
  participant A as Express API
  participant O as OSV
  participant G as GitHub
  participant R as RSS

  B->>A: POST /api/watch {stack, knownIds}
  A->>O: Query per tool
  A->>G: Advisory cache
  A->>R: Feed parse
  A->>A: Merge + diff vs knownIds
  A-->>B: newVulns, hasNew, meta

Step-by-step (watch)

  1. useWatch hook fires on interval (2/5/15 min from settings).
  2. Client sends current CVE ids as knownIds.
  3. Server fetches OSV/GitHub/RSS only.
  4. New ids not in knownIds populate newVulns; banner/toast if alerts enabled.
  5. Client merges into local vuln list and updates sources_updated_at.

GitLab and Linux distribution feeds

Beyond NVD/OSV/GitHub/RSS, full scan can query GitLab Advisory Database (GraphQL) and several Linux distribution security feeds when enabled in source settings or via environment variables.

Source Role
GitLab Package advisories (npm, PyPI, Maven, Go, …) — skipped when AIRGAPPED=true unless GITLAB_ADVISORY_MIRROR_URL is set
Alpine secdb JSON per release (ALPINE_RELEASES)
Ubuntu USN Prefetched CVE→package map from Ubuntu security JSON
Red Hat Enrichment pass (CVE metadata, not standalone rows)
Debian Security Tracker (DEBIAN_ENABLED or DEBIAN_TRACKER_CACHE_PATH for air-gap)
Amazon Linux ALAS updateinfo XML
MITRE CVE Optional JSON 5.x enrichment when MITRE_CVE_ENABLED=true

Package-level matching uses shared/distroPackages.ts (stack tool → distro package name). Distro findings dedupe with NVD/OSV on CVE id; sources_failed records partial feed errors like other built-ins.

Notes and exceptions

  • Server rate limit (429): POST /scan and POST /watch share separate in-memory per-IP buckets (RATE_LIMIT_SCAN_PER_MIN default 12, RATE_LIMIT_WATCH_PER_MIN default 120). Response: code: "RATE_LIMITED", retryAfterSec. POST /scan/validate is not limited. Multiple tabs or React StrictMode in dev can trigger watch limits — close extra tabs or tune env vars.
  • Without NVD_API_KEY, large stacks may hit HTTP 403/429 from NVD upstream — retry later or add a key (different from app rate limit).
  • Periodic auto full scan (30 min) is optional in settings; independent of watch interval.
  • Scan progress bar in the header reflects client-side progress estimation during long scans.

Next: i18n & translation