The hidden risk in your dependency tree
You spent three days building a CLI tool that parses JSON and prints it nicely. You're about to push to GitHub. Your friend runs it, and suddenly the terminal starts beeping. A dependency you pulled in has a known buffer overflow that triggers on specific input. You didn't write the bug, but your users are the ones who get hit. This is the reality of dependency management. You trust your code. You have to trust your dependencies too. cargo audit is how you verify that trust.
Rust projects rarely consist of just your code. A typical binary pulls in dozens of crates. Those crates pull in more crates. The dependency tree grows deep and wide. You review your source files. You run tests. You rarely review the thousands of lines of code living in ~/.cargo/registry. cargo audit bridges that gap. It scans your dependency tree and compares every crate against the RustSec advisory database. It tells you if you're sitting on a bomb.
How cargo audit works
cargo audit reads your Cargo.lock file. It does not read Cargo.toml. This distinction matters. Cargo.toml declares version ranges. Cargo.lock records the exact versions resolved for your project. cargo audit checks the exact versions you are using.
Think of Cargo.lock as a receipt. It lists every single crate and version your project uses, down to the exact commit hash. cargo audit takes that receipt and compares it against a database of known security advisories. The RustSec database is like a community-maintained list of recalled items. If your receipt contains a recalled item, cargo audit flags it. It doesn't fix the problem. It tells you exactly what's wrong, why it's dangerous, and what version fixes it.
The advisory database lives on GitHub. Anyone can submit an advisory. The RustSec team reviews and merges them. Each advisory gets an ID like RUSTSEC-2023-0001. The ID encodes the year and a sequence number. Advisories describe the vulnerability, the affected versions, and the patched versions. They also include a severity rating and a reference to the CVE if one exists.
Minimal example
Install cargo-audit and run it in your project directory. The tool reads Cargo.lock and reports any matches against the advisory database.
# Install cargo-audit to your local cargo bin directory
cargo install cargo-audit
# Run the audit against the current project's Cargo.lock
cargo audit
If your project is clean, you see a message saying no vulnerabilities were found. If there are issues, you see a table. The table lists the crate name, version, advisory ID, severity, and a link to the advisory. The output uses colors to highlight severity. Red indicates critical issues. Yellow indicates high severity. Green indicates low severity or unmaintained warnings.
Warning: Vulnerability found in crate `serde` version `1.0.100`
ID: RUSTSEC-2023-0001
Title: Deserialization of untrusted data leads to RCE
URL: https://rustsec.org/advisories/RUSTSEC-2023-0001.html
Patched versions: >= 1.0.101
The output tells you the crate, the vulnerable version, and the patched version. You can update the dependency to the patched version and run the audit again. The cycle continues until the output is clean.
Treat the advisory database as a living document. New vulnerabilities get discovered daily. Run cargo audit regularly.
Walk through the audit process
When you run cargo audit, the tool performs a sequence of steps. Understanding these steps helps you troubleshoot and integrate the tool into your workflow.
First, cargo audit looks for Cargo.lock in the current directory. If the file is missing, the tool stops. Libraries often do not commit Cargo.lock. Binary crates do. If you are auditing a library, you need to generate a lock file first. Run cargo generate-lockfile to create the file. Then run cargo audit.
Second, cargo audit fetches the advisory database. It caches the database in ~/.cargo/audit. The cache updates periodically. If you run the tool frequently, it uses the cached database. If the cache is stale, the tool fetches the latest version. You can force an update by deleting the cache or using the --db-path flag to point to a fresh copy.
Third, cargo audit iterates over every crate in the lock file. It matches the crate name and version against the advisories. The matching logic checks if the version falls within the affected range. If a match is found, the tool records the vulnerability.
Fourth, cargo audit prints the results. It groups results by severity. It also checks for unmaintained crates if you enable that feature. Unmaintained crates are not necessarily vulnerable. They just lack recent activity. The community considers unmaintained crates a risk because vulnerabilities might go unpatched.
Finally, cargo audit exits with a status code. A zero exit code means no issues were found. A non-zero exit code means vulnerabilities were detected. This behavior makes the tool suitable for CI pipelines. The pipeline fails if the audit finds issues.
Run the audit before every release. Security is a process, not a one-time check.
Realistic example: transitive dependencies and CI
In a real project, vulnerabilities often hide in transitive dependencies. You might depend on framework, which depends on vulnerable-crate. You never added vulnerable-crate to your Cargo.toml. cargo audit still catches it. The tool scans the entire tree.
# Cargo.toml
[dependencies]
# You depend on framework, which pulls in vulnerable-crate
framework = "2.0.0"
# cargo audit output
Warning: Vulnerability found in crate `vulnerable-crate` version `0.5.0`
ID: RUSTSEC-2023-0042
Title: Path traversal in file handling
Patched versions: >= 0.5.1
You need to update framework to a version that uses a patched vulnerable-crate. Or you need to override the dependency. Run cargo tree -i vulnerable-crate to see where the crate comes from. This command prints the dependency path. It helps you decide whether to update a parent crate or add an override.
# Show the dependency tree for vulnerable-crate
cargo tree -i vulnerable-crate
The output shows the chain. my-app -> framework -> vulnerable-crate. You can update framework or add a [patch] section to force a newer version. After fixing the issue, run cargo audit again to verify.
Integrating cargo audit into CI is the community convention. You want to block merges that introduce vulnerabilities. Add a step to your GitHub Actions workflow. The step runs cargo audit and fails if issues are found.
# .github/workflows/audit.yml
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/audit@v1
with:
command: audit
args: --deny warnings
The --deny warnings flag tells the tool to treat warnings as failures. This ensures the pipeline fails on any advisory. Adjust the flag based on your risk tolerance. Some teams ignore low-severity issues. Others fail on everything.
Don't rely on manual checks. Automate the audit. If the pipeline passes, you have confidence. If it fails, you have a clear path to fix.
Pitfalls and edge cases
cargo audit is powerful, but it has limitations. Knowing these pitfalls prevents frustration and false confidence.
If you run cargo audit in a library crate without a Cargo.lock, the tool fails with "No Cargo.lock found". Libraries do not commit lock files. Generate one temporarily. Run cargo generate-lockfile && cargo audit. Delete the lock file afterward. Or add a CI step that generates the lock file for auditing.
The tool does not check for yanked crates. Yanked means the author removed the version from crates.io. It might be broken, but not necessarily vulnerable. cargo update warns about yanked crates. cargo audit focuses on security advisories. If you care about yanked crates, run cargo update separately.
Advisories can be too broad. An advisory might list a vulnerability that only affects Windows. You deploy on Linux. cargo audit reports the vulnerability regardless of your platform. You have to judge the relevance. Use --ignore RUSTSEC-2023-0001 to suppress specific advisories. Document the reason. If you ignore an advisory, add a comment in your code or CI config explaining why. Future you will thank you.
False positives happen. An advisory might claim a crate is vulnerable, but the vulnerability requires a feature flag you don't use. cargo audit does not analyze feature flags. It checks the crate version. If the version is affected, the tool reports it. You need to review the advisory details. Check the "Details" section. It often mentions feature flags or configuration requirements.
The advisory database might be stale. If you run the tool offline, it uses the cached database. The cache could be days old. New vulnerabilities might not be included. Force an update by deleting ~/.cargo/audit or running with network access.
Ignore advisories sparingly. Every ignored advisory increases your risk surface. If you ignore, you are accepting the risk. Make that decision consciously.
Treat the SAFETY comment as a proof. If you can't write it, you don't have one. The same applies to ignored advisories. If you can't justify ignoring an advisory, don't ignore it.
Decision matrix
Choose the right tool for your project size and needs. cargo audit is the standard for simple checks. Other tools fill gaps for larger workflows.
Use cargo audit when you need a quick check against the RustSec advisory database for a binary project. Use cargo audit in CI pipelines to block merges that introduce known vulnerabilities. Use cargo audit when you want a lightweight tool with minimal configuration.
Use cargo deny when you manage a monorepo and need fine-grained control over licenses, sources, and vulnerability policies. Use cargo deny when you need to enforce custom rules beyond the advisory database. Use cargo deny when you want a single tool for security, license compliance, and source verification.
Use cargo outdated when you want to see which dependencies have newer versions available, regardless of security status. Use cargo outdated to plan dependency updates.
Reach for manual review when an advisory describes a vulnerability that requires specific configuration to trigger, and you need to verify your setup is safe. Reach for manual review when you are evaluating a new crate and want to check its security posture before adding it.
Trust the borrow checker. It usually has a point. Trust cargo audit too. It catches the bugs you didn't write.