Skip to content

Repository files navigation

ZEN SecDB CLI

CI release GitHub release License

Command-line client for the ZEN SecDB API.

Installation

go install github.com/giterlizzi/secdb-cli@latest

Or clone and build locally (requires Go 1.26+):

git clone https://github.com/giterlizzi/secdb-cli
cd secdb-cli
make build

Pre-built binaries for Linux, macOS and Windows (amd64/arm64) are published on the Releases page via GoReleaser.

Configuration

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/).

Usage

Look up a CVE

secdb cve CVE-2021-44228

Renders 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.

secdb cve example output

Output formats

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.

Audit PURLs against known vulnerabilities

Simple

secdb audit purl pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1

From file

secdb audit purl --file=purls.txt

From STDIN

secdb audit purl < purls.txt

From pipe

command | secdb audit purl

Using 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.json

CI

Useful in CI pipelines to fail the build when high/critical vulnerabilities are found.

secdb audit purl --sbom bom.json --fail-on=high

SARIF report (e.g. for GitHub Code Scanning)

secdb audit purl --sbom bom.json --output=sarif > results.sarif

Produces 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.csv

Emits 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-unfixed

The --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)

Audit a dependency manifest

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.

Editor integration (Language Server)

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.

Audit a CycloneDX SBOM

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.sarif

The 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)

Audit a Linux system (EXPERIMENTAL)

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 linux

Remote host over SSH

secdb audit linux --host server.example.com --user ops

Uses 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)

Audit a Docker image or container (EXPERIMENTAL)

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-container

The 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)

Calculate SSVC

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 material

Bulk, multiple CVEs

secdb ssvc calculate CVE-2021-44228 CVE-2023-4863 --mission-prevalence support --public-well-being-impact minimal

From file

secdb ssvc calculate --file cves.txt --mission-prevalence support --public-well-being-impact minimal

From STDIN

secdb ssvc calculate --mission-prevalence support --public-well-being-impact minimal < cves.txt

CVE 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

Check for a new version

secdb check-update   # alias: secdb update

A lightweight background check also runs automatically on every command (cooldown: 24h, silent on failure, skipped in CI or with SECDB_NO_UPDATE_CHECK set).

License

Apache License 2.0.

Third-party dependency attributions are listed in THIRD-PARTY-NOTICES.md.

Releases

Packages

Used by

Contributors

Languages