| Version | Supported |
|---|---|
| Latest (main) | Yes |
| Previous minor | Yes (for 90 days) |
| Older | No |
Please do not report security vulnerabilities via public GitHub Issues.
If you discover a security vulnerability in this project, open a GitHub Security Advisory or contact the maintainer directly via GitHub.
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Any suggested fixes (optional)
You will receive an acknowledgment within 48 hours and a resolution timeline within 7 days.
Please give us a reasonable chance to fix the issue before disclosing it publicly. We are happy to credit you in the release notes if you would like that.
In scope: this repository's code, including the desktop app, the cve-intel CLI, the MCP server and the REST API. Out of scope: vulnerabilities in the third-party services we query (NVD, OpenAI, Anthropic, Google, …) — report those to the vendor.
Never include real API keys in a report. If you think one of your keys was exposed, revoke it with the provider first.
By default the Rust application makes outbound HTTPS requests only to known security APIs (plus model/runtime downloads the user explicitly starts). It does not:
- Open inbound network ports (the optional local model server and
cve-intel servebind to127.0.0.1by default) - Execute code from external sources
- Store API keys in logs or cache, or expose them to the desktop frontend
- Query private/internal IP addresses via external APIs (blocked at the validation layer, including IPv4-mapped IPv6)
- Ever issue MCP
sampling/createMessagerequests (the server never asks a host LLM to generate on its behalf)
The desktop app can refresh the local watchlist in the background while it is open (every 6 hours by default; configurable or off in Settings). That task only makes outbound HTTPS requests to the public sources needed for the items you watch (NVD, OSV, EPSS, CISA KEV, ENISA EUVD, exploit indexes). Nothing runs while the app is closed. The MCP and REST interfaces can read the watchlist but cannot add, remove or refresh items.
The optional keys for sources such as NVD, GitHub, VirusTotal, Shodan and AbuseIPDB can be supplied two ways: environment variables / a .env file, or the desktop Settings → Data source API keys screen. A key saved in the app wins over the environment.
- Keys saved in the app go into the embedded SQLite database
~/.cve-intel/settings.db(or$CVE_INTEL_HOME/settings.db), tablesource_keys. The file is created with owner-only permissions (0600) on Unix before SQLite writes to it, andsecure_deleteis on so a removed key is overwritten. They are stored in plain text: not encrypted, and not in the OS keychain. Anyone who can read your user account's files or a backup of them can read them. Prefer environment variables on shared machines, servers and in Docker, and use keys with the lowest privileges and limits the provider offers (for example, a GitHub token with no scopes). - The same database is read by the CLI, the MCP server and the REST API on that machine, so a key saved in the desktop app applies to them too.
- The UI is only ever told whether a key is set and where it came from (app or environment), never its value. Keys are never logged, cached or included in any tool, REST or MCP response, and
Config'sDebugoutput is redacted. Values are validated as printable ASCII before storage so they cannot inject HTTP headers. - "Get a key" buttons open the system browser, but the frontend can only pass an id; the URL comes from a fixed list compiled into the app, so the UI cannot be used to open arbitrary links.
- The settings database is separate from the lookup cache, so "Clear cache" never touches your keys. Remove next to a key deletes it.
Vendors, products, packages and CVEs you follow are stored in
~/.cve-intel/watch.db (or $CVE_INTEL_HOME/watch.db), created
owner-only (0600 on Unix) before SQLite writes to it, with
secure_delete on. It is not encrypted. It holds the items you added,
a snapshot of each tracked CVE's signals (KEV, EPSS, CVSS, exploit
evidence) and the change events those snapshots produced.
- Separate from the lookup cache and from
settings.db, so Clear cache never removes the watchlist and removing a key never removes your items. - Only the desktop app and
cve-intel watch add|remove|refreshchange it. MCP tools (list_watchlist,get_watchlist_changes) andGET /api/v1/watchare read-only. - The first refresh of an item records a silent baseline. Later refreshes write events (new CVE, added to KEV, EPSS jump, and so on). The feed keeps the newest 5,000 events.
Scans saved by the desktop app (every scan made there) and by
cve-intel scan … --save are stored in ~/.cve-intel/scans.db (or
$CVE_INTEL_HOME/scans.db), created owner-only (0600 on Unix) before
SQLite writes to it, with secure_delete on. On Windows the file inherits
your profile directory's permissions. It is not encrypted. It holds,
per target, the folder and file paths that were scanned, and per scan the
findings: package names and versions, advisory IDs and their KEV, EPSS,
exploit and risk signals.
- Pasted text is never stored. A pasted list is identified by a hash of the packages in it, so scanning the same list again shows what changed.
- Scan again re-reads only the folders and files of a saved target, the ones you dropped or chose before; nothing else on disk is read.
- The MCP tools
list_scansandget_scan_findings, andGET /api/v1/scans…, are read-only and serve local callers only: MCP over stdio, and the server the desktop app starts (which refuses requests from web pages).cve-intel serveallows any origin, so it does not serve saved scans at all. No tool can start, save or forget a scan. - Forget in the app, or
cve-intel scans forget <key>, deletes a target and every scan of it. Each target keeps its newest 30 scans, and the newest 200 targets are kept. - Monitor on a target is your standing consent to scan it again in the
background, once a day, while the desktop app is open; nothing runs while
it is closed. A monitored exposure check spends your Shodan quota each
time. At most 20 targets can be monitored; untick Monitor or
Forget the target to stop. What a background scan finds new or
riskier is written to the watchlist feed (
watch.db).
What each kind of scan sends where:
| Scan | Read on your machine | Sent to |
|---|---|---|
| Dependencies | The lockfiles, manifests and SBOMs you drop, choose or paste | OSV.dev: package names and versions. FIRST EPSS and CISA KEV: CVE IDs. Exploit-DB and Metasploit indexes are downloaded whole, so nothing is sent |
| Public exposure | Nothing on your machine | crt.sh: your domain. Your DNS resolver: the names found. Shodan or Shodan InternetDB: the public addresses you leave ticked (private and reserved addresses are never sent). NVD: product and version of the software seen there. Nothing connects to the targets |
| Container image | The image file or OCI folder you drop or choose, read as a stream and never unpacked to disk. Only os-release, the package databases and dependency files are kept, in memory, with size limits | OSV.dev: package names and versions |
| This computer | Windows: the Uninstall registry keys and the Windows build. macOS: Info.plist of apps in /Applications and ~/Applications, the Homebrew Cellar, the macOS version. Never Desktop, Documents, Downloads or Library |
NVD: vendor, product and version of recognised apps and the OS. Only with look up unrecognised apps ticked: the names of other apps, to NVD's product dictionary. Nothing is sent by List installed software |
The desktop app records what is done in it so you can look back later: lookups and their inputs, Ask questions and answers, scans (including the pasted dependency list), comparisons, model and settings changes, and the tool calls, REST calls and refused web-page requests on the MCP server it hosts. Each entry also lists the upstream hosts its work contacted.
- Stored in
~/.cve-intel/activity.db(or$CVE_INTEL_HOME/activity.db), created owner-only (0600on Unix) before SQLite writes to it, withsecure_deleteon. It is not encrypted, and it contains the indicators you investigated, so treat it as you would your shell history. On Windows the file inherits your profile directory's permissions. - API key values are never passed to the log. Saving or removing a key records only its name, and changing a cloud provider records only whether a new key was saved. Upstream requests are stored as host and path only, because some sources (Shodan) take their key as a query parameter. Error messages have URL query strings stripped and key-like tokens redacted, since a rejected cloud key is echoed back partly masked.
- Activity → Record activity pauses recording, and Clear log deletes every entry and compacts the file. Pausing, resuming and clearing are each noted in the log, so gaps are explained. Only the newest 20,000 entries are kept.
- Only the desktop app records anything.
cve-intel serve,cve-intel mcpand the other CLI commands write no activity log.
The Ask feature can use a hosted model instead of the on-device one. This is off by default and only active after the user selects a provider in Settings (or passes --provider on the CLI). Be aware of what that changes:
Data leaves the machine. While a cloud provider is selected, each question and the intelligence-tool results gathered for it (CVE details, plus any IP address, file hash or domain the user asked about) are sent to that provider and are subject to its terms and data-retention policy. The desktop app shows a banner in Ask AI whenever this is on. If you analyse sensitive indicators, keep the private local model selected.
How keys are handled:
- Keys come from the
OPENAI_API_KEY/ANTHROPIC_API_KEY/GEMINI_API_KEYenvironment variables, or from the Settings screen. The environment variable is the safer option because nothing is written to disk. - A key saved in Settings is stored in plain text in the same embedded SQLite database described above (
settings.db, tableproviders), with the same owner-only permissions andsecure_delete. It is not encrypted and not in the OS keychain: anyone who can read your user account's files, or a backup of them, can read the key. On Windows the file inherits your profile directory's permissions. - The key is never returned to the UI (the frontend only learns whether a key exists and where it came from), never written to logs, the lookup cache or MCP/REST responses, and is redacted from debug output. Keys are validated (printable ASCII, no whitespace) before use so they cannot inject HTTP headers.
- Settings are kept in a separate database from the lookup cache, so "Clear cache" does not touch them. Remove saved key in Settings deletes the key.
- Use API keys with a spending limit, and rotate any key you suspect was exposed.
Where requests go: each provider's base URL is a compiled-in HTTPS constant. A user can choose the model name but cannot point a key at an arbitrary host. Hosted-API requests have a 120-second timeout.
The REST API does not use cloud AI. POST /api/v1/ask only ever uses the local / CVE_LLM_BASE_URL model, so a web page or another local process cannot make the server spend your cloud key. The MCP server never calls a model at all.
Model output is untrusted. Whatever model answers, treat its text as advice to verify, not as a verdict. The structured evidence shown next to each answer (risk score, KEV, EPSS, PoC) comes from the data sources, not the model.
For full details see the Architecture section of the README.