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:

  1. Update the dependency: If the crate offers a version with an allowed license, update it in Cargo.toml as described for cargo-audit.

  2. Adjust deny.toml: If the license is acceptable and you want to permit it, add it to the allowed list in your deny.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.

Want structured learning?

Take the full Rust course →