Documentation
Send what's installed on a server. Get back which CVEs apply, whether a fix exists, what the vendor says, and what to do first.
Quick start
- Sign up with your email and click the link we send.
- In the dashboard, create an API key and copy it (shown once).
- On a Linux server (Ubuntu, Debian, RHEL, AlmaLinux or Rocky Linux), run:
export CVEFOCUS_KEY=cvf_live_…
{ cat /etc/os-release; uname -r; if command -v dpkg-query >/dev/null; then dpkg-query -W -f='${Package} ${Version} ${Architecture} ${source:Package}\n'; else rpm -qa --qf '%{NAME} %{EPOCHNUM}:%{VERSION}-%{RELEASE} %{ARCH} %{SOURCERPM}\n'; fi; } \
| curl -sS "https://api.cvefocus.com/v1/check?format=text" \
-H "Authorization: Bearer $CVEFOCUS_KEY" \
-H "Content-Type: text/plain" --data-binary @-Collecting an inventory
The command lists installed packages with their versions and source package, plus /etc/os-release and the running kernel. It is read-only and sends no configs, hostnames, IPs or secrets.
Any Linux
{ cat /etc/os-release; uname -r; if command -v dpkg-query >/dev/null; then dpkg-query -W -f='${Package} ${Version} ${Architecture} ${source:Package}\n'; else rpm -qa --qf '%{NAME} %{EPOCHNUM}:%{VERSION}-%{RELEASE} %{ARCH} %{SOURCERPM}\n'; fi; } > inventory.txtUbuntu / Debian
{ cat /etc/os-release; uname -r; dpkg-query -W -f='${Package} ${Version} ${Architecture} ${source:Package}\n'; } > inventory.txtRHEL / Alma / Rocky
{ cat /etc/os-release; uname -r; rpm -qa --qf '%{NAME} %{EPOCHNUM}:%{VERSION}-%{RELEASE} %{ARCH} %{SOURCERPM}\n'; } > inventory.txtWindows
# Coming soon: Windows support (OS build + installed programs).
macOS
# Coming soon: macOS support (sw_vers + Homebrew).
We also accept dpkg -l output, plain name version lines (we look up the source package for you), JSON, and Debian purls:
{
"os": { "id": "ubuntu", "version": "24.04" },
"packages": [
{ "name": "openssl", "version": "3.0.13-0ubuntu3.16", "source": "openssl" },
"pkg:deb/ubuntu/curl@8.5.0-2ubuntu10.6?distro=ubuntu-24.04"
]
}Supported now: Ubuntu 20.04 and later, Debian 11 and later, Red Hat Enterprise Linux 7–10, AlmaLinux and Rocky Linux 8–10. Next: Amazon Linux, Alpine, language packages, then Windows and macOS.
RHEL family today: results cover every CVE with a released security advisory (RHSA/ALSA/RLSA), with the fixed version and the dnf command. CVEs Red Hat has not fixed yet (affected, will not fix) are not listed for these systems yet; the response says so in warnings. Results are for standard RHEL minor releases, not EUS/AUS streams.
Calling the API
POST https://api.cvefocus.com/v1/check with header Authorization: Bearer cvf_live_…. Send the inventory as text/plain or application/json, up to 5 MB.
Responses are JSON, for scripts and integrations. In a terminal, add ?format=text for a readable report instead:
curl -sS "https://api.cvefocus.com/v1/check?format=text" -H "Authorization: Bearer $CVEFOCUS_KEY" --data-binary @inventory.txt
Or pretty-print the JSON with jq: … | jq .decisions.
curl
curl -sS https://api.cvefocus.com/v1/check \ -H "Authorization: Bearer $CVEFOCUS_KEY" \ -H "Content-Type: text/plain" \ --data-binary @inventory.txt
Python
import os, requests
resp = requests.post(
"https://api.cvefocus.com/v1/check",
headers={"Authorization": "Bearer " + os.environ["CVEFOCUS_KEY"],
"Content-Type": "text/plain"},
data=open("inventory.txt", "rb"),
timeout=60,
)
resp.raise_for_status()
for d in resp.json()["decisions"]:
print(d["priority"], d["title"])Node.js
import { readFile } from "node:fs/promises";
const res = await fetch("https://api.cvefocus.com/v1/check", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CVEFOCUS_KEY}`,
"Content-Type": "text/plain",
},
body: await readFile("inventory.txt"),
});
const result = await res.json();
for (const d of result.decisions) console.log(d.priority, d.title);PHP
<?php
$ch = curl_init("https://api.cvefocus.com/v1/check");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("CVEFOCUS_KEY"),
"Content-Type: text/plain",
],
CURLOPT_POSTFIELDS => file_get_contents("inventory.txt"),
]);
$result = json_decode(curl_exec($ch), true);
foreach ($result["decisions"] as $d) echo $d["priority"], " ", $d["title"], PHP_EOL;Ruby
require "net/http"
require "json"
uri = URI("https://api.cvefocus.com/v1/check")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("CVEFOCUS_KEY")}"
req["Content-Type"] = "text/plain"
req.body = File.read("inventory.txt")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
JSON.parse(res.body)["decisions"].each { |d| puts "#{d["priority"]} #{d["title"]}" }C#
using System.Net.Http.Headers;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("CVEFOCUS_KEY"));
var body = new StringContent(File.ReadAllText("inventory.txt"), System.Text.Encoding.UTF8, "text/plain");
var res = await http.PostAsync("https://api.cvefocus.com/v1/check", body);
res.EnsureSuccessStatusCode();
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
foreach (var d in doc.RootElement.GetProperty("decisions").EnumerateArray())
Console.WriteLine($"{d.GetProperty("priority")} {d.GetProperty("title")}");Go
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
f, _ := os.Open("inventory.txt")
defer f.Close()
req, _ := http.NewRequest("POST", "https://api.cvefocus.com/v1/check", f)
req.Header.Set("Authorization", "Bearer "+os.Getenv("CVEFOCUS_KEY"))
req.Header.Set("Content-Type", "text/plain")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var out struct {
Decisions []struct{ Priority, Title string } `json:"decisions"`
}
json.NewDecoder(res.Body).Decode(&out)
for _, d := range out.Decisions {
fmt.Println(d.Priority, d.Title)
}
}Java
import java.net.URI;
import java.net.http.*;
import java.nio.file.Path;
public class CveFocusCheck {
public static void main(String[] args) throws Exception {
var request = HttpRequest.newBuilder(URI.create("https://api.cvefocus.com/v1/check"))
.header("Authorization", "Bearer " + System.getenv("CVEFOCUS_KEY"))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("inventory.txt")))
.build();
var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}PowerShell
$headers = @{ Authorization = "Bearer $env:CVEFOCUS_KEY" }
$result = Invoke-RestMethod -Method Post -Uri "https://api.cvefocus.com/v1/check" -Headers $headers `
-ContentType "text/plain" -Body (Get-Content -Raw inventory.txt)
$result.decisions | Format-Table priority, titleOther endpoints: GET /v1/cve/{id} (any published CVE: description, severity, exploitation, the products the vendor reported, and each distribution's status), GET /v1/usage, and GET /v1/status (data freshness, no key needed). Full details in the API reference.
Reading the response
decisions: start here. One action per problem, most urgent first: reboot, upgrade a package, a package with no fix yet, or CVEs the vendor will not fix.findings: one row per CVE per affected package, with fix, severity, exploitation and vendor status.ok: products with no open CVEs at the installed version.status:ok, orneeds_infowith aquestionthat says exactly what's missing (for example, an unsupported OS).data_as_of: when each data source was last refreshed.
{
"status": "ok",
"os": { "id": "ubuntu", "version": "24.04", "kernel": "7.0.0-1012-aws" },
"summary": { "packages": 837, "products": 486, "affected_products": 3, "findings": 4,
"by_priority": { "act_now": 2, "this_week": 1, "plan": 0, "monitor": 1 } },
"decisions": [
{ "priority": "act_now",
"title": "Upgrade openssh to 1:9.6p1-3ubuntu13.3",
"detail": "Fixes 1 CVE. Known exploited: CVE-2024-6387. Highest exploit likelihood: CVE-2024-6387 (EPSS 0.93).",
"command": "sudo apt-get install --only-upgrade openssh-client openssh-server",
"package": "openssh", "cves": ["CVE-2024-6387"] }
],
"findings": [
{ "cve": "CVE-2024-6387", "package": "openssh", "binaries": ["openssh-client", "openssh-server"],
"installed": "1:9.6p1-3ubuntu13.2", "status": "affected", "priority": "act_now",
"fix": { "available": true, "version": "1:9.6p1-3ubuntu13.3", "requires": null, "command": "sudo apt-get …" },
"severity": { "cvss": 8.1, "vector": "CVSS:3.1/AV:N/AC:H/…", "distro_priority": "high" },
"exploited": { "kev": true, "kev_added": "2024-07-01", "kev_due": null, "ransomware": false },
"epss": { "score": 0.93, "percentile": 0.99 },
"vendor": { "name": "Ubuntu", "status": "fixed", "reason": null, "source": "vex" },
"summary": "A signal handler race condition was found in OpenSSH's server (sshd)…",
"url": "https://ubuntu.com/security/CVE-2024-6387" }
],
"ok": ["bash", "curl", "glibc", "…"],
"warnings": [],
"data_as_of": { "ubuntu": "2026-10-29T14:00:12Z", "ubuntu-vex": "…", "kev": "…", "epss": "…" },
"usage": { "products_today": 486, "products_limit": 500, "calls_today": 1, "plan": "free" }
}Priorities
Deterministic rules, no AI in the verdict path:
| Priority | When |
|---|---|
| act_now | On CISA's Known Exploited Vulnerabilities list, or EPSS ≥ 10% (likely to be exploited in the next 30 days). Also: a newer kernel is installed but not running. |
| this_week | CVSS ≥ 7, or the distribution rates it high or critical. |
| plan | CVSS ≥ 4, or the distribution rates it medium. |
| monitor | Everything else, including CVEs the vendor decided not to fix (unless exploited). |
Vendor status
Every finding carries what the vendor (here, the distribution) says, from its own VEX statements where available:
| vendor.status | Meaning |
|---|---|
fixed | A fixed build is released; fix.version says which. |
affected | Affected; the vendor intends to fix it but hasn't yet. |
under_investigation | The vendor hasn't finished triage. |
will_not_fix | The vendor reviewed it and decided not to fix it in this release; reason says why. |
unknown | RHEL module packages: the vendor fixed it only in a newer module stream (e.g. nodejs:24) and hasn't said whether your stream is affected. fix.requires is module-stream-switch; priority is capped at plan unless the CVE is known exploited. |
When the vendor says a package is not affected, the CVE doesn't appear in your results at all.
Limits
Plans are metered by distinct products per UTC day, counting only products we hold vulnerability data for. A product is a source package: openssl, libssl3t64 and libssl-dev count once. Packages no distribution has ever published a CVE for are free, and re-checking products you've already checked today is free, so hourly cron or every CI run costs nothing extra.
A typical Ubuntu server has 400–900 source packages, of which roughly a third have vulnerability data, so a server usually counts 150–350 products. The free plan includes 500 products per day. There is also a limit of 120 requests per minute per key. Every response includes usage, and summary.products_with_data shows what a request counts.
Errors
| Status | Meaning |
|---|---|
| 401 | Missing, unknown or revoked API key. |
| 413 | Inventory larger than 5 MB. |
| 422 | No packages found in the body; detail.hint explains. |
| 429 | Daily product limit reached (detail has the numbers) or more than 120 requests per minute. |
Daily checks
Save as /usr/local/bin/cvefocus-check:
#!/bin/sh
# /usr/local/bin/cvefocus-check — writes the latest result to /var/tmp/cvefocus.json
{ cat /etc/os-release; uname -r; if command -v dpkg-query >/dev/null; then dpkg-query -W -f='${Package} ${Version} ${Architecture} ${source:Package}\n'; else rpm -qa --qf '%{NAME} %{EPOCHNUM}:%{VERSION}-%{RELEASE} %{ARCH} %{SOURCERPM}\n'; fi; } \
| curl -sS --fail-with-body https://api.cvefocus.com/v1/check \
-H "Authorization: Bearer $(cat /etc/cvefocus.key)" \
-H "Content-Type: text/plain" --data-binary @- > /var/tmp/cvefocus.jsonThen:
sudo chmod 755 /usr/local/bin/cvefocus-check sudo sh -c 'umask 077; printf %s "cvf_live_YOUR_KEY" > /etc/cvefocus.key' echo '15 6 * * * root /usr/local/bin/cvefocus-check' | sudo tee /etc/cron.d/cvefocus
Privacy
We never store your inventories, results, IP addresses or request logs. See exactly what we store.