Skip to content

Security: user17286439/cve-intel

Security

SECURITY.md

Security Policy

Supported versions

Version Supported
Latest (main) Yes
Previous minor Yes (for 90 days)
Older No

Reporting a vulnerability

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.

Security design notes

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 serve bind to 127.0.0.1 by 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/createMessage requests (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.

API keys for data sources

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), table source_keys. The file is created with owner-only permissions (0600) on Unix before SQLite writes to it, and secure_delete is 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's Debug output 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.

Watchlist (watch.db)

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|refresh change it. MCP tools (list_watchlist, get_watchlist_changes) and GET /api/v1/watch are 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.

Scan history (scans.db)

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_scans and get_scan_findings, and GET /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 serve allows 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

Activity log (desktop app)

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 (0600 on Unix) before SQLite writes to it, with secure_delete on. 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 mcp and the other CLI commands write no activity log.

Optional cloud AI (OpenAI, Anthropic, Google Gemini)

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_KEY environment 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, table providers), with the same owner-only permissions and secure_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.

There aren't any published security advisories