CLI Reference¶
Vexcalibur is usable for the commands documented here, but public contracts are not stable before 1.0. Command names, flags, output fields, and exit behavior can change before the first stable compatibility policy.
The installed package exposes two executables:
vexcalibur: the primary command.vexy: a compatibility command for a documented subset of legacyvexyinvocations.
Run uv run --frozen vexcalibur --help or uv run --frozen vexcalibur COMMAND --help for the exact help text generated by the current Typer command definitions.
Expected input and configuration errors are reported without Python tracebacks. Treat any nonzero exit code as a failed command in automation.
vexcalibur query-osv¶
Query OSV for one or more package URLs and print vulnerability IDs.
uv run --frozen vexcalibur query-osv PURL... [OPTIONS]
Arguments:
PURL...: one or more package URLs.
Package URL constraints:
Each value must parse as a package URL using
packageurl-python.The command sends each package URL to the OSV query batch API as the package
purlvalue.Include a package version in the PURL when the OSV query should be version specific, for example
pkg:pypi/django@1.2.Invalid package URL strings are rejected before any OSV request is made.
Options:
--osv-url TEXT: OSV API base URL. Use this for private OSV mirrors. Defaults tohttps://api.osv.dev.--allow-public-osv: allow sending package URLs to the public OSV API.
Network behavior:
The default OSV URL is
https://api.osv.dev.Public OSV queries fail unless
--allow-public-osvis present.Private mirrors do not require
--allow-public-osv; pass the mirror base URL with--osv-url.--osv-urlmust be an absolutehttps://URL with a hostname. Cleartexthttp://is accepted only for loopback hosts such as127.0.0.1,::1, orlocalhost. Do not include userinfo, query strings, or fragments.The OSV endpoint must support the
/v1/querybatchresponse shape used by the public OSV API.
The public OSV API requires explicit opt-in:
uv run --frozen vexcalibur query-osv pkg:pypi/django@1.2 --allow-public-osv
Illustrative output shape:
pkg:pypi/django@1.2: VULN-ID-1, VULN-ID-2
Live OSV data changes over time, so vulnerability IDs, counts, and ordering can change. If OSV returns no matches for an input, the line uses this shape:
pkg:pypi/example@1.0.0: no vulnerabilities found
For private package inventories, use a private mirror:
uv run --frozen vexcalibur query-osv \
pkg:pypi/example@1.0.0 \
--osv-url https://osv.internal.example
query-osv exit behavior:
Condition |
Exit code |
Output stream and message shape |
|---|---|---|
All package URLs are queried successfully |
|
Standard output contains one result line per input PURL. |
Missing |
nonzero Typer usage error |
Typer prints a missing-argument error. |
Invalid package URL |
nonzero Typer parameter error |
Typer includes |
Public OSV URL without |
|
Standard error starts with |
Invalid or unsafe |
|
Standard error starts with |
OSV HTTP, network, pagination, or response-shape failure |
|
Standard error starts with |
Verification commands:
uv run --frozen vexcalibur query-osv pkg:pypi/django@1.2 --allow-public-osv
Expected success signal: exit code 0 and one output line for the submitted PURL.
uv run --frozen vexcalibur query-osv pkg:pypi/django@1.2
Expected failure signal: nonzero exit and an OSV query failed: message that asks
for --allow-public-osv.
vexcalibur generate¶
Generate CycloneDX VEX JSON from a local CycloneDX SBOM file or from a GitHub Dependency Graph SBOM.
uv run --frozen vexcalibur generate [INPUT_FILE] [OPTIONS]
Arguments:
INPUT_FILE: readable CycloneDX JSON or XML SBOM file. Omit this argument when using--github-repo.
Exactly one package inventory source is required:
INPUT_FILEfor a local CycloneDX JSON or XML SBOM.--github-repo OWNER/REPOfor the repository’s GitHub Dependency Graph SBOM.
JSON input must be UTF-8. XML input must be rooted at bom in the
http://cyclonedx.org/schema/bom/1.4, /1.5, or /1.6 namespace. XML may use
parser-detected XML encodings such as UTF-8 or UTF-16. DTD, entity, and
external-reference declarations are rejected.
GitHub Dependency Graph input uses GitHub’s asynchronous SBOM REST API to request and download an SPDX 2.3 JSON report. Vexcalibur extracts package URL references from that document and passes them through the same provider-neutral VEX generation path used for local CycloneDX files.
Options:
--output PATH,-o PATH: write VEX JSON to a file instead of standard output.--timestamp TEXT: ISO-8601 timestamp for deterministic output metadata.--findings-file PATH: local Vexcalibur findings JSON file. When set, no OSV API request is sent.--offline: disable network vulnerability sources. Currently requires--findings-fileand cannot be combined with--github-repo.--osv-url TEXT: OSV API base URL. Use this for private OSV mirrors. Defaults tohttps://api.osv.dev.--allow-public-osv: allow sending SBOM package URLs and versions to the public OSV API.--github-repo TEXT: fetch the GitHub Dependency Graph SBOM forOWNER/REPOinstead of reading a local SBOM file.--github-api-url TEXT: GitHub API base URL for--github-repo. Defaults tohttps://api.github.com.--github-token-env TEXT: environment variable containing a GitHub token. Use this explicitly for non-default GitHub API hosts.--gh-auth / --no-gh-auth: allow or disable fallback togh auth tokenwhen no GitHub token environment variable is set. Enabled by default.
The command fails closed for public OSV:
uv run --frozen vexcalibur generate tests/fixtures/sbom/cyclonedx-json-simple.json
Pass --allow-public-osv only when the SBOM inventory is safe to send to the public OSV API:
uv run --frozen vexcalibur generate \
tests/fixtures/sbom/cyclonedx-json-simple.json \
--allow-public-osv \
--output /tmp/vexcalibur-vex.json
For private SBOMs, use an internal OSV-compatible endpoint:
uv run --frozen vexcalibur generate \
path/to/sbom.json \
--osv-url https://osv.internal.example \
--output /tmp/vexcalibur-vex.json
For offline workflows, use local findings:
uv run --frozen vexcalibur generate \
path/to/sbom.json \
--offline \
--findings-file path/to/findings.json \
--output /tmp/vexcalibur-vex.json
--findings-file cannot be combined with --allow-public-osv or --osv-url.
To generate from a GitHub repository’s Dependency Graph SBOM, omit INPUT_FILE
and pass --github-repo:
uv run --frozen vexcalibur generate \
--github-repo vexcalibur-dev/vexcalibur \
--allow-public-osv \
--output /tmp/vexcalibur-vex.json
Public GitHub repositories can be requested without a token, subject to GitHub API rate limits. For authenticated requests, Vexcalibur resolves a token in this order:
the environment variable named by
--github-token-env;GH_TOKENorGITHUB_TOKENforhttps://api.github.com;gh auth token --hostname HOST, unless--no-gh-authis set.
Use --github-api-url with GitHub Enterprise API hosts:
uv run --frozen vexcalibur generate \
--github-repo internal/example \
--github-api-url https://github.example.test/api/v3 \
--github-token-env GH_ENTERPRISE_TOKEN \
--osv-url https://osv.internal.example \
--output /tmp/vexcalibur-vex.json
--github-api-url must use HTTPS and must not include userinfo, query strings,
or fragments. Token-backed GitHub SBOM requests need repository Contents: read
permission. Public repositories can be requested without a token, subject to
GitHub API rate limits.
Fetching the SBOM from GitHub is a network operation, so --github-repo cannot
be combined with --offline. Sending the resulting package inventory to public
OSV is a separate network decision and still requires --allow-public-osv.
generate output behavior:
Without
--output, the command writes CycloneDX 1.6 VEX JSON to standard output.With
--output, the command writes the VEX JSON file and does not echo it to standard output.OSV-derived findings are marked
in_triageuntil policy-driven state selection is implemented.Local findings can supply explicit CycloneDX VEX analysis states.
generate exit behavior:
Condition |
Exit code |
Output stream and message shape |
|---|---|---|
VEX JSON generated successfully |
|
Standard output contains VEX JSON unless |
Invalid timestamp |
nonzero Typer parameter error |
Typer prints the parameter error. |
Missing package inventory source |
|
Standard error starts with |
Mutually incompatible source flags |
|
Standard error starts with |
Unsupported, invalid, unsafe, or unqueryable SBOM |
|
Standard error starts with |
GitHub SBOM request, authentication, API, or SPDX parsing error |
|
Standard error starts with |
Local findings file error |
|
Standard error starts with |
Empty |
|
Standard error starts with |
Public OSV URL without |
|
Standard error starts with |
Invalid or unsafe |
|
Standard error starts with |
OSV HTTP, network, pagination, or response-shape failure |
|
Standard error starts with |
Output file cannot be written |
|
Standard error starts with |
Verification command:
uv run --frozen vexcalibur generate \
tests/fixtures/sbom/cyclonedx-json-simple.json \
--allow-public-osv \
--timestamp 2026-06-23T00:00:00Z \
--output /tmp/vexcalibur-vex.json
python - <<'PY'
import json
from pathlib import Path
vex = json.loads(Path("/tmp/vexcalibur-vex.json").read_text())
assert vex["bomFormat"] == "CycloneDX"
assert vex["specVersion"] == "1.6"
assert vex["metadata"]["timestamp"] == "2026-06-23T00:00:00+00:00"
print("generated CycloneDX VEX")
PY
Expected success signal: the Python verification command prints
generated CycloneDX VEX.
See CycloneDX VEX output for the generated document shape, grouping rules, affected component behavior, and deterministic output contract.
vexy¶
vexy is a compatibility executable for migrating selected legacy workflows to
Vexcalibur. It does not reimplement the old Sonatype OSS Index integration, old
CycloneDX XML VEX output, or old CycloneDX 1.4 VEX output. It maps supported
legacy-style flags onto Vexcalibur’s current CycloneDX 1.6 JSON generator.
uv run --frozen vexy [OPTIONS]
Supported compatibility flags:
-c PATH,--config PATH: accepted so legacy command lines still parse. Vexcalibur does not read this file or use legacy data-source credentials.-i PATH,--in-file PATH: CycloneDX JSON or XML SBOM file.-i -stdin input is rejected.--format json: required output format.--format xmlis rejected.--schema-version 1.6: required VEX schema version. Legacy--schema-version 1.4is rejected.-o PATH,--o PATH,--output PATH: write VEX JSON to a file. Use--output -for standard output. When this option is omitted,vexywritescyclonedx-vex.jsonin the current working directory.--force: overwrite an existing output file.-q: accepted for compatibility. The compatibility command does not print progress output.-X: print compatibility debug output to standard error.
Vexcalibur source-mode flags are also available:
--findings-file PATH: local Vexcalibur findings JSON file. When set, no OSV API request is sent.--offline: disable network sources. Currently requires--findings-file.--osv-url TEXT: OSV-compatible API base URL for a private mirror.--allow-public-osv: allow sending SBOM package URLs and versions tohttps://api.osv.dev.--timestamp TEXT: ISO-8601 timestamp for deterministic output metadata.
The compatibility command preserves Vexcalibur’s public-service boundary. A
legacy -c config containing OSV or OSS Index sources is not enough to contact a
public service. Use --allow-public-osv only when the SBOM inventory is safe to
send to public OSV, or use an absolute https:// --osv-url for a private
OSV-compatible endpoint.
Offline migration example:
uv run --frozen vexy \
-c tests/fixtures/vexy/legacy-config.yml \
-i tests/fixtures/sbom/cyclonedx-xml-1.5-simple.xml \
--format json \
--schema-version 1.6 \
--output - \
--offline \
--findings-file tests/fixtures/findings/all-analysis-states.json \
--timestamp 2026-06-23T00:00:00Z
Expected success signal: exit code 0 and CycloneDX 1.6 VEX JSON on standard
output.
vexy exit behavior:
Condition |
Exit code |
Output stream and message shape |
|---|---|---|
VEX JSON generated successfully with |
|
Standard output contains CycloneDX 1.6 VEX JSON. |
VEX JSON generated successfully with a file output |
|
The output file contains CycloneDX 1.6 VEX JSON. |
Missing |
|
Standard error starts with |
Unsupported legacy output format or schema version |
|
Standard error starts with |
|
|
Standard error says stdin input is not supported. |
Output file exists without |
|
Standard error asks for |
|
|
Standard error starts with |
Empty |
|
Standard error starts with |
|
|
Standard error starts with |
Public OSV URL without |
|
Standard error starts with |
Invalid or unsafe |
|
Standard error starts with |
OSV HTTP, network, pagination, or response-shape failure |
|
Standard error starts with |