# cargo-auditable embeds a dependency list in every Rust binary you ship

> cargo-auditable wraps cargo so that the crate versions behind a binary are written into a linker section, letting scanners find vulnerable dependencies in shipped artefacts. It is a small wrapper with a large ecosystem behind it, and it costs almost nothing in binary size.

**rust-secure-code/cargo-auditable** — Make production Rust binaries auditable

- Repository: https://github.com/rust-secure-code/cargo-auditable
- Stars: 867 · Forks: 44
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/rust-secure-code-cargo-auditable

## The problem is a binary with no visible dependency list

A Rust binary built for production usually tells you nothing about which crate versions went into it. The Cargo.lock file records that information, but it lives in the source tree, and the source tree is not what ends up on a customer's machine or in a container image layer. Once the build is done and the artefact is shipped, the audit trail is gone.

cargo-auditable closes that gap. The project states the goal plainly: know the exact crate versions used to build your Rust executable, then audit binaries for known bugs or security vulnerabilities in production, at scale, with zero bookkeeping. Zero bookkeeping is the phrase worth sitting with, because the alternative approaches all require someone to keep a manifest next to every artefact.

The audience is distributors and platform teams rather than application developers. Linux distributions build their Rust packages with it, container base images wrap cargo so that every build is auditable, and a release pipeline can attach the same data as a container attestation. If you ship a Rust binary to someone else and they ask what is inside it, this is the mechanism that gives a real answer.

## A JSON dependency list in a .dep-v0 linker section

The mechanism is a build time wrapper, not a post build scanner. cargo auditable invokes cargo, records the resolved dependency tree, and embeds it into the compiled executable as JSON inside a dedicated linker section named .dep-v0.

Two properties of that design do the work. The data travels with the artefact, so nothing has to be shipped alongside it. And the format is deliberately plain, because the JSON is Zlib compressed and described by a JSON schema that lives in the repository as cargo-auditable.schema.json, with the parsing instructions in PARSING.md.

That plainness is a deliberate interoperability choice. The project says the format is designed for alternative implementations, and that parsing it takes 5 lines of Python. A scanner written in any language can read a binary, find the section, decompress it and get a list of crates.

The repository is a Cargo workspace with seven members: auditable-info, auditable-extract, auditable-serde, cargo-auditable, auditable-cyclonedx, auditable2cdx and resolverver. The separation between the wrapper, the extraction crate, the serialisation crate and the format converters is what lets other tools depend on the reader without depending on the whole build wrapper.

## Installing the tools and building an auditable binary

There are two tools in play. cargo-auditable does the embedding, and cargo-audit does the scanning. The documented setup is a single cargo install of both:

```bash
# Install the tools
cargo install cargo-auditable cargo-audit
# Build your project with dependency lists embedded in the binaries
cargo auditable build --release
# Scan the binary for vulnerabilities
cargo audit bin target/release/your-project
```

The scan step is the payoff. Rather than auditing a Cargo.lock in a source tree, cargo audit bin reads the embedded section of a finished binary and reports on the vulnerabilities affecting the crates inside it.

The wrapper is transparent in one important respect. cargo auditable works with any cargo command, and all arguments are passed to cargo as is, so cargo auditable test and cargo auditable build --release behave the way you would expect. The wrapper has to be on the path in the build environment, which is the main operational thing to arrange in a CI pipeline.

## Nightly Cargo records a more accurate SBOM precursor

On a nightly toolchain there is a better source of dependency data. Cargo has a native SBOM precursor, and the project shows how to use it:

```bash
CARGO_BUILD_SBOM=true cargo +nightly auditable build -Z sbom --release
```

The point of the environment variable and the unstable flag is accuracy. Recording dependencies from Cargo's own build rather than from a separate resolution step captures what actually went into the binary, including details the wrapper cannot infer on its own.

The cost of that route is a nightly dependency and an unstable flag, which is a trade most teams make only for release builds. There is also a documented wrinkle. A bug in Cargo means you may have to run touch src/* or cargo clean first if you used cargo auditable without -Z sbom in the same project, because the two build modes leave different artefacts behind and the stale one confuses the next build.

That caveat is the kind of detail that decides whether you adopt the nightly path at all. If your build matrix is already nightly, the flag is nearly free. If it is not, the stable wrapper is the more predictable choice.

## Scanners that already read the embedded section

The value of the format depends entirely on whether existing tooling consumes it, and a wide range of tools does. For vulnerability reporting, cargo-audit v0.17.3 and later can detect the data in binaries, trivy v0.31.0 and later does the same, grype v0.83.0 and later reports on it for both binaries and container images, and osv-scanner v2.0.1 and later reads it when scanning container images.

For recovering the dependency list itself, syft v1.15.0 and later can extract it and convert it to other formats, with older versions needing the --catalogers all option. wasm-tools v1.227.0 and later can recover it from WebAssembly, for example with wasm-tools metadata show. The rust-audit-info crate recovers the list and prints it as JSON, and auditable2cdx prints it as CycloneDX.

Container images get a path of their own. Docker supports embedding CycloneDX documents into images, so building with an SBOM attestation and using cargo auditable inside the Dockerfile puts the Rust dependencies into the image attestation itself, with the BuildKit Syft scanner supplying them.

## The overhead is under 4kB, and it does not break reproducible builds

The obvious objection is that embedding a dependency list makes the binary bigger. The project answers that in its own FAQ with a specific figure: the embedded dependency list uses under 4kB even on dependency trees with 400 or more entries, which typically translates to between 1/1000 and 1/10,000 of the size of the binary.

That is small enough to be invisible in practice, and it is small enough that the reproducibility question is the more interesting one. The data format is designed specifically not to disrupt reproducible builds, which is a stronger requirement than simply being small.

Reproducibility matters here because the whole point is supply chain auditing. If embedding the list changed the hash of an otherwise identical build, then the embedded data could not be compared across two builds of the same source, and the audit trail would be less trustworthy. Designing the format around that constraint is the part of the engineering that a size argument alone would not cover.

## Distribution adoption is where the real argument sits

The adoption section is the strongest evidence that the approach works, because it describes other people shipping binaries with the data inside rather than people evaluating it.

Microsoft uses cargo auditable internally and previously maintained a data extraction library for Go. Alpine Linux, NixOS, openSUSE, Void Linux, Chimera Linux and Wolfi OS all build their Rust packages with cargo auditable, so packages installed from those repositories can be audited. Ubuntu 26.04 uses it for select packages and lets others opt in, including through PPAs.

Chainguard goes further and includes cargo auditable in its Rust base container with a default cargo wrapper that always calls it, which makes Rust applications built with that container auditable by default. That is the pattern worth copying if you run a container build service: the wrapper is invisible to the developer and the property holds for everyone.

The long term direction is also stated. The goal is to get Cargo itself to encode this information in binaries, and there is an RFC for an implementation inside Cargo for which this project is the groundwork. If that lands, the wrapper becomes a compatibility shim rather than something you have to remember to call.

## Platform coverage, embedded targets and the drop in cargo wrapper

Linux, Windows and macOS are officially supported, and WebAssembly is supported from v0.6.3. Other ELF targets should work but are not tested on CI, so an unusual target gives you a mechanism without a guarantee.

Embedded platforms get a specific treatment. The audit data is embedded in the firmware image but is not flashed onto the space constrained microcontroller, which keeps the image auditable without spending precious ROM. That is a sensible compromise for firmware, where every kilobyte is accounted for.

Two more mechanisms are worth knowing. If you cannot change how cargo is invoked, cargo auditable can be used as a drop in replacement for cargo, with the details in REPLACING_CARGO.md. On a shell like bash you can set an alias in your .bashrc:

```bash
alias cargo="cargo auditable"
```

and cargo dist has opt in support for cargo auditable for GitHub releases. The repository also carries both LICENSE-APACHE and LICENSE-MIT files, which is the dual licensing pattern common in the Rust ecosystem, so a dependant can pick either.

## Conclusion

cargo-auditable is a good fit for teams that ship Rust binaries or container images and want to answer which crate versions are inside an artefact they no longer have the build environment for, since the data is embedded rather than generated at scan time. It is not useful if you never ship binaries, and it depends on a Rust toolchain version that supports the mechanism, which rules out some older targets. Before adopting it, install cargo-auditable and cargo-audit, run cargo auditable build --release on a real project, and confirm with cargo audit bin that your scanner understands the embedded section before you rely on it in a release gate.

## FAQ

### How do I make a Rust binary auditable with cargo-auditable?

Install cargo-auditable and cargo-audit with cargo install cargo-auditable cargo-audit, then build with cargo auditable build --release. All arguments are passed to cargo as-is, so it works with any cargo command.

### Does embedding the dependency list make my binary much bigger?

The project says the embedded dependency list uses under 4kB even on dependency trees with 400 or more entries, which is typically between 1/1000 and 1/10,000 of the binary size. The format is also designed not to disrupt reproducible builds.

### Which scanners can read the data embedded by cargo-auditable?

cargo-audit v0.17.3 and later, trivy v0.31.0 and later, grype v0.83.0 and later and osv-scanner v2.0.1 and later detect it in binaries and container images. syft, wasm-tools, rust-audit-info and auditable2cdx can recover the dependency list itself.

## Sources

- [Issues](https://github.com/rust-secure-code/cargo-auditable/issues)
- [License: Apache-2.0](https://github.com/rust-secure-code/cargo-auditable/blob/master/LICENSE)
- [README](https://github.com/rust-secure-code/cargo-auditable/blob/master/README.md)
- [Releases](https://github.com/rust-secure-code/cargo-auditable/releases)
- [rust-secure-code/cargo-auditable on GitHub](https://github.com/rust-secure-code/cargo-auditable)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rust-secure-code-cargo-auditable
