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)¶
- User clicks Full scan; client sends
stackandtranslateflag. - Server validates stack array and starts timers.
- CISA KEV JSON, GitHub advisory pages (cached), and RSS feeds load in parallel.
- For each tool, NVD/OSV/GitHub run with look-back
SCAN_DAYS. mergeVulnerabilitiesdeduplicates and mergessourcestags.enrichWithKevmarks exploited CVEs.- If
translate: true, Persian fields may be filled on scan; other locales can be requested later via/api/translate. - Summary counts and vuln array return; client persists via
src/lib/scanCache.ts(stackKeymust 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)¶
useWatchhook fires on interval (2/5/15 min from settings).- Client sends current CVE ids as
knownIds. - Server fetches OSV/GitHub/RSS only.
- New ids not in
knownIdspopulatenewVulns; banner/toast if alerts enabled. - 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 /scanandPOST /watchshare separate in-memory per-IP buckets (RATE_LIMIT_SCAN_PER_MINdefault 12,RATE_LIMIT_WATCH_PER_MINdefault 120). Response:code: "RATE_LIMITED",retryAfterSec.POST /scan/validateis 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