Rust Engineering Practices
Dependency Management and Supply Chain Security 🟢
What you'll learn:
- Scanning for known vulnerabilities with
cargo-audit- Enforcing license, advisory, and source policies with
cargo-deny- Supply chain trust verification with Mozilla's
cargo-vet- Tracking outdated dependencies and detecting breaking API changes
- Visualizing and deduplicating your dependency tree
Cross-references: Release Profiles —
cargo-udepstrims unused dependencies found here · CI/CD Pipeline — audit and deny jobs in the pipeline · Build Scripts —build-dependenciesare part of your supply chain too
A Rust binary doesn't just contain your code — it contains every transitive
dependency in your Cargo.lock. A vulnerability, license violation, or
malicious crate anywhere in that tree becomes your problem. This chapter
covers the tools that make dependency management auditable and automated.
cargo-audit — Known Vulnerability Scanning
cargo-audit
checks your Cargo.lock against the RustSec Advisory Database,
which tracks known vulnerabilities in published crates.
# Installcargo install cargo-audit# Scan for known vulnerabilitiescargo audit# Output:# Crate: chrono# Version: 0.4.19# Title: Potential segfault in localtime_r invocations# Date: 2020-11-10# ID: RUSTSEC-2020-0159# URL: https://rustsec.org/advisories/RUSTSEC-2020-0159# Solution: Upgrade to >= 0.4.20# Check and fail CI if vulnerabilities existcargo audit --deny warnings# Generate JSON output for automated processingcargo audit --json# Fix vulnerabilities by updating Cargo.lockcargo audit fixCI integration:
# .github/workflows/audit.ymlname: Security Auditon: schedule: - cron: '0 0 * * *' # Daily check — advisories appear continuously push: paths: ['Cargo.lock']jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: rustsec/audit-check@v2 with: token: ${{ secrets.GITHUB_TOKEN }}cargo-deny — Comprehensive Policy Enforcement
cargo-deny goes far beyond
vulnerability scanning. It enforces policies across four dimensions:
- Advisories — known vulnerabilities (like cargo-audit)
- Licenses — allowed/denied license list
- Bans — forbidden crates or duplicate versions
- Sources — allowed registries and git sources
# Installcargo install cargo-deny# Initialize configurationcargo deny init# Creates deny.toml with documented defaults# Run all checkscargo deny check# Run specific checkscargo deny check advisoriescargo deny check licensescargo deny check banscargo deny check sourcesExample deny.toml:
# deny.toml[advisories]vulnerability = "deny" # Fail on known vulnerabilitiesunmaintained = "warn" # Warn on unmaintained cratesyanked = "deny" # Fail on yanked cratesnotice = "warn" # Warn on informational advisories[licenses]unlicensed = "deny" # All crates must have a licenseallow = [ "MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-DFS-2016",]copyleft = "deny" # No GPL/LGPL/AGPL in this projectdefault = "deny" # Deny anything not explicitly allowed[bans]multiple-versions = "warn" # Warn if same crate appears at 2 versionswildcards = "deny" # No path = "*" in dependencieshighlight = "all" # Show all duplicates, not just first# Ban specific problematic cratesdeny = [ # openssl-sys pulls in C OpenSSL — prefer rustls { name = "openssl-sys", wrappers = ["native-tls"] },]# Allow specific duplicate versions (when unavoidable)[[bans.skip]]name = "syn"version = "1.0" # syn 1.x and 2.x often coexist[sources]unknown-registry = "deny" # Only allow crates.iounknown-git = "deny" # No random git dependenciesallow-registry = ["https://github.com/rust-lang/crates.io-index"]License enforcement is particularly valuable for commercial projects:
# Check which licenses are in your dependency treecargo deny list# Output:# MIT — 127 crates# Apache-2.0 — 89 crates# BSD-3-Clause — 12 crates# MPL-2.0 — 3 crates ← might need legal review# Unicode-DFS — 1 cratecargo-vet — Supply Chain Trust Verification
cargo-vet (from Mozilla) addresses a
different question: not "does this crate have known bugs?" but "has a trusted
human actually reviewed this code?"
# Installcargo install cargo-vet# Initialize (creates supply-chain/ directory)cargo vet init# Check which crates need reviewcargo vet# After reviewing a crate, certify it:cargo vet certify serde 1.0.203# Records that you've audited serde 1.0.203 for your criteria# Import audits from trusted organizationscargo vet import mozillacargo vet import googlecargo vet import bytecode-allianceHow it works:
supply-chain/
├── audits.toml ← Your team's audit certifications
├── config.toml ← Trust configuration and criteria
└── imports.lock ← Pinned imports from other organizationscargo-vet is most valuable for organizations with strict supply-chain
requirements (government, finance, infrastructure). For most teams,
cargo-deny provides sufficient protection.
cargo-outdated and cargo-semver-checks
cargo-outdated — find dependencies that have newer versions:
cargo install cargo-outdatedcargo outdated --workspace# Output:# Name Project Compat Latest Kind# serde 1.0.193 1.0.203 1.0.203 Normal# regex 1.9.6 1.10.4 1.10.4 Normal# thiserror 1.0.50 1.0.61 2.0.3 Normal ← major version availablecargo-semver-checks — detect breaking API changes before publishing.
Essential for library crates:
cargo install cargo-semver-checks# Check if your changes are semver-compatiblecargo semver-checks# Output:# ✗ Function `parse_gpu_csv` is now private (was public)# → This is a BREAKING change. Bump MAJOR version.## ✗ Struct `GpuInfo` has a new required field `power_limit_w`# → This is a BREAKING change. Bump MAJOR version.## ✓ Function `parse_gpu_csv_v2` was added (non-breaking)cargo-tree — Dependency Visualization and Deduplication
cargo tree is built into Cargo (no installation needed) and is invaluable
for understanding your dependency graph:
# Full dependency treecargo tree# Find why a specific crate is includedcargo tree --invert --package openssl-sys# Shows all paths from your crate to openssl-sys# Find duplicate versionscargo tree --duplicates# Output:# syn v1.0.109# └── serde_derive v1.0.193## syn v2.0.48# ├── thiserror-impl v1.0.56# └── tokio-macros v2.2.0# Show only direct dependenciescargo tree --depth 1# Show dependency featurescargo tree --format "{p} {f}"# Count total dependenciescargo tree | wc -lDeduplication strategy: When cargo tree --duplicates shows the same crate
at two major versions, check if you can update the dependency chain to unify them.
Each duplicate adds compile time and binary size.
Application: Multi-Crate Dependency Hygiene
The the workspace uses [workspace.dependencies] for centralized
version management — an excellent practice. Combined with
cargo tree --duplicates for size
analysis, this prevents version drift and reduces binary bloat:
# Root Cargo.toml — all versions pinned in one place[workspace.dependencies]serde = { version = "1.0", features = ["derive"] }serde_json = { version = "1.0", features = ["preserve_order"] }regex = "1.10"thiserror = "1.0"anyhow = "1.0"rayon = "1.8"Recommended additions for the project:
# Add to CI pipeline:cargo deny init # One-time setupcargo deny check # Every PR — licenses, advisories, banscargo audit --deny warnings # Every push — vulnerability scanningcargo outdated --workspace # Weekly — track available updatesRecommended deny.toml for the project:
[advisories]vulnerability = "deny"yanked = "deny"[licenses]allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unicode-DFS-2016"]copyleft = "deny" # Hardware diagnostics tool — no copyleft[bans]multiple-versions = "warn" # Track duplicates, don't block yetwildcards = "deny"[sources]unknown-registry = "deny"unknown-git = "deny"Supply Chain Audit Pipeline
🏋️ Exercises
🟢 Exercise 1: Audit Your Dependencies
Run cargo audit and cargo deny init && cargo deny check on any Rust project. How many advisories are found? How many license categories are in your tree?
Solution
cargo audit# Note any advisories — often chrono, time, or older cratescargo deny initcargo deny list# Shows license breakdown: MIT (N), Apache-2.0 (N), etc.cargo deny check# Shows full audit across all four dimensions🟡 Exercise 2: Find and Eliminate Duplicate Dependencies
Run cargo tree --duplicates on a workspace. Find a crate that appears at two versions. Can you update Cargo.toml to unify them? Measure the compile-time and binary-size impact.
Solution
cargo tree --duplicates# Typical: syn 1.x and syn 2.x# Find who pulls in the old version:cargo tree --invert --package syn@1.0.109# Output: serde_derive 1.0.xxx -> syn 1.0.109# Check if a newer serde_derive uses syn 2.x:cargo update -p serde_derivecargo tree --duplicates# If syn 1.x is gone, you've eliminated a duplicate# Measure impact:time cargo build --release # Before and aftercargo bloat --release --crates | head -20Key Takeaways
cargo auditcatches known CVEs — run it on every push and on a daily schedulecargo denyenforces four policy dimensions: advisories, licenses, bans, and sources- Use
[workspace.dependencies]to centralize version management across a multi-crate workspace cargo tree --duplicatesreveals bloat; each duplicate adds compile time and binary sizecargo-vetis for high-security environments;cargo-denyis sufficient for most teams