Command-line client for the ZEN SecDB API.
go install github.com/giterlizzi/secdb-cli@latestOr clone and build locally (requires Go 1.26+):
git clone https://github.com/giterlizzi/secdb-cli
cd secdb-cli
make buildPre-built binaries for Linux, macOS and Windows (amd64/arm64) are published on the Releases page via GoReleaser.
| Environment variable | Purpose |
|---|---|
SECDB_API_KEY |
API key sent as the X-API-KEY header on every request |
SECDB_DEBUG |
Set to any value to enable debug logging to stderr (same as --debug) |
SECDB_NO_UPDATE_CHECK |
Set to any value to disable the background update check |
NO_COLOR |
Print raw Markdown instead of ANSI-styled text output |
CI |
Automatically disables the background update check when set |
--base-url overrides the API endpoint (default: https://secdb.nttzen.cloud/).
secdb cve CVE-2021-44228Renders a curated, human-readable report (CVSS v2/v3/v4, SSVC, EPSS, CISA KEV status, weaknesses, exploit maturity, affected vendors/products and advisories) as Markdown, syntax-highlighted in an interactive terminal.
Supports -o / --output:
| Format | Description |
|---|---|
text (default) |
Curated Markdown report, rendered with ANSI styling in a terminal, printed raw when piped/redirected |
yaml |
Raw API response as YAML |
json |
Raw API response as JSON |
template |
Custom Go template via --template (inline) or --template-file |
html |
Custom HTML via --template/--template-file, rendered with html/template (safe escaping) |
sarif |
SARIF 2.1.0 report (audit commands only, see below) |
csv |
CSV of the per-advisory audit details, one row per advisory (audit commands only, see below) |
secdb cve CVE-2021-44228 -o json
secdb cve CVE-2021-44228 -o template --template '{{.severity}}: {{.score}}'Templates have access to Sprig functions (string manipulation, math, lists, dates, ...) in addition to the Go template built-ins. The env, expandenv, and getHostByName functions are disabled to prevent untrusted templates from reading environment variables (e.g. SECDB_API_KEY) or exfiltrating data over the network.
Simple
secdb audit purl pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1From file
secdb audit purl --file=purls.txtFrom STDIN
secdb audit purl < purls.txtFrom pipe
command | secdb audit purlUsing CycloneDX SBOM file (JSON)
syft packages dir:. -o cyclonedx-json > bom.json && secdb audit purl --sbom bom.json
cdxgen -o bom.json . && secdb audit purl --sbom bom.jsonCI
Useful in CI pipelines to fail the build when high/critical vulnerabilities are found.
secdb audit purl --sbom bom.json --fail-on=highSARIF report (e.g. for GitHub Code Scanning)
secdb audit purl --sbom bom.json --output=sarif > results.sarifProduces a SARIF 2.1.0 report - one rule/result per (advisory, affected package) pair, with severity, CVEs, CWEs and a CVSS-derived security-severity score. The artifact location in the report comes from --sbom, so pair --output=sarif with --sbom for a meaningful report; without --sbom the artifact location is left empty. A finding matched by --ignore-file is still included in the report, but carries a SARIF suppressions entry (kind: external, status: accepted, with the rule's reason as justification), so consumers like GitHub Code Scanning don't open a new alert for it.
CSV report (for spreadsheets)
secdb audit purl --sbom bom.json --output=csv > report.csvEmits one row per advisory with the columns ID, Title, Severity, CVSS, CVEs, CWEs, Packages, URL, Ignored, Ignore Reason. The list columns (CVEs, CWEs, packages) are flattened into a single cell each, joined by "; ", and every text field is quoted per RFC 4180 so commas and quotes in titles/reasons don't break the columns. Like sarif, the csv output always uses the details shape (the --view flag doesn't affect it) and is only supported by the audit commands.
Tips: The layout is a Go template, so if you need different columns you can supply your own template instead: --output=template --template-file my-csv.tmpl.
Ignoring accepted-risk findings
secdb audit purl --sbom bom.json --fail-on=high --ignore-file=/path-of/.secdbignore--ignore-file (default: .secdbignore) points to a YAML file of accepted-risk rules. A matching rule never hides a finding from the report; it only excludes it from the --fail-on exit-code check (and, for --output=sarif, marks the result as suppressed instead of removing it):
ignore:
- vulnerability: CVE-2021-44228
reason: "Not reachable in our usage of this library"
- vulnerability: CVE-2024-33333
reason: "Fixed upstream, upgrade planned"
package:
name: some-package
version: 1.0.0 # optional: without it, the rule matches every version of the package
expires: 2026-12-31 # optional: rule stops applying after this date (inclusive)A rule matches on vulnerability (advisory ID or CVE) and, optionally, narrows to a specific package.name/package.version. It's a no-op if the audit result doesn't already have a matching, non-expired rule.
Showing vulnerabilities with no available fix
An advisory can affect a package for which no fix has been released yet (CSAF remediation status none_available). By default these "unfixed" findings are hidden from every view (summary, details, sarif, csv) and excluded from the --fail-on check, so the report focuses on actionable vulnerabilities. When any are hidden, the --output=text header shows a warning row with their count:
Unfixed: ⚠️ 98 hidden (run with --show-unfixed to list them)
Pass --show-unfixed to include them; in the details view each such advisory is marked Fix: ❌ No fix available for the affected package.
secdb audit purl --sbom bom.json --show-unfixedThe --output=text report (both --view modes) is preceded by a short metadata header: the input source (arguments / --file / stdin / --sbom) and the number of PURLs scanned. The header is text-only; it never appears in json/yaml/sarif output.
Package URLs (PURLs) can be passed as arguments, read from a file with --file/-f (one PURL per line, # for comments), from CycloneDX --sbom file, or piped via stdin.
| Flag | Description |
|---|---|
-f, --file |
Read PURLs from a file instead of arguments/stdin |
--sbom |
Read PURLs from CycloneDX SBOM file (JSON) instead of arguments/stdin/file |
-v, --view |
summary (default), one row per package, or details, one row per advisory (only applies to --output=text) |
--fail-on |
Exit with status 2 if any package has a vulnerability at or above the given severity (critical, high, medium, low, info) |
--ignore-file |
YAML file of accepted-risk rules that exclude matching findings from --fail-on (default .secdbignore) |
--show-unfixed |
Also report vulnerabilities that have no fix available (hidden by default) |
Parse a project's dependency manifest, resolve its packages to PURLs, and audit them against ZEN SecDB. The format is detected from the file name.
secdb audit manifest --file go.mod
secdb audit manifest --file package-lock.json
secdb audit manifest --file requirements.txt --view details
secdb audit manifest --file Gemfile.lock --fail-on=high
secdb audit manifest --file pom.xml
secdb audit manifest --file composer.lock| Ecosystem | Files |
|---|---|
| Go | go.mod |
| npm | package-lock.json, yarn.lock |
| Python | requirements*.txt |
| Ruby | Gemfile.lock |
| Java (Maven) | pom.xml |
| PHP (Composer) | composer.lock |
For Python range specifiers that aren't an exact version (>=2.28), the leading version is audited; entries with no resolvable version (unpinned Python requirements, Maven versions supplied by a parent POM or an imported BOM, Composer platform requirements like php/ext-*) are skipped. The npm parser uses the lockfiles' resolved versions, so npm findings reflect what's actually installed. The Maven parser reads a single pom.xml and resolves ${...} properties and versions declared in <dependencyManagement>, but does not follow parent POMs or transitive dependencies. Results are shaped and rendered exactly like audit purl: --view, --fail-on, --ignore-file, --show-unfixed and --output=sarif/--output=csv all behave the same way.
| Flag | Description |
|---|---|
-f, --file |
(required) Path to the dependency manifest to audit |
-v, --view |
summary (default) or details (only applies to --output=text) |
--fail-on |
Exit with status 2 at or above the given severity |
--ignore-file |
YAML file of accepted-risk rules (default .secdbignore) |
--show-unfixed |
Also report vulnerabilities that have no fix available (hidden by default) |
Support for more manifest formats can be added over time.
secdb lsp starts a Language Server that audits dependency manifests as you open and edit them, reporting known vulnerabilities inline as editor diagnostics, each linking to its ZEN SecDB advisory. It reuses the same engine as audit manifest, so it recognizes the same files (go.mod, package-lock.json, yarn.lock, requirements*.txt, Gemfile.lock, pom.xml, composer.lock) and honors the same SECDB_API_KEY and --base-url configuration.
The server speaks JSON-RPC over stdin/stdout and is meant to be launched by an editor's LSP client, not run by hand (in a plain terminal it just waits for input). It debounces edits, so it audits shortly after you stop typing rather than on every keystroke. Set SECDB_DEBUG=1 (or pass --debug) to log to stderr.
Kate (Settings > LSP Client > User Server Settings)
{
"servers": {
"secdb": {
"command": ["secdb", "lsp"],
"commandDebug": ["secdb", "lsp", "--debug"],
"rootIndicationFileNames": ["go.mod", "package-lock.json", "requirements.txt", "Gemfile.lock", "pom.xml", "composer.lock"],
"highlightingModeRegex": "^(Go|JSON|Python|Ruby|XML)$"
}
}
}Sublime Text (Preferences > Package Settings > LSP > Settings, requires the LSP package)
{
"clients": {
"secdb": {
"enabled": true,
"command": ["secdb", "lsp"],
"selector": "source.go-mod | source.json | text.plain | text.xml | text.xml.dtd"
}
}
}Neovim (0.10+, native LSP client, no plugin)
Neovim has a built-in LSP client. Since secdb isn't a preconfigured server, start it from an autocommand keyed on the manifest file names (this sidesteps filetype detection, which is inconsistent for yarn.lock/Gemfile.lock). Add to your init.lua:
vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, {
pattern = {
"go.mod", "package-lock.json", "yarn.lock",
"requirements*.txt", "Gemfile.lock", "pom.xml",
"composer.lock",
},
callback = function(args)
vim.lsp.start({
name = "secdb",
cmd = { "secdb", "lsp" },
root_dir = vim.fs.root(args.buf, { ".git", "go.mod", "package.json", "pom.xml" }),
})
end,
})secdb must be on your PATH. vim.lsp.start reuses one server per project root, and Neovim shows the reported vulnerabilities as diagnostics automatically.
Zed (companion extension)
Unlike Kate and Sublime, Zed can't point at an arbitrary LSP binary from its settings: a language server must be provided by an extension. Install the companion secdb Zed extension and, once enabled, Zed starts secdb lsp automatically on the recognized manifests (make sure secdb is on your PATH).
A few files aren't recognized as a distinct language by every editor, so the server may not start on them out of the box: Sublime scopes go.mod as text.xml.dtd (hence the entry in the selector above), and requirements.txt, Gemfile.lock and yarn.lock are plain text. The server itself detects the format from the file name regardless; it's only the editor's trigger that needs the scope/language hint.
Extract the PURLs from a CycloneDX BOM (JSON) and audit them against ZEN SecDB. This is a convenience front-end for audit purl --sbom: the two produce identical output.
secdb audit sbom --file bom.json
# generate then audit
syft packages dir:. -o cyclonedx-json > bom.json && secdb audit sbom --file bom.json
cdxgen -o bom.json . && secdb audit sbom --file bom.json
# CI (fail on high or critical)
secdb audit sbom --file bom.json --fail-on=high
# SARIF (e.g. for GitHub Code Scanning)
secdb audit sbom --file bom.json --output=sarif > results.sarifThe PURLs are collected from the BOM's components (recursively). Results are shaped and rendered exactly like audit purl: --view, --fail-on, --ignore-file, --show-unfixed and --output=sarif/--output=csv all behave the same way.
| Flag | Description |
|---|---|
-f, --file |
(required) Path to the CycloneDX SBOM (JSON) to audit |
-v, --view |
summary (default) or details (only applies to --output=text) |
--fail-on |
Exit with status 2 at or above the given severity |
--ignore-file |
YAML file of accepted-risk rules (default .secdbignore) |
--show-unfixed |
Also report vulnerabilities that have no fix available (hidden by default) |
Audits the installed packages of a Linux host against ZEN SecDB. By default it audits the local machine (local auditing is only supported on Linux); it can also target a remote host over SSH. To audit a Docker image or container, use audit docker.
Local system
secdb audit linuxRemote host over SSH
secdb audit linux --host server.example.com --user opsUses your system ssh client, so ~/.ssh/config, the SSH agent and known_hosts all apply (host-key checking stays enabled). Use --port, --identity-file, or --ssh-config to override.
The command runs only fixed, read-only commands on the target: reading /etc/os-release, uname -m, and the distribution's package-list command (dpkg-query / rpm / apk / Slackware /var/log/packages). Supported distributions include Debian/Ubuntu, RHEL/Rocky Linux/AlmaLinux/Oracle Linux/Amazon Linux/Fedora/SUSE, Alpine Linux and Slackware Linux.
--view, --fail-on, --output=sarif/--output=csv, --ignore-file and --show-unfixed work exactly as for audit purl. The --output=text report is preceded by a metadata header showing the target (local, user @ host:port, or the Docker image/container), OS/version, architecture, and packages scanned. Progress lines (Detected ..., Auditing ...) are written to stderr only when it's a terminal, so piped/redirected output stays clean.
| Flag | Description |
|---|---|
--host |
Audit a remote host over SSH (default: local machine) |
--user, --port |
SSH user and port |
--identity-file |
SSH identity (private key) file |
--ssh-config |
SSH config file (when set, host-key policy is left to it) |
--sudo |
Prefix the package-list command with sudo -n |
-v, --view |
summary (default) or details (only applies to --output=text) |
--fail-on |
Exit with status 2 at or above the given severity |
--ignore-file |
YAML file of accepted-risk rules (default .secdbignore) |
--show-unfixed |
Also report vulnerabilities that have no fix available (hidden by default) |
Audits the installed packages of a Docker image or container. Provide exactly one of --image (run the package-list command in an ephemeral docker run --rm container) or --container (exec it in a running container). The docker CLI must be available and able to reach the daemon.
secdb audit docker --image debian:12
secdb audit docker --container my-running-containerThe same read-only collection, distribution support, and --view / --fail-on / --output=sarif / --output=csv / --ignore-file / --show-unfixed behavior as audit linux apply.
| Flag | Description |
|---|---|
--image |
Audit a local Docker image (run ephemerally) |
--container |
Audit a running local Docker container |
-v, --view |
summary (default) or details (only applies to --output=text) |
--fail-on |
Exit with status 2 at or above the given severity |
--ignore-file |
YAML file of accepted-risk rules (default .secdbignore) |
--show-unfixed |
Also report vulnerabilities that have no fix available (hidden by default) |
Stakeholder-Specific Vulnerability Categorization (SSVC), per the CISA methodology, combines a CVE's exploitation status and technical impact (from ZEN SecDB) with stakeholder-supplied context to produce an actionable decision: track, track*, attend, or act.
Simple
secdb ssvc calculate CVE-2021-44228 --mission-prevalence essential --public-well-being-impact materialBulk, multiple CVEs
secdb ssvc calculate CVE-2021-44228 CVE-2023-4863 --mission-prevalence support --public-well-being-impact minimalFrom file
secdb ssvc calculate --file cves.txt --mission-prevalence support --public-well-being-impact minimalFrom STDIN
secdb ssvc calculate --mission-prevalence support --public-well-being-impact minimal < cves.txtCVE identifiers can be passed as arguments, read from a file with --file/-f (one CVE per line, # for comments), or piped via stdin (same precedence as audit purl: arguments, then --file, then stdin). Duplicate CVEs are deduplicated; a CVE that can't be found still appears in the report with its status instead of failing the whole batch.
| Flag | Description |
|---|---|
-f, --file |
Read CVEs from a file instead of arguments/stdin |
--mission-prevalence |
(required) minimal, support, or essential |
--public-well-being-impact |
(required) minimal, material, or irreversible |
secdb check-update # alias: secdb updateA lightweight background check also runs automatically on every command (cooldown: 24h, silent on failure, skipped in CI or with SECDB_NO_UPDATE_CHECK set).
Third-party dependency attributions are listed in THIRD-PARTY-NOTICES.md.
