Find the CUDA your SBOM missed.
cudabom is an open-source Rust CLI that proves which NVIDIA CUDA software is actually inside an artifact (Python wheels, shared libraries, executables, and container images) and turns that evidence into security-grade SBOM, advisory, and VEX data.
The question it answers:
What CUDA is really inside this artifact, how do we know, and are we affected by any NVIDIA security bulletin?
Built with
What it does (and what it does not)
cudabom scans compiled, packaged artifacts, the things you ship or pull after a build, and tells you which CUDA components are physically inside them.
It does:
- Scan built binaries and packages: ELF shared libraries and executables
(
.so, bare ELF), Python wheels (.whl),tar/gziparchives, Unix static libraries (.a, detected and walked), and container-image layer tarballs or unpacked layer directories. - Identify the CUDA inside them (cudart, cuBLAS, cuDNN, NCCL, nvJPEG, and more) by hashes, build-ids, and symbols: including copies that are statically linked, vendored, or renamed, which manifest- and source-level scanners miss.
- Correlate each identified version against NVIDIA's security bulletins and emit SBOM / VEX / SARIF for CI.
It does not:
- Read your source code (
.cu,.cpp, headers); it does not parse source to infer dependencies. - Read your dependency manifests (
requirements.txt,Cargo.toml,environment.yml); it inspects what the build actually produced, not what you declared. - Run, load, or JIT anything it scans, and it needs no GPU.
Inputs and outputs
| You give it | It walks | You get back |
|---|---|---|
foo.whl / .zip |
every ELF inside the archive | identified components + advisories |
libcudart.so.12 (bare ELF) |
the shared object directly | identity (hash/build-id/soname) + CVEs |
libcudart_static.a |
the static archive (detected; member identification is expanding) | embedded-CUDA findings as coverage grows |
image layer .tar/.tar.gz |
files inside the layer | per-file CUDA findings |
| a directory | files under it (bounded) | aggregate findings |
Output formats (--format): table (human), json (native), cyclonedx
(SBOM), sarif (code scanning), markdown (PR comment). Any of them to a file
with -o.
Why not source parsing?
Deliberate scope, not a missing feature. cudabom's thesis is evidence over declaration: what you write (source) and declare (manifests) routinely disagrees with what actually lands in the shipped binary. Source says intent; the artifact tells the truth.
-lcublasin a build, orimport torchin a.py, tells you something was meant to link, not which version shipped, whether it was statically baked in, dead-stripped out, or whether a transitive dependency vendored a different copy. A renamed/vendoredlibcudartembedded in a third-party.sohas no source footprint in your repo at all; only binary evidence finds it.- Source and lockfile analysis is also environment-dependent (toolchain, flags, platform), which would break cudabom's deterministic, reproducible output.
Source- and manifest-level dependency discovery is already well served by SCA
tooling (Dependabot, pip-audit, Trivy's lockfile scanners). cudabom is the
complement: it answers "what's really in the artifact," and
cudabom reconcile proves whether that agrees with
what a source/manifest-derived SBOM declared. The adjacent extension that stays
true to the model is ingesting more declaration formats to reconcile against
(it already reads PEP 770 / CycloneDX), never guessing dependencies from source.
Start here
Pick the command that matches your goal:
| Your goal | Command |
|---|---|
| "What CUDA is in this artifact, and am I affected?" | cudabom scan <artifact> --advisories <index> |
| "Fail my CI build if an identified CUDA is affected" | cudabom scan <artifact> --fail-on affected (or cudabom gate --policy) |
| "Produce an SBOM / VEX / SARIF for this artifact" | cudabom scan ... --format cyclonedx / cudabom vex ... / cudabom scan ... --format sarif |
| "Add the CUDA my existing SBOM missed" | cudabom enrich --sbom in.cdx.json <artifact> -o out.cdx.json |
| "Does what the vendor declared match what's really inside?" | cudabom reconcile <artifact> --sbom declared.cdx.json |
| "Why did it identify this?" | cudabom explain <finding-id> |
| "Install / refresh the fingerprint + advisory data" | cudabom update |
The 60-second first run is in See it work end to end below; CI wiring is in Generating the report artifacts in CI.
What makes it specific
General binary and native-dependency discovery already exists: Syft has ELF catalogers, Trivy consumes and produces SBOMs, and OWASP blint does symbol- and fingerprint-based native identification. cudabom does not claim those tools cannot find dependencies in binaries. It specializes where they are general-purpose:
- CUDA component identity: cudart, cuBLAS/cuBLASLt, cuDNN, NCCL, and more over time, including statically linked and vendored/renamed copies.
- An explicit evidence model with EXACT / LIKELY / UNKNOWN confidence.
- Correlation against NVIDIA's machine-readable security bulletins (CSAF).
- Enriching existing Syft/Trivy/PEP 770 SBOMs rather than replacing them.
- GPU code awareness: fatbins, embedded PTX/SASS, SM targets, and producer/toolchain metadata.
- Declared vs. discovered reconciliation: what a wheel's PEP 770 SBOM says vs. what cudabom finds.
A fair, verified comparison table against Syft, Trivy, and blint lives in
docs/comparison.md, measured by actually running them
(cargo xtask eval); the summary is under Documentation.
Design principles
- No GPU required, runs on any CI runner. cudabom reads and analyzes artifacts on the CPU; it never runs code on a GPU.
- Never executes what it scans. No
dlopen, no JIT, and no network except the explicit data-refresh commands (cudabom update,cudabom db update). All crates build under#![forbid(unsafe_code)]. Seedocs/threat-model.md. - Never asserts more than the evidence supports. A confident wrong answer is
worse than "unknown". See
docs/evidence-model.md. - Deterministic output, so diffs and snapshots are meaningful.
Install
Once a release is published, cudabom is available from several official
channels. Each drops the same cudabom executable on your PATH:
cargo install cudabom # crates.io
brew install cpeoples/tap/cudabom # Homebrew
snap install cudabom # Snap Store
pip install cudabom # PyPI (also: uvx cudabom)
Prebuilt, Sigstore-signed binaries for Linux, macOS, and Windows are attached to each GitHub Release.
Quickstart
Build from source (the toolchain is pinned in
rust-toolchain.toml):
git clone https://github.com/cpeoples/cudabom.git
cd cudabom
cargo build --release
./target/release/cudabom version --verbose
Command surface (crate and pipeline architecture in docs/architecture.md):
cudabom scan <target>... --format table|json|cyclonedx|sarif|markdown [--fail-on none|found|affected]
cudabom gate <target>... --policy policy.json
cudabom enrich --sbom input.cdx.json <target> -o output.cdx.json
cudabom vex <target> -o vex.cdx.json
cudabom reconcile <target>... --sbom declared.cdx.json --vex declared.vex.json
cudabom explain <finding-id>
cudabom update
cudabom db update | db status | db build
cudabom schema
cudabom version --verbose
Exit codes: 0 success, 1 policy violation (gate) or scan --fail-on
threshold met, 2 usage error, 3 input error, 4 internal error. A plain
scan is a reporting command and exits 0 even when it identifies CUDA or
finds affected advisories; opt into failure with --fail-on found (any CUDA
identified) or --fail-on affected (an advisory affects an identified version),
or use gate for full policy enforcement.
See it work end to end
The chain, on a real NVIDIA library. cudabom independently fingerprints a
.so, identifies it, correlates it to CVEs, and reconciles it against a
declared SBOM. From a clone (pointing at the committed data):
# 1. Identify + correlate to NVIDIA advisories.
cudabom scan libnvjpeg.so.11.5.2.120 \
--db fingerprints/cuda --advisories advisories/index.json
findings:
[Exact] nvjpeg 11.5.2.120 (EmbeddedCopy) <- 1 evidence: KnownFileHash
composition:
contains nvjpeg 11.5.2.120 advisories: not_affected(CVE-2023-31028),
affected(CVE-2024-0142), affected(CVE-2024-0143), ... affected(CVE-2025-23275)
advisories:
summary: 12 affected (2 high, 7 medium, 3 low), 1 not affected, 0 under investigation
most severe: CVE-2025-23275 nvjpeg (HIGH CVSS 7.5)
HIGH (2):
CVE-2025-23275 CVSS 7.5 2025-09-23 https://nvd.nist.gov/vuln/detail/CVE-2025-23275
...
MEDIUM (7):
CVE-2024-0142 CVSS 6.8 2025-02-11 https://nvd.nist.gov/vuln/detail/CVE-2024-0142
...
The [Exact] identity is evidence-backed: its SHA-256 matched a fingerprint
derived from NVIDIA's own archive. Advisory verdicts are version-range decisions
(CVE-2023-31028 is correctly ruled not_affected), and the result leads with a
severity breakdown and the most severe CVE, grouping affected advisories
highest-severity-first with CVSS, date, and link. On a multi-file artifact the
table listing leads with the files that carry a CUDA signal and collapses the
rest into ... and N other file(s) (--all-files shows everything; -v adds
per-advisory detail). The same scan emits CycloneDX (--format cyclonedx), SARIF
(--format sarif), or a VEX document (cudabom vex).
Reconcile what a vendor declares against what cudabom discovers:
# 2. Declared (e.g. an NGC image SBOM/VEX) vs. discovered.
cudabom reconcile libcudart.so.12.4.127 libcublas.so.11.6.5.2 libnvjpeg.so.11.5.2.120 \
--db fingerprints/cuda \
--sbom fixtures/ngc/sbom.cyclonedx.json --vex fixtures/ngc/vex.cyclonedx.json
reconciliation: 2 matched, 0 declared-only, 1 discovered-only (1 non-CUDA declared)
discovered but NOT declared (declaration under-reports):
+ nvjpeg 11.5.2.120 # the CUDA the SBOM missed
matched:
= cublas 11.6.5.2 (version mismatch: declared 12.4.5.8)
= cudart 12.4.127
vex: not_affected CVE-2025-23248 # the declaration's VEX rides along
Reconciliation compares by canonical CUDA identity (so cuda-cudart,
libcudart.so.12, and pkg:generic/cuda-cudart@... all line up with cudart),
then sorts each component into one bucket:
flowchart LR
D["Declared SBOM / VEX<br/>(e.g. NGC image)"]
F["Discovered findings<br/>(cudabom scan)"]
R{"reconcile<br/>by CUDA identity"}
D --> R
F --> R
R --> M["matched<br/>declared and discovered"]
R --> DO["declared-only<br/>claimed, not found"]
R --> FO["discovered-only<br/>found, not declared"]
R --> NC["non-CUDA declared<br/>counted, out of scope"]
M -.->|versions differ| VM["version mismatch"]
cudabom reconcile --ngc-image <org/repo:tag> fetches the declared documents
straight from NGC (opt-in, key-gated) instead of local files.
Enrich an existing SBOM instead of replacing it. enrich keeps the input
document byte-for-byte (including fields cudabom does not model) and only adds
the CUDA components it discovered that the SBOM was missing, reporting the delta
on stderr:
cudabom enrich --sbom syft.cdx.json libnvjpeg.so.11.5.2.120 \
--db fingerprints/cuda -o enriched.cdx.json
# cudabom: enriched SBOM (+1 component(s), 3 already present)
Emit a VEX document (CycloneDX 1.6) carrying the advisory verdicts as VEX
analysis.state statements, so a consumer sees which CVEs apply and which were
ruled out:
cudabom vex libnvjpeg.so.11.5.2.120 \
--db fingerprints/cuda --advisories advisories/index.json -o vex.cdx.json
Validate native JSON output against the published schema. cudabom schema
prints the JSON Schema for --format json, keyed by schema version so a
consumer can branch on it:
cudabom schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cpeoples.github.io/cudabom/schema/0.1.0/cudabom.schema.json",
"title": "cudabom native output",
"type": "object",
"properties": { "schemaVersion": { "type": "string", "const": "0.1.0" } },
"required": ["schemaVersion"]
}
The .so files above come from NVIDIA's redistributables; the committed
lockfiles under fingerprints/ describe exactly which archives to fetch, and
cargo xtask corpus fetch retrieves and verifies them.
Generating the report artifacts in CI
cudabom has no single "report file": the report is the command output in the format you request. A typical CI step produces the security artifacts and gates the build in one place:
# Machine-readable deliverables (write to files with -o):
cudabom scan "$ARTIFACT" --db "$DB" --advisories "$ADV" --format cyclonedx -o sbom.cdx.json
cudabom vex "$ARTIFACT" --db "$DB" --advisories "$ADV" -o vex.cdx.json
cudabom scan "$ARTIFACT" --db "$DB" --advisories "$ADV" --format sarif -o cudabom.sarif
# Gate the build: exit non-zero only when an advisory affects an identified version.
cudabom scan "$ARTIFACT" --db "$DB" --advisories "$ADV" --fail-on affected
Upload cudabom.sarif to code scanning, attach sbom.cdx.json / vex.cdx.json
as build artifacts, and the --fail-on affected step decides pass/fail. For
policy beyond a single threshold (severity floors, component allow/deny), use
cudabom gate --policy policy.json (schema in docs/policy.md).
Getting the data
cudabom ships as a single binary that carries no fingerprint database or advisory index: those are versioned, reviewed data published per release. A binary-only install (crates.io, a Snap, a release archive) fetches them with:
cudabom update # install the latest release's data bundle
cudabom update --tag v1.2.3 # or pin a specific release
cudabom version --verbose # confirm what is installed
update downloads the signed cudabom-data-<tag>.tar.gz from the project's
GitHub Releases, verifies it against its .sha256 sidecar, and unpacks it into
a per-user data directory, resolved in this order:
CUDABOM_DATA_DIR: explicit override.SNAP_USER_DATA/cudabom: inside a confined Snap.XDG_DATA_HOME/cudabom: the freedesktop base dir.~/.local/share/cudabom: the default.
Once installed, scan, gate, vex, enrich, reconcile, and explain
default their --db and --advisories to this directory, so no flags are
needed; pass --db/--advisories (or --data-dir on update) to override.
Working from a clone instead? Point --db fingerprints/cuda and
--advisories advisories/index.json at the committed data directly.
Project layout
crates/
cudabom/ clap front end, output selection, exit codes
cudabom-core/ shared types: Artifact, Location, Component, Evidence,
Confidence, Finding, and tunable safety Limits
cudabom-extract/ safe archive/container/wheel walkers with nesting limits
cudabom-elf/ ELF facts: SONAME, NEEDED, symbols, build-id, sections
cudabom-fatbin/ fatbin/cubin/PTX parsing
cudabom-identify/ fingerprint DB + matchers -> CUDA identities + confidence
cudabom-advisory/ NVIDIA CSAF ingestion, normalization, matching
cudabom-sbom/ CycloneDX emit/enrich/merge, PEP 770 reader, VEX
cudabom-report/ table, JSON, SARIF, Markdown renderers
cudabom-policy/ policy evaluation for `gate`
xtask/ fixture builds, fingerprint-DB builder, corpus evaluation
fingerprints/ versioned fingerprint data (derived data only, no binaries)
fixtures/ CUDA C++ test-fixture sources (compiled artifacts are not committed)
fuzz/ fuzz targets for every parser
docs/ architecture, evidence model, advisories, threat model
Configuration
Tunables are centralized so there are no scattered magic values:
| Concern | Where |
|---|---|
| Dependency versions, lint policy | root Cargo.toml |
| Code formatting | rustfmt.toml |
| Clippy thresholds | clippy.toml |
| Supply-chain policy (licenses, advisories, bans) | deny.toml |
| Runtime safety limits (nesting, byte caps, ratios, timeouts) | crates/cudabom-core/src/config.rs |
Security
cudabom parses attacker-controlled files, so it holds itself to a high bar:
#![forbid(unsafe_code)], panic-free parsers, bounded extraction, SHA-pinned
GitHub Actions on hardened runners, cargo-audit and cargo-deny gates, CodeQL,
and OpenSSF Scorecard. Report vulnerabilities via a private advisory; see
SECURITY.md.
Documentation
The long-form documentation lives in docs/ as standalone Markdown;
GitHub renders it inline, and the full documentation site (Hugo + the Relearn
theme, served at GitHub Pages) presents the same content with a navigation
sidebar and search.
| Topic | Source |
|---|---|
| CLI reference (commands, formats, exit codes) | docs/cli.md |
| Architecture (crates, pipeline, invariants) | docs/architecture.md |
| Evidence and confidence model | docs/evidence-model.md |
| Gate policy schema | docs/policy.md |
| Advisory ingestion and correlation | docs/advisories.md |
| Data sources and provenance | docs/sources.md |
| Measured comparison vs. blint / Syft / Trivy (accuracy + speed) | docs/comparison.md |
| Benchmarks (how performance is measured, head to head) | docs/benchmarks.md |
| Threat model | docs/threat-model.md |
The site is generated from docs/*.md plus data-driven pages: CUDA coverage
(from the fingerprint database), advisory coverage (from the advisory index),
and a CLI reference (from cudabom --help), by
.hugo/scripts/build_docs.py, then built and
deployed by .github/workflows/docs.yml on every
push to main.
Preview it locally:
git submodule update --init # fetch the Relearn theme (first time only)
cargo build --release # so the generated CLI reference matches the binary
python .hugo/scripts/build_docs.py
hugo server -s .hugo
How does cudabom compare to blint, Syft, and Trivy?
docs/comparison.md runs each tool through its own CLI on
the same real artifacts and records what each reports, regenerated by
cargo xtask eval --write-comparison. In short: blint surfaces the cudart
soname but pins no release and no CVE; Syft and Trivy report nothing for a bare
shared object; cudabom closes the last mile from build-id/hash to exact CUDA
version to CVE correlation. It is authoritative for CUDA identity specifically,
and complements rather than replaces general SBOM and OS-package scanners.
Contributing
See CONTRIBUTING.md for the dev setup, the quality gates,
and how to add a fingerprint or an output format.
License
Metadata
Release files for cudabom 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| cudabom-0.1.0-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| cudabom-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| cudabom-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details |
| cudabom-0.1.0-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| cudabom-0.1.0-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 8.8 MB
Release files / cudabom-0.1.0-py3-none-win_amd64.whl
| Download URL | cudabom-0.1.0-py3-none-win_amd64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
d2e73cd3f61706f48007586d4080ef600c27167b0bb92a860d94f672c05fe7d8
|
|
BLAKE2b-256 checksum How to use checksums |
8663ebc002b89ee8dff0ebb363f3e77ebb3fb535321d97bf6d2321dc5076ad0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency logRelease files / cudabom-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | cudabom-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.9 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
3d8971f9716086aacdfd147ddd1d2aa6bde728f6452c13a3f346ddd98bc10124
|
|
BLAKE2b-256 checksum How to use checksums |
f200f0eaaab53c84a490904b863f2b1ed59fa926f679463ad9eac93f67f8554e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency logRelease files / cudabom-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | cudabom-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.8 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
9eda003c9aa7a89c781434d1da7dfbd116bee6bc1becb603dd8201c1efdfcc85
|
|
BLAKE2b-256 checksum How to use checksums |
d9a01adf8d5123777b13589dcd0a84436f3a0c78a790f6dbb6e2d05fb7085854
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency logRelease files / cudabom-0.1.0-py3-none-macosx_11_0_arm64.whl
| Download URL | cudabom-0.1.0-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.6 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9825793c52419855da4aa539e801f32f4465f449e102a389be734b63861b2ab2
|
|
BLAKE2b-256 checksum How to use checksums |
4fe8647a0f6bb7f5d0b60de7a4793ebe38651b1a9f1d53b0a4b10057bf45b524
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency logRelease files / cudabom-0.1.0-py3-none-macosx_10_12_x86_64.whl
| Download URL | cudabom-0.1.0-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.8 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
65a0bd7eb325d197e8fdf809cd059044a4d7e76060a380bd89ca7885b190c4ea
|
|
BLAKE2b-256 checksum How to use checksums |
323c4dcb5ba07adfa4efcf0c257438e68b2d661f67dd698e73901834a5384c65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency log