Setting up cargo-audit and cargo-deny is less about discovering vulnerabilities and more about proactively preventing known bad actors from entering your Rust project.
Here’s how to integrate them into your workflow.
First, let’s get cargo-audit installed. This tool checks your project’s dependencies against known security advisories.
cargo install cargo-audit
Now, within your project directory, run cargo audit to see any immediate issues.
cargo audit
If cargo audit flags a vulnerability in a dependency, you’ll typically see output like this:
Auditing dependencies
The following crates have known security vulnerabilities:
- serde_derive:CVE-2023-XXXX - High severity - Deserialization vulnerability
affected versions: >= 1.0.0, < 1.0.150
fix available: 1.0.150
To update crates.io dependencies, run `cargo update` or specify a version in Cargo.toml.
The most common fix is to update the affected dependency. You can try a blanket update first:
cargo update
If that doesn’t bring in the patched version, or if you need to be more specific, edit your Cargo.toml file. Find the problematic dependency and explicitly set its version to the one that fixes the vulnerability. For example, to fix serde_derive to 1.0.150:
[dependencies]
serde_derive = "1.0.150"
After editing Cargo.toml, run cargo update again to ensure Cargo fetches the specified version. Then, re-run cargo audit to confirm the vulnerability is no longer present.
Next up is cargo-deny. This tool goes beyond just security advisories; it enforces policies on which crates are allowed in your project, based on criteria like licenses, features, and even specific crate names. This is crucial for compliance and preventing the introduction of unwanted code.
Install cargo-deny:
cargo install cargo-deny
cargo-deny works by reading a configuration file, typically named deny.toml, in your project’s root directory. Let’s create a basic one.
# deny.toml
[advisories]
# Enable checking against known vulnerabilities
ignore-purl = [] # List of PURL identifiers to ignore
# You can also ignore specific CVEs
# ignore-vulnerability = ["CVE-2023-XXXX"]
[licenses]
# Define allowed licenses. This is a common set for many projects.
# You can find a full list at https://spdx.org/licenses/
allowed = [
"Apache-2.0",
"MIT",
"ISC",
"Zlib",
]
# You can also define disallowed licenses
# disallow = ["GPL-2.0-only", "GPL-3.0-only"]
[crates]
# Explicitly allow or deny specific crates.
# This is useful for preventing specific problematic crates.
allow = []
deny = []
# You can also configure bans based on transitive dependencies.
# For example, to prevent any version of 'log' from being used.
# ban = ["log"]
[sources]
# Define allowed sources for crates.
# By default, crates.io is allowed.
allow-only = ["crates-io"]
Now, run cargo deny check in your project’s root.
cargo deny check
If your project uses a crate with a license not listed in deny.toml’s [licenses.allowed] section, you’ll get an error like this:
License check failed.
The following crates have disallowed licenses:
- my_crate: GPL-2.0-only
To fix this, you have two primary options:
-
Update the dependency: If the crate offers a version with an allowed license, update it in
Cargo.tomlas described forcargo-audit. -
Adjust
deny.toml: If the license is acceptable and you want to permit it, add it to theallowedlist in yourdeny.toml:[licenses] allowed = [ "Apache-2.0", "MIT", "ISC", "Zlib", "GPL-2.0-only", # Added to allow this license ]Then, re-run
cargo deny check.
Similarly, if a crate is explicitly denied in the [crates.deny] section, you’ll need to either remove it from the deny list in deny.toml (if you’ve decided it’s acceptable) or remove the crate from your project’s dependencies.
A powerful, often overlooked aspect of cargo-deny is its ability to enforce rules on transitive dependencies. For instance, you might have a rule that all dependencies must be dual-licensed under MIT or Apache-2.0. cargo-deny will recursively check every crate, even those pulled in indirectly by your direct dependencies. This means you can’t just check your direct dependencies; cargo-deny’s check command traverses the entire dependency tree.
The next step after setting up these tools is integrating them into your CI/CD pipeline to automate these checks on every commit or pull request.