# 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 legacy `vexy` invocations. 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. ```bash 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 `purl` value. - 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 to `https://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-osv` is present. - Private mirrors do not require `--allow-public-osv`; pass the mirror base URL with `--osv-url`. - `--osv-url` must be an absolute `https://` URL with a hostname. Cleartext `http://` is accepted only for loopback hosts such as `127.0.0.1`, `::1`, or `localhost`. Do not include userinfo, query strings, or fragments. - The OSV endpoint must support the `/v1/querybatch` response shape used by the public OSV API. The public OSV API requires explicit opt-in: ```bash uv run --frozen vexcalibur query-osv pkg:pypi/django@1.2 --allow-public-osv ``` Illustrative output shape: ```text 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: ```text pkg:pypi/example@1.0.0: no vulnerabilities found ``` For private package inventories, use a private mirror: ```bash 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 | `0` | Standard output contains one result line per input PURL. | | Missing `PURL...` argument | nonzero Typer usage error | Typer prints a missing-argument error. | | Invalid package URL | nonzero Typer parameter error | Typer includes `not a valid package URL`. | | Public OSV URL without `--allow-public-osv` | `1` | Standard error starts with `OSV query failed:` and mentions `--allow-public-osv`. | | Invalid or unsafe `--osv-url` | `1` | Standard error starts with `OSV query failed:` and names the URL constraint. | | OSV HTTP, network, pagination, or response-shape failure | `1` | Standard error starts with `OSV query failed:`. | Verification commands: ```bash 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. ```bash 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. ```bash 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_FILE` for a local CycloneDX JSON or XML SBOM. - `--github-repo OWNER/REPO` for 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-file` and cannot be combined with `--github-repo`. - `--osv-url TEXT`: OSV API base URL. Use this for private OSV mirrors. Defaults to `https://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 for `OWNER/REPO` instead of reading a local SBOM file. - `--github-api-url TEXT`: GitHub API base URL for `--github-repo`. Defaults to `https://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 to `gh auth token` when no GitHub token environment variable is set. Enabled by default. The command fails closed for public OSV: ```bash 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: ```bash 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: ```bash 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: ```bash 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`: ```bash 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_TOKEN` or `GITHUB_TOKEN` for `https://api.github.com`; - `gh auth token --hostname HOST`, unless `--no-gh-auth` is set. Use `--github-api-url` with GitHub Enterprise API hosts: ```bash 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_triage` until 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 | `0` | Standard output contains VEX JSON unless `--output` is set. | | Invalid timestamp | nonzero Typer parameter error | Typer prints the parameter error. | | Missing package inventory source | `1` | Standard error starts with `Invalid generate options:` and mentions `INPUT_FILE` or `--github-repo`. | | Mutually incompatible source flags | `1` | Standard error starts with `Invalid generate options:`. | | Unsupported, invalid, unsafe, or unqueryable SBOM | `1` | Standard error starts with `SBOM ingest failed:`. | | GitHub SBOM request, authentication, API, or SPDX parsing error | `1` | Standard error starts with `GitHub SBOM ingest failed:`. | | Local findings file error | `1` | Standard error starts with `Local findings ingest failed:`. | | Empty `--osv-url` | `1` | Standard error starts with `Invalid generate options:` and says `--osv-url must not be empty`. | | Public OSV URL without `--allow-public-osv` | `1` | Standard error starts with `VEX generation failed:` and mentions `--allow-public-osv`. | | Invalid or unsafe `--osv-url` | `1` | Standard error starts with `VEX generation failed:` and names the URL constraint. | | OSV HTTP, network, pagination, or response-shape failure | `1` | Standard error starts with `OSV query failed:`. | | Output file cannot be written | `1` | Standard error starts with `Could not write VEX output`. | Verification command: ```bash 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](cyclonedx-vex-output.md) 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. ```bash 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 xml` is rejected. - `--schema-version 1.6`: required VEX schema version. Legacy `--schema-version 1.4` is rejected. - `-o PATH`, `--o PATH`, `--output PATH`: write VEX JSON to a file. Use `--output -` for standard output. When this option is omitted, `vexy` writes `cyclonedx-vex.json` in 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 to `https://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: ```bash 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 `--output -` | `0` | Standard output contains CycloneDX 1.6 VEX JSON. | | VEX JSON generated successfully with a file output | `0` | The output file contains CycloneDX 1.6 VEX JSON. | | Missing `-i/--in-file` | `1` | Standard error starts with `vexy compatibility failed:`. | | Unsupported legacy output format or schema version | `1` | Standard error starts with `vexy compatibility failed:`. | | `-i -` stdin input | `1` | Standard error says stdin input is not supported. | | Output file exists without `--force` | `1` | Standard error asks for `--force`. | | `--offline` without `--findings-file` | `1` | Standard error starts with `vexy compatibility failed:` and mentions `--findings-file`. | | Empty `--osv-url` | `1` | Standard error starts with `vexy compatibility failed:` and says `--osv-url must not be empty`. | | `--findings-file` combined with `--allow-public-osv` or `--osv-url` | `1` | Standard error starts with `vexy compatibility failed:` and identifies the incompatible option. | | Public OSV URL without `--allow-public-osv` | `1` | Standard error starts with `VEX generation failed:` and mentions `--allow-public-osv`. | | Invalid or unsafe `--osv-url` | `1` | Standard error starts with `VEX generation failed:` and names the URL constraint. | | OSV HTTP, network, pagination, or response-shape failure | `1` | Standard error starts with `OSV query failed:`. |