cargo-deny

cargo-deny is a cargo plugin that lets you lint your project's dependency graph to ensure all your dependencies conform to your expectations and requirements.

Quickstart

Installs cargo-deny, initializes your project with a default configuration, then runs all of the checks against your project.

cargo install --locked cargo-deny && cargo deny init && cargo deny check

Command Line Interface

cargo-deny is intended to be used as a Command Line Tool, see the link for the available commands and options.

Checks

cargo-deny supports several classes of checks, see Checks for the available checks and their configuration options.

API

cargo-deny is primarily meant to be used as a cargo plugin, but a majority of its functionality is within a library whose docs you may view on docs.rs

GitHub Action

For GitHub projects, one can run cargo-deny automatically as part of continuous integration using a GitHub Action:

name: CI
on: [push, pull_request]
jobs:
  cargo-deny:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: EmbarkStudios/cargo-deny-action@v1

For more information, see cargo-deny-action repository.

Command Line Tool

cargo-deny can be used either as a command line tool or as a Rust crate. Let's focus on the command line tool capabilities first.

Install From Binaries

Precompiled binaries are provided for major platforms on a best-effort basis. Visit the releases page to download the appropriate version for your platform.

Installation on Arch Linux

cargo-deny is available in the Arch Linux extra repository, you can install it via pacman as shown below:

pacman -S cargo-deny

Install From Source

cargo-deny can also be installed from source.

Pre-requisites

cargo-deny is written in Rust and therefore needs to be compiled with Cargo. If you haven't already installed Rust, please go ahead and install it now.

cargo-deny depends on some crates that use C code, so you will also need to have a C toolchain available on your machine, such as gcc, clang, or msvc.

Install Crates.io version

Installing cargo-deny is relatively easy if you already have Rust and Cargo installed. You just have to type this snippet in your terminal:

cargo install --locked cargo-deny

This will fetch the source code for the latest release from Crates.io and compile it. You will have to add Cargo's bin directory to your PATH if you have not done so already.

Run cargo deny help in your terminal to verify if it works. Congratulations, you have installed cargo-deny!

Install Git version

The git version contains all the latest bug-fixes and features, that will be released in the next version on Crates.io, if you can't wait until the next release. You can build the git version yourself.

cargo install --locked --git https://github.com/EmbarkStudios/cargo-deny cargo-deny

Run cargo deny help in your terminal to verify if it works. Congratulations, you have installed cargo-deny!

CI Usage

We now have a Github Action for running cargo-deny on your Github repositories, check it out here.

If you don't want to use the action, you can manually download (or install) cargo-deny as described above, but here's an example script that you can copy to get you started.

#!/bin/bash
set -eu

NAME="cargo-deny"
VS="0.8.5"
DIR="/tmp/$NAME"

mkdir $DIR

# Download the tarball
curl -L -o $DIR/archive.tar.gz https://github.com/EmbarkStudios/$NAME/releases/download/$VS/$NAME-$VS-x86_64-unknown-linux-musl.tar.gz

# Unpack the tarball into the temp directory
tar -xzvf $DIR/archive.tar.gz --strip-components=1 -C $DIR

# Run cargo deny check in our current directory
$DIR/$NAME --context . -L debug check bans licenses advisories

Common options

The subcommands share some common options that can be used before the subcommand.

Options

--manifest-path

The path to a Cargo.toml file which is used as the context for operations.

--all-features (single crate or workspace)

Enables all features when determining which crates to consider. Works for both single crates and workspaces.

--no-default-features (single crate only)

Disables the default feature for a crate when determining which crates to consider.

--features (single crate only)

Space-separated list of features to enable when determining which crates to consider.

--workspace

Forces all workspace crates to be used as roots in the crate graph that we operate on, unless they are excluded by other means. By default, if you specify a virtual manifest, all crates in the workspace will be used as roots. However, if you specify a normal package manifest somewhere inside a workspace, only that crate will be used as a graph root, and only other workspaces crates it depends on will be included in the graph. If you want to specify a sub-crate in a workspace, but still include all other crates in the workspace, you can use this flag.

--exclude-dev

If set to true, all dev-dependencies, even one for workspace crates, are not included in the crate graph used for any of the checks.

--exclude

Exclude the specified package(s) from the crate graph. Unlike other cargo subcommands, it doesn't have to be used in conjunction with the --workspace flag. This flag may be specified multiple times.

This uses a similar (though slightly more strict) Package ID specification to other cargo subcommands.

Packages can also be excluded in your configuration files, specifying this on the command line will append the package ID to the list that may exist in your configuration.

-L, --log-level

The log level for messages, only log messages at or above the level will be emitted.

Possible values:

  • off - No output will be emitted
  • error
  • warn (default)
  • info
  • debug
  • trace

--format

The format of the output of both log and diagnostic messages.

Possible values:

  • human (default) - Output for the pesky humans
  • json - Each log message/diagnostic is outputted as a single line JSON object

--color

Whether coloring is applied to human-formatted output, using it on JSON output has no effect.

Possible values:

  • auto (default) - Coloring is applied if the output stream is a TTY
  • always - Coloring is always applied
  • never - No coloring is applied for any output

-t, --target

One or more platforms to filter crates with. If a dependency is target specific, it will be ignored if it does not match at least 1 of the specified targets. This overrides the top-level targets = [] configuration value.

--offline

Disables network I/O.

The init command

cargo-deny's configuration is a little bit complicated, so we provide the init command to create a configuration file from a template for you to give you a starting point for configuring how you want cargo-deny to lint your project.

The init command is used like this:

cargo deny init

Specify a path

The init command can take a path as an argument to use as path of the config instead of the default which is <cwd>/deny.toml.

cargo deny init path/to/config.toml

Template

A deny.toml file will be created in the current working directory that is a direct copy of this template.

# This template contains all of the possible sections and their default values

# Note that all fields that take a lint level have these possible values:
# * deny - An error will be produced and the check will fail
# * warn - A warning will be produced, but the check will not fail
# * allow - No warning or error will be produced, though in some cases a note
# will be

# The values provided in this template are the default values that will be used
# when any section or field is not specified in your own configuration

# Root options

# The graph table configures how the dependency graph is constructed and thus
# which crates the checks are performed against
[graph]
# If 1 or more target triples (and optionally, target_features) are specified,
# only the specified targets will be checked when running `cargo deny check`.
# This means, if a particular package is only ever used as a target specific
# dependency, such as, for example, the `nix` crate only being used via the
# `target_family = "unix"` configuration, that only having windows targets in
# this list would mean the nix crate, as well as any of its exclusive
# dependencies not shared by any other crates, would be ignored, as the target
# list here is effectively saying which targets you are building for.
targets = [
    # The triple can be any string, but only the target triples built in to
    # rustc (as of 1.40) can be checked against actual config expressions
    #"x86_64-unknown-linux-musl",
    # You can also specify which target_features you promise are enabled for a
    # particular target. target_features are currently not validated against
    # the actual valid features supported by the target architecture.
    #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
]
# When creating the dependency graph used as the source of truth when checks are
# executed, this field can be used to prune crates from the graph, removing them
# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate
# is pruned from the graph, all of its dependencies will also be pruned unless
# they are connected to another crate in the graph that hasn't been pruned,
# so it should be used with care. The identifiers are [Package ID Specifications]
# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)
#exclude = []
# If true, metadata will be collected with `--all-features`. Note that this can't
# be toggled off if true, if you want to conditionally enable `--all-features` it
# is recommended to pass `--all-features` on the cmd line instead
all-features = false
# If true, metadata will be collected with `--no-default-features`. The same
# caveat with `all-features` applies
no-default-features = false
# If set, these feature will be enabled when collecting metadata. If `--features`
# is specified on the cmd line they will take precedence over this option.
#features = []

# The output table provides options for how/if diagnostics are outputted
[output]
# When outputting inclusion graphs in diagnostics that include features, this
# option can be used to specify the depth at which feature edges will be added.
# This option is included since the graphs can be quite large and the addition
# of features from the crate(s) to all of the graph roots can be far too verbose.
# This option can be overridden via `--feature-depth` on the cmd line
feature-depth = 1

# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# The path where the advisory databases are cloned/fetched into
#db-path = "$CARGO_HOME/advisory-dbs"
# The url(s) of the advisory databases to use
#db-urls = ["https://github.com/rustsec/advisory-db"]
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
    #"RUSTSEC-0000-0000",
    #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
    #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
    #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
# See Git Authentication for more information about setting up git authentication.
#git-fetch-with-cli = true

# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
# List of explicitly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
    #"MIT",
    #"Apache-2.0",
    #"Apache-2.0 WITH LLVM-exception",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
    # Each entry is the crate and version constraint, and its specific allow
    # list
    #{ allow = ["Zlib"], crate = "adler32" },
]

# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
# The package spec the clarification applies to
#crate = "ring"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]

[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries.
# To see how to mark a crate as unpublished (to the official registry),
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
ignore = false
# One or more private registries that you might publish crates to, if a crate
# is only published to private registries, and ignore is true, the crate will
# not have its license(s) checked
registries = [
    #"https://sekretz.com/registry
]

# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "allow"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# The default lint level for `default` features for crates that are members of
# the workspace that is being checked. This can be overridden by allowing/denying
# `default` on a crate-by-crate basis if desired.
workspace-default-features = "allow"
# The default lint level for `default` features for external crates that are not
# members of the workspace. This can be overridden by allowing/denying `default`
# on a crate-by-crate basis if desired.
external-default-features = "allow"
# List of crates that are allowed. Use with care!
allow = [
    #"ansi_term@0.11.0",
    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" },
]
# List of crates to deny
deny = [
    #"ansi_term@0.11.0",
    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" },
    # Wrapper crates can optionally be specified to allow the crate when it
    # is a direct dependency of the otherwise banned crate
    #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] },
]

# List of features to allow/deny
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#[[bans.features]]
#crate = "reqwest"
# Features to not allow
#deny = ["json"]
# Features to allow
#allow = [
#    "rustls",
#    "__rustls",
#    "__tls",
#    "hyper-rustls",
#    "rustls",
#    "rustls-pemfile",
#    "rustls-tls-webpki-roots",
#    "tokio-rustls",
#    "webpki-roots",
#]
# If true, the allowed features must exactly match the enabled feature set. If
# this is set there is no point setting `deny`
#exact = true

# Certain crates/versions that will be skipped when doing duplicate detection.
skip = [
    #"ansi_term@0.11.0",
    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" },
]
# Similarly to `skip` allows you to skip certain crates during duplicate
# detection. Unlike skip, it also includes the entire tree of transitive
# dependencies starting at the specified crate, up to a certain depth, which is
# by default infinite.
skip-tree = [
    #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies
    #{ crate = "ansi_term@0.11.0", depth = 20 },
]

# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "warn"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "warn"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = []

[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
github = [""]
# 1 or more gitlab.com organizations to allow git sources for
gitlab = [""]
# 1 or more bitbucket.org organizations to allow git sources for
bitbucket = [""]

The check command

The check command is the primary subcommand of cargo-deny as it is what actually runs through all of the crates in your project and checks them against your configuration.

Args

<which>

The check(s) to perform. By default, all checks will be performed, unless one or more checks are specified here.

See checks for the list of available checks.

Options

-A, --allow <ALLOW>

Set lint allowed

--audit-compatible-output

To ease transition from cargo-audit to cargo-deny, this flag will tell cargo-deny to output the exact same output as cargo-audit would, to stdout instead of stderr, just as with cargo-audit.

Note that this flag only applies when the output format is JSON, and note that since cargo-deny supports multiple advisory databases, instead of a single JSON object, there will be 1 for each unique advisory database.

-c, --config <CONFIG>

Path to the config to use

Defaults to <cwd>/deny.toml if not specified

-d, --disable-fetch

Disable fetching of the advisory database

When running the advisories check, the configured advisory database will be fetched and opened. If this flag is passed, the database won't be fetched, but an error will occur if it doesn't already exist locally.

This option is also set if the --offline flag is used in the global options.

--exclude-dev

If set to true, all dev-dependencies, even one for workspace crates, are not included in the crate graph used for any of the checks.

-D, --deny <DENY>

Set lint denied

--feature-depth <FEATURE_DEPTH>

Specifies the depth at which feature edges are added in inclusion graphs

-g, --graph <GRAPH>

Path to graph_output root directory

If set, a dotviz graph will be created for whenever multiple versions of the same crate are detected.

Each file will be created at <dir>/graph_output/<crate_name>.dot. <dir>/graph_output/* is deleted and recreated each run.

--hide-inclusion-graph

Hides the inclusion graph when printing out info for a crate

By default, if a diagnostic message pertains to a specific crate, cargo-deny will append an inverse dependency graph to the diagnostic to show you how that crate was pulled into your project.

some diagnostic message

the-crate
├── a-crate
└── b-crate
    └── c-crate

-s, --show-stats

Show stats for all the checks, regardless of the log-level

-W, --warn <WARN>

Set lint warnings

Exit Codes

As of 0.14.1, the exit code for the check command is a bitset of the checks that were executed and had 1 or more errors.

A script or program can use the following values to determine exactly which check(s) failed.

  • advisories - 0x1
  • bans - 0x2
  • licenses - 0x4
  • sources - 0x8

The list command

Similarly to cargo-license, list prints out the license information for each crate.

Options

-f, --format

The format of the output

  • human (default) - Simple format where each crate or license is its own line
  • json
  • tsv

--color

Output coloring, only applies to the human format.

  • auto (default) - Only colors if stdout is a TTY
  • always - Always emits colors
  • never - Never emits colors

Colors:

  • SPDX identifier - blue
  • Crate with 1 license - white
  • Crate with 2 or more licenses - yellow
  • Crate with 0 licenses - yellow

-l, --layout

The layout of the output. Does not apply to the tsv format.

  • license (default) - Each license acts as the key, and the values are all of the crates that use that license
  • crate - Each crate is a key, and the values are the list of licenses it uses.

-t, --threshold

The confidence threshold required for assigning a license identifier to a license text file. See the license configuration for more information.

  • layout = license, format = human (default)

Imgur

  • layout = crate, format = human

Imgur

  • layout = license, format = json

Imgur

  • layout = license, format = tsv

Imgur

Checks

cargo-deny supports several different classes of checks that can be performed on your project's crate graph. By default, cargo deny check will execute all of the supported checks, falling back to the default configuration for that check if one is not explicitly specified.

licenses

Checks the license information for each crate.

bans

Checks for specific crates in your graph, as well as duplicates.

advisories

Checks advisory databases for crates with security vulnerabilities, or that have been marked as Unmaintained, or which have been yanked from their source registry.

sources

Checks the source location for each crate.

config

The top level config for cargo-deny, by default called deny.toml.

Example - cargo-deny's own configuration

[graph]
# cargo-deny is really only ever intended to run on the "normal" tier-1 targets
targets = [
    "x86_64-unknown-linux-gnu",
    "aarch64-unknown-linux-gnu",
    "x86_64-unknown-linux-musl",
    "aarch64-apple-darwin",
    "x86_64-apple-darwin",
    "x86_64-pc-windows-msvc",
]
all-features = true

[advisories]
version = 2
ignore = [
    { id = "RUSTSEC-2022-0092", reason = "askalono always provides valid utf-8 files from a cache, this is not relevant" },
]

[bans]
multiple-versions = "deny"
wildcards = 'deny'
deny = [
    { crate = "git2", use-instead = "gix" },
    { crate = "openssl", use-instead = "rustls" },
    { crate = "openssl-sys", use-instead = "rustls" },
    "libssh2-sys",
    { crate = "cmake", use-instead = "cc" },
    { crate = "windows", reason = "bloated and unnecessary", use-instead = "ideally inline bindings, practically, windows-sys" },
]
skip = [
    { crate = "bitflags@1.3.2", reason = "https://github.com/seanmonstar/reqwest/pull/2130 should be in the next version" },
    { crate = "winnow@0.5.40", reason = "gix 0.59 was yanked, see https://github.com/Byron/gitoxide/issues/1309" },
    { crate = "heck@0.4.1", reason = "strum_macros uses this old version" },
]
skip-tree = [
    { crate = "windows-sys@0.48.0", reason = "a foundational crate for many that bumps far too frequently to ever have a shared version" },
]

[sources]
unknown-registry = "deny"
unknown-git = "deny"

[licenses]
version = 2
# We want really high confidence when inferring licenses from text
confidence-threshold = 0.93
allow = [
    "Apache-2.0",
    "Apache-2.0 WITH LLVM-exception",
    "MIT",
    "MPL-2.0",
    "BSD-3-Clause",
    "ISC",
]
exceptions = [
    # Use exceptions for these as they only have a single user
    { allow = ["Zlib"], crate = "tinyvec" },
    { allow = ["Unicode-DFS-2016"], crate = "unicode-ident" },
    { allow = ["OpenSSL"], crate = "ring" },
]

# Sigh
[[licenses.clarify]]
crate = "ring"
# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
# https://spdx.org/licenses/OpenSSL.html
# ISC - Both BoringSSL and ring use this for their new files
# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
# license, for third_party/fiat, which, unlike other third_party directories, is
# compiled into non-test libraries, is included below."
# OpenSSL - Obviously
expression = "ISC AND MIT AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]

[[licenses.clarify]]
crate = "webpki"
expression = "ISC"
license-files = [{ path = "LICENSE", hash = 0x001c7e6c }]

# Actually "ISC-style"
[[licenses.clarify]]
crate = "rustls-webpki"
expression = "ISC"
license-files = [{ path = "LICENSE", hash = 0x001c7e6c }]

The graph field (optional)

The graph tables provides configuration options for how the dependency graph that the various checks are executed against is constructed.

[graph]
targets = [
    "x86_64-unknown-linux-gnu",
    { triple = "aarch64-apple-darwin" },
    { triple = "x86_64-pc-windows-msvc", features = ["sse2"] },
]
exclude = ["some-crate@0.1.0"]
all-features = true
no-default-features = false
features = ["some-feature"]
exclude-dev = true

The targets field (optional)

By default, cargo-deny will consider every single crate that is resolved by cargo, including target specific dependencies eg

[target.x86_64-pc-windows-msvc.dependencies]
winapi = "0.3.8"

[target.'cfg(target_os = "fuchsia")'.dependencies]
fuchsia-cprng = "0.1.1"

But unless you are actually targeting x86_64-fuchsia or aarch64-fuchsia, the fuchsia-cprng is never actually going to be compiled or linked into your project, so checking it is pointless for you.

The targets field allows you to specify one or more targets which you actually build for. Every dependency link to a crate is checked against this list, and if none of the listed targets satisfy the target constraint, the dependency link is ignored. If a crate has no dependency links to it, it is not included into the crate graph that the checks are executed against.

The targets.triple field (optional) or "<triple_string>"

The target triple for the target you wish to filter target specific dependencies with. If the target triple specified is not one of the targets builtin to rustc, the configuration check for that target will be limited to only the raw [target.<target-triple>.dependencies] style of target configuration, as cfg() expressions require us to know the details about the target.

The targets.features field (optional)

Rust cfg() expressions support the target_feature = "feature-name" predicate, but at the moment, the only way to actually pass them when compiling is to use the RUSTFLAGS environment variable. The features field allows you to specify 1 or more target_features you plan to build with, for a particular target triple. At the time of this writing, cargo-deny does not attempt to validate that the features you specify are actually valid for the target triple, but this is planned.

The exclude field (optional)

Just as with the --exclude command line option, this field allows you to specify one or more Package ID specifications that will cause the crate(s) in question to be excluded from the crate graph that is used for the operation you are performing.

Note that excluding a crate is recursive, if any of its transitive dependencies are only referenced via the excluded crate, they will also be excluded from the crate graph.

The all-features field (optional)

If set to true, --all-features will be used when collecting metadata.

The no-default-features field (optional)

If set to true, --no-default-features will be used when collecting metadata.

The features field (optional)

If set, and --features is not specified on the cmd line, these features will be used when collecting metadata.

The exclude-dev field (optional)

If set to true, all dev-dependencies, even one for workspace crates, are not included in the crate graph used for any of the checks. This option can also be enabled on cmd line with --exclude-dev either before or after the check subcommand.

The output field (optional)

The feature-depth field (optional)

The maximum depth that features will be displayed when inclusion graphs are included in diagnostics, unless specified via --feature-depth on the command line. Only applies to diagnostics that actually print features. If not specified defaults to 1.

Package Specs

Many configuration options require a package specifier at a minimum, which we'll describe here. The options that use package specifiers will be called out in their individual documentation. We'll use the bans.deny option in the following examples.

String format

If the particular only requires a package spec at a minimum, then the string format can be used, which comes in three forms.

Simple

# Will match any version of the simple crate
deny = ["simple"]

The simplest string is one which is just the crate name. In this case, the version requirement used when checking will be * meaning it will match against all versions of that crate in the graph.

With Version Requirements

# Will match only this versions of the simple crate that match the predicate(s)
deny = ["simple:<=0.1,>0.2"]

If you want to apply version requirements (predicates) to the crate, simply append them following a : separator.

Exact

# Will match only this exact version of the simple crate
deny = [
    "simple@0.1.0",
    # This is semantically equivalent to the above
    "simple:=0.1.0",
]

The exact form is a specialization of the version requirements, where the semver after the @ is transformed to be = (Exact).

Table format

Crate format

deny = [
    { crate = "simple@0.1.0" }, # equivalent to "simple@0.1.0"
    { crate = "simple", wrappers = ["example"] },
]

The crate format is a replacement for the old name and/or version table format. It uses the string format described above in a single crate key.

Old format

deny = [
    { name = "simple" },
    { name = "simple", version = "*" }
    { name = "simple", wrappers = ["example"] }
]

The old format uses a required name key and an optional version key. This format is deprecated and should not be used.

The [licenses] section

See the licenses config for more info.

The [bans] section

See the bans config for more info.

The [advisories] section

See the advisories config for more info.

The [sources] section

See the sources config for more info.

advisories

The advisories check is used to detect issues for crates by looking in an advisory database.

cargo deny check advisories

Use Case - Detecting security vulnerabilities

Security vulnerabilities are generally considered "not great" by most people, luckily, Rust has a great advisory database which cargo-deny can use to check that you don't have any crates with (known) security vulnerabilities.

You can also use your own advisory databases instead of, or in addition to, the above default, as long as it follows the same format.

Use Case - Detecting unmaintained crates

The advisory database also contains advisories for unmaintained crates, which in most cases users will want to avoid in favor of more actively maintained crates.

Example output

advisories output

The [advisories] section

Contains all of the configuration for cargo deny check advisories

Example Config

[advisories]
db-path = "~/.cargo/advisory-dbs"
db-urls = ["https://github.com/RustSec/advisory-db"]
vulnerability = "deny"
unmaintained = "warn"
unsound = "warn"
yanked = "warn"
notice = "warn"
ignore = [
    "RUSTSEC-0000-0000",
    "crate@0.1",
    { crate = "yanked", reason = "a new version has not been released" },
]
severity-threshold = "medium"

The db-urls field (optional)

URLs to one or more advisory databases.

Default: RustSec Advisory DB

The db-path field (optional)

Path to the root directory into which one or more advisory databases are cloned into.

This value supports basic shell expansion:

Note that the path must be valid utf-8, after expansion.

Default: $CARGO_HOME/advisory-dbs

The version field (optional)

version = 2

The advisories section has an upcoming breaking change, with deprecation warnings for several fields that will be removed. Setting version = 2 will opt-in to the future default behavior.

The breaking change is as follows:

  • vulnerability - Removed, all vulnerability advisories now emit errors.
  • unmaintained - Removed, all unmaintained advisories now emit errors.
  • unsound - Removed, all unsound advisories now emit errors.
  • notice - Removed, all notice advisories now emit errors.
  • severity-threshold - Removed, all vulnerability advisories now emit errors.

As before, if you want to ignore a specific advisory, add it to the ignore field.

The vulnerability field (optional)

DEPRECATED

Determines what happens when a crate with a security vulnerability is encountered.

  • deny (default) - Will emit an error with details about each vulnerability, and fail the check.
  • warn - Prints a warning for each vulnerability, but does not fail the check.
  • allow - Prints a note about the security vulnerability, but does not fail the check.

The unmaintained field (optional)

DEPRECATED

Determines what happens when a crate with an unmaintained advisory is encountered.

  • deny - Will emit an error with details about the unmaintained advisory, and fail the check.
  • warn (default) - Prints a warning for each unmaintained advisory, but does not fail the check.
  • allow - Prints a note about the unmaintained advisory, but does not fail the check.

The unsound field (optional)

DEPRECATED

Determines what happens when a crate with an unsound advisory is encountered.

  • deny - Will emit an error with details about the unsound advisory, and fail the check.
  • warn (default) - Prints a warning for each unsound advisory, but does not fail the check.
  • allow - Prints a note about the unsound advisory, but does not fail the check.

The yanked field (optional)

Determines what happens when a crate with a version that has been yanked from its source registry is encountered.

  • deny - Will emit an error with the crate name and version that was yanked, and fail the check.
  • warn (default) - Prints a warning with the crate name and version that was yanked, but does not fail the check.
  • allow - Prints a note about the yanked crate, but does not fail the check.

The notice field (optional)

DEPRECATED

Determines what happens when a crate with a notice advisory is encountered.

NOTE: As of 2019-12-17 there are no notice advisories in the RustSec Advisory DB

  • deny - Will emit an error with details about the notice advisory, and fail the check.
  • warn (default) - Prints a warning for each notice advisory, but does not fail the check.
  • allow - Prints a note about the notice advisory, but does not fail the check.

The ignore field (optional)

ignore = [
   "RUSTSEC-0000-0000",
   { id = "RUSTSEC-0000-0000", reason = "this vulnerability does not affect us as we don't use the particular code path" },
   "yanked@0.1.1",
   { crate = "yanked-crate@0.1.1", reason = "a semver compatible version hasn't been published yet" },
]

Every advisory in the advisory database contains a unique identifier, eg. RUSTSEC-2019-0001. Putting an identifier in this array will cause the advisory to be treated as a note, rather than a warning or error.

In addition, yanked crate versions can be ignored by specifying a PackageSpec with an optional reason.

The severity-threshold field (optional)

DEPRECATED

The threshold for security vulnerabilities to be turned into notes instead of warnings or errors, depending upon its CVSS score. So having a high threshold means some vulnerabilities might not fail the check, but having a log level >= info will mean that a note will be printed instead of a warning or error, depending on [advisories.vulnerability].

  • None (default) - CVSS Score 0.0
  • Low - CVSS Score 0.1 - 3.9
  • Medium - CVSS Score 4.0 - 6.9
  • High - CVSS Score 7.0 - 8.9
  • Critical - CVSS Score 9.0 - 10.0

The git-fetch-with-cli field (optional)

Similar to cargo's net.git-fetch-with-cli, this field allows you to opt-in to fetching advisory databases with the git CLI rather than using gix.

  • false (default) - Fetches advisory databases via gix
  • true - Fetches advisory databases using git. Git must be installed and in PATH.

The maximum-db-staleness field (optional)

A duration in RFC3339 format that specifies the maximum amount of time that can pass before the database is considered stale and an error is emitted. This is only checked when advisory database fetching has been disabled via the --offline or check --disable-fetch flags, as otherwise the database is always cloned or fetched to be up to date with the remote git repository.

The default if not specified is the same value that cargo-audit uses, and cargo-deny has been using, which is P90D, or 90 days.

The RFC3339 duration format is...not well documented. The official grammar is as follows:

   dur-second        = 1*DIGIT "S"
   dur-minute        = 1*DIGIT "M" [dur-second]
   dur-hour          = 1*DIGIT "H" [dur-minute]
   dur-time          = "T" (dur-hour / dur-minute / dur-second)
   dur-day           = 1*DIGIT "D"
   dur-week          = 1*DIGIT "W"
   dur-month         = 1*DIGIT "M" [dur-day]
   dur-year          = 1*DIGIT "Y" [dur-month]
   dur-date          = (dur-day / dur-month / dur-year) [dur-time]

   duration          = "P" (dur-date / dur-time / dur-week)

However, as far as I can tell, there are no official spec compliance tests one can run for the duration formation, and several parsers I found written in other languages seemed to...not actually properly follow the grammar, so the implementation in cargo-deny may be wrong according to the spec, but at least it will be consistently wrong.

Note that while the spec supports , as a decimal separator, for simplicity cargo-deny only supports . as a decimal separator.

One final note, there are 2 units available in the format that are not exact, namely, year 'Y' and month 'M'. It's not recommended to use either of them for that reason, but if you do they are calculated as follows.

  • 1 year = 365 days
  • 1 month = 30.43 days

Advisories Diagnostics

vulnerability

A vulnerability advisory was detected for a crate.

notice

A notice advisory was detected for a crate.

unmaintained

An unmaintained advisory was detected for a crate.

unsound

An unsound advisory was detected for a crate.

yanked

A crate using a version that has been yanked from the registry index was detected.

index-failure

An error occurred trying to read or update the registry index (typically crates.io) so cargo-deny was unable to check the current yanked status for any crate.

advisory-not-detected

An advisory in advisories.ignore didn't apply to any crate. This could happen if the advisory was withdrawn, or the version of the crate no longer falls within the range of affected versions the advisory applies to.

unknown-advisory

An advisory in advisories.ignore wasn't found in any of the configured advisory databases, usually indicating a typo, as advisories, at the moment, are never deleted from the database, at least the canonical advisory-db.

bans

The bans check is used to deny (or allow) specific crates, as well as detect and handle multiple versions of the same crate.

cargo deny check bans

Use Case - Denying specific crates

Sometimes, certain crates just don't fit in your project, so you have to remove them. However, nothing really stops them from sneaking back in due to innocuous changes like doing a cargo update and getting it transitively, or even forgetting to set default-features = false, features = ["feature-without-the-thing"] when the crate is pulled in via the default features of a crate you already depend on, in your entire workspace.

For example, we previously depended on OpenSSL as it is the "default" for many crates that provide TLS. This was extremely annoying as it required us to have OpenSSL development libraries installed on Windows, for both individuals and CI. We moved all of our dependencies to use the much more streamlined native-tls or ring crates instead, and now we can make sure that OpenSSL doesn't return from the grave by accident.

Use Case - Duplicate version detection

The larger your project and number of external dependencies, the likelihood that you will have multiple versions of the same crate rises. This is due to two fundamental aspects of the Rust ecosystem.

  1. Cargo's dependency resolution tries to solve all the version constraints to a crate to the same version, but is totally ok with using multiple versions if it is unable to.
  2. Rust has a huge (ever growing) number of crates. Every maintainer has different amounts of time and energy they can spend on their crate, not to mention different philosophies on dependencies and how often (or not) they should be updated, so it is inevitable that crates will not always agree on which version of another crate they want to use.

This tradeoff of allowing multiple version of the same crate is one of the reasons that cargo is such a pleasant experience for many people new to Rust, but as with all tradeoffs, it does come with costs.

  1. More packages must be fetched, which especially impacts CI, as well as devs.
  2. Compile times increase, which impacts CI and devs.
  3. Target directory size increases, which can impact devs, or static CI environments.
  4. Final binary size will also tend to increase, which can impact users.

Normally, you will not really notice that you have multiple versions of the same crate unless you constantly watch your build log, but as mentioned above, it does introduce paper cuts into your workflows.

The intention of duplicate detection in cargo-deny is not to "correct" cargo's behavior, but rather to draw your attention to duplicates so that you can make an informed decision about how to handle the situation.

  • Maybe you want to open up a PR on a crate to use a version of the duplicate that is aligned with the rest of the ecosystem.
  • Maybe the crate has actually already been updated, but the maintainer hasn't published a new version yet, and you can ask if they can publish a new one.
  • Maybe, even though the versions are supposedly incompatible according to semver, they actually aren't, and you can temporarily patch 1 or more crates to use a different version requirement without actually change the crates' code itself.
  • Sometimes having the "latest and greatest" is not really that important for every version, and you can just specify a lower version in your own project that matches the transitive constraint(s).
  • And finally, you don't care about a particular case of multiple versions, so you just tell cargo-deny to ignore one or more of the specific versions, and the situation will eventually resolve itself.

Example output

bans output

The [bans] section

Contains all of the configuration for cargo deny check bans

Example Config

[bans]
multiple-versions = "deny"
wildcards = "deny"
allow-wildcard-paths = true
highlight = "simplest-path"
workspace-default-features = "warn"
external-default-features = "deny"
allow = [
    { name = "all-versionsa" },
    "version-rangea:<0.1.1",
    "specific-versionb@0.1.2",
    "any-version",
]
deny = [
    "specific-versiond@0.1.9",
    { name = "all-versionsd", wrappers = [
        "specific-versiona",
    ], reason = "we want to get rid of this crate but there is still one user of it" },
]
skip-tree = [{ name = "blah", depth = 20 }]

[[bans.skip]]
name = "rand"
version = "=0.6.5"

[[bans.features]]
name = "featured-krate"
version = "1.0"
deny = ["bad-feature"]
allow = ["good-feature"]
exact = true
reason = "`bad-feature` is bad"

[bans.build]
allow-build-scripts = [{ name = "all-versionsa" }]
executables = "warn"
interpreted = "deny"
script-extensions = ["cs"]
enable-builtin-globs = true
include-dependencies = true
include-workspace = true
include-archives = true

[[bans.build.bypass]]
name = "allversionsa"
build-script = "5392f0e58ad06e089462d93304dfe82337acbbefb87a0749a7dc2ed32af04af7"
required-features = ["feature-used-at-build-time"]
allow-globs = ["scripts/*.cs"]
allow = [
    { path = "bin/x86_64-linux", checksum = "5392f0e58ad06e089462d93304dfe82337acbbefb87a0749a7dc2ed32af04af7" },
]

The multiple-versions field (optional)

Determines what happens when multiple versions of the same crate are encountered.

  • deny - Will emit an error for each crate with duplicates and fail the check.
  • warn (default) - Prints a warning for each crate with duplicates, but does not fail the check.
  • allow - Ignores duplicate versions of the same crate.

The multiple-versions-include-dev field (optional)

If true, dev-dependencies are included when checking for multiple versions of crates. By default this is false, and any crates that are only reached via dev dependency edges are ignored when checking for multiple versions. Note that this also means that skip and skip tree are not used, which may lead to warnings about unused configuration.

The wildcards field (optional)

Determines what happens when a dependency is specified with the * (wildcard) version.

  • deny - Will emit an error for each crate specified with a wildcard version.
  • warn (default) - Prints a warning for each crate with a wildcard version, but does not fail the check.
  • allow - Ignores all wildcard version specifications.

The allow-wildcard-paths field (optional)

If specified, alters how the wildcard field behaves:

  • path or git dependencies in private crates will no longer emit a warning or error.
  • path or git dev-dependencies in both public and private crates will no longer emit a warning or error.
  • path or git dependencies and build-dependencies in public crates will continue to produce warnings and errors.

Being limited to private crates is due to crates.io not allowing packages to be published with path or git dependencies except for dev-dependencies.

The highlight field (optional)

When multiple versions of the same crate are encountered and multiple-versions is set to warn or deny, using the -g <dir> option will print out a dotgraph of each of the versions and how they were included into the graph. This field determines how the graph is colored to help you quickly spot good candidates for removal or updating.

  • lowest-version - Highlights the path to the lowest duplicate version. Highlighted in red
  • simplest-path - Highlights the path to the duplicate version with the fewest number of total edges to the root of the graph, which will often be the best candidate for removal and/or upgrading. Highlighted in blue.
  • all - Highlights both the lowest-version and simplest-path. If they are the same, they are only highlighted in red.

Imgur

The deny field (optional)

deny = ["package-spec"]

Determines specific crates that are denied. Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

The wrappers field (optional)

deny = [{ crate = "crate-you-don't-want:<=0.7.0", wrappers = ["this-can-use-it"] }]

This field allows specific crates to have a direct dependency on the banned crate but denies all transitive dependencies on it.

The deny-multiple-versions field (optional)

multiple-versions = 'allow'
deny = [{ crate = "crate-you-want-only-one-version-of", deny-multiple-versions = true }]

This field allows specific crates to deny multiple versions of themselves, but allowing or warning on multiple versions for all other crates. This field cannot be set simultaneously with wrappers.

The deny.reason field (optional)

deny = [{ crate = "package-spec", reason = "the reason this crate is banned"}]

This field provides the reason the crate is banned as a string (eg. a simple message or even a url) that is surfaced in diagnostic output so that the user does not have to waste time digging through history or asking maintainers why this is the case.

The deny.use-instead field (optional)

deny = [{ crate = "openssl", use-instead = "rustls"}]

This is a shorthand for the most common case for banning a particular crate, which is that your project has chosen to use a different crate for that functionality.

The allow field (optional)

deny = ["package-spec"]

Determines specific crates that are allowed. If the allow list has one or more entries, then any crate not in that list will be denied, so use with care. Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

The allow.reason field (optional)

allow = [{ crate = "package-spec", reason = "the reason this crate is allowed"}]

This field provides the reason the crate is allowed as a string (eg. a simple message or even a url) that is surfaced in diagnostic output so that the user does not have to waste time digging through history or asking maintainers why this is the case.

The external-default-features field (optional)

Determines the lint level used for when the default feature is enabled on a crate not in the workspace. This lint level will can then be overridden on a per-crate basis if desired.

For example, if an-external-crate had the default feature enabled it could be explicitly allowed.

[bans]
external-default-features = "deny"

[[bans.features]]
crate = "an-external-crate"
allow = ["default"]

The workspace-default-features field (optional)

The workspace version of external-default-features.

[bans]
external-default-features = "allow"

[[bans.features]]
crate = "a-workspace-crate"
deny = ["default"]

The features field (optional)

[[bans.features]]
crate = "featured-krate:1.0"
deny = ["bad-feature"]
allow = ["good-feature"]
exact = true

Allows specification of crate specific allow/deny lists of features. Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

The features.deny field (optional)

Denies specific features for the crate.

The features.allow field (optional)

Allows specific features for the crate, enabled features not in this list are denied.

The features.exact field (optional)

If specified, requires that the features in allow exactly match the features enabled on the crate, and will fail if features are allowed that are not enabled.

The skip field (optional)

skip = [
    "package-spec",
    { crate = "package-spec", reason = "an old version is used by crate-x, see <PR link> for updating it" },
]

When denying duplicate versions, it's often the case that there is a window of time where you must wait for, for example, PRs to be accepted and new version published, before 1 or more duplicates are gone. The skip field allows you to temporarily ignore a crate during duplicate detection so that no errors are emitted, until it is no longer need.

It is recommended to use specific version constraints for crates in the skip list, as cargo-deny will emit warnings when any entry in the skip list no longer matches a crate in your graph so that you can cleanup your configuration.

Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

The skip-tree field (optional)

skip-tree = [
    "windows-sys<=0.52", # will skip this crate and _all_ direct and transitive dependencies
    { crate = "windows-sys<=0.52", reason = "several crates use the outdated 0.42 and 0.45 versions" },
    { crate = "windows-sys<=0.52", depth = 3, reason = "several crates use the outdated 0.42 and 0.45 versions" },
]

When dealing with duplicate versions, it's often the case that a particular crate acts as a nexus point for a cascade effect, by either using bleeding edge versions of certain crates while in alpha or beta, or on the opposite end of the spectrum, a crate is using severely outdated dependencies while much of the rest of the ecosystem has moved to more recent versions. In both cases, it can be quite tedious to explicitly skip each transitive dependency pulled in by that crate that clashes with your other dependencies, which is where skip-tree comes in.

skip-tree entries are similar to skip in that they are used to specify a crate name and version range that will be skipped, but they also have an additional depth field used to specify how many levels from the crate will also be skipped. A depth of 0 would be the same as specifying the crate in the skip field.

Note that by default, the depth is infinite.

Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

NOTE: skip-tree is a very big hammer, and should be used with care.

The build field (optional)

The build field contains configuration for raising diagnostics for crates that execute at compile time, either because they have a build script, or they are a procedural macro. The configuration is (currently) focused on diagnostics around specific file types, as configured via extension glob patterns, as well as executables, either native or in the form of interpreted shebang scripts.

While the intention of this configuration is to raise awareness of crates that have or use precompiled binaries or scripts, or otherwise contain file types that you want to be aware of, the compile time crate linting supplied by cargo-deny does NOT protect you from actively malicious code.

A quick run down of things that cargo-deny WILL NOT DETECT.

  • The crate just straight up does bad things like uploading your SSH keys to a remote server using vanilla rust code
  • The crate contains compressed, or otherwise obfuscated executable binaries
  • The build script uses include!() for code that is benign in one version, then replaces it with something malicious without triggering a checksum mismatch on the build script contents itself.
  • A build time dependency of a non-malicious crate does any of the above.
  • Tons of other stuff I haven't thought of because I am not a security person

So all this is to say, cargo-deny (currently) is only really useful for analyzing when crates have native executables, and/or the crate maintainers have either forgotten or purposefully left helper scripts for their CI/release management/etc in the crate source that are not actually ever executed automatically.

The allow-build-scripts field (optional)

Specifies all the crates that are allowed to have a build script. If this option is omitted, all crates are allowed to have a build script, and if this option is set to an empty list, no crate is allowed to have a build script.

The executables field (optional)

This controls how native executables are handled. Note this check is done by actually reading the file headers from disk so that this check works on Windows as well, ie the executable bit is irrelevant.

  • deny (default) - Emits an error when native executables are detected.
  • warn - Prints a warning when native executables are detected, but does not fail the check.
  • allow - Prints a note when native executables are detected, but does not fail the check.

This check currently only handles the major executable formats.

The interpreted field (optional)

This controls how interpreted scripts are handled. Note this check is done by actually reading the file header from disk so that this check works on Windows as well, ie the executable bit is irrelevant.

  • deny - Emits an error when interpreted scripts are detected.
  • warn - Prints a warning when interpreted scripts are detected, but does not fail the check.
  • allow (default) - Prints a note when interpreted scripts are detected, but does not fail the check.

The script-extensions field (optional)

If supplied scans crates that execute at compile time for any files with the specified extension(s), emitting an error for every one that matches.

The enable-builtin-globs field (optional)

If true, enables the builtin glob patterns for common languages that tend to by installed on most developer machines, such as python.

# List of "common" patterns for scripting languages or easily executable
# compiled languages that could be used for local execution with build scripts
# or proc macros. Obviously this list is not and never can be complete since
# in most cases extensions are only for humans and most tools will happily
# execute scripts/code they can regardless of extension (eg. shell scripts),
# and besides that, an _actually_ malicious crate could generate files on demand,
# download from a remote location, or, really, anything
globs = [
    "*.bat", "*.cmd", # batch
    "*.go", # Go `go run <file.go>`
    "*.java", # Java `java <file.java>`
    "*.js", "*._js", "*.es", "*.es6", # javascript
    "*.pl", "*.perl", "*.pm", # perl
    "*.6pl", "*.6pm", "*.p6", "*.p6l", "*.p6m", "*.pl6", "*.pm6", "*.t", # perl 6
    "*.ps1", "*.psd1", "*.psm1", # powershell
    "*.py", # python
    "*.rb", # ruby
    "*.sh", "*.bash", "*.zsh", # shell
]

The include-dependencies field (optional)

By default, only the crate that executes at compile time is scanned, but if set to true, this field will check this crate as well as all of its dependencies. This option is disabled by default, as this will tend to only find CI scripts that people leave in their published crates.

The include-workspace field (optional)

If true, workspace crates will also be scanned. This defaults to false as you presumably have some degree of trust for your own code.

The include-archives field (optional)

If true, archive files (eg. Windows .lib, Unix .a, C++ .o object files etc) are also counted as native code. This defaults to false, as these tend to need to be linked before they can be executed.

The bypass field (optional)

While all the previous configuration is about configuration the global checks that run on compile time crates, the allow field is how one can suppress those lints on a crate-by-crate basis.

Each entry uses the same PackageSpec as other parts of cargo-deny's configuration.

[build.bypass]
crate = "crate-name"
The build-script and required-features field (optional)

If set to a valid, 64-character hexadecimal SHA-256, the build-script field will cause the rest of the scanning to be bypassed if the crate's build script's checksum matches the user specified checksum AND none of the features specified in the required-features field are enabled. If the checksum does not match, the calculated checksum will be emitted as a warning, and the crate will be scanned as if a checksum was not supplied.

NOTE: These options only applies to crate with build scripts, not proc macros, as proc macros do not have a single entry point that can be easily checksummed.

[[build.bypass]]
name = "crate-name"
build-script = "5392f0e58ad06e089462d93304dfe82337acbbefb87a0749a7dc2ed32af04af7"
The allow-globs field (optional)

Bypasses scanning of files that match one or more of the glob patterns specified. Note that unlike the script-extensions field that applies to all crates, these globs can match anything, not just extensions.

[build]
script-extensions = ["cs"]

[[build.bypass]]
crate = "crate-name"
allow-globs = [
    "scripts/*.cs",
]
The bypass.allow field (optional)

Bypasses scanning a single file.

[build]
executables = "deny"

[[build.bypass]]
crate = "crate-name"
allow = [
    { path = "bin/x86_64-linux", checksum = "5392f0e58ad06e089462d93304dfe82337acbbefb87a0749a7dc2ed32af04af7" }
]
The path field

The path, relative to the crate root, of the file to bypass scanning.

The checksum field (optional)

The 64-character hexadecimal SHA-256 checksum of the file. If the checksum does not match, an error is emitted.

Bans diagnostics

banned

A crate which is explicitly banned was detected.

allowed

A crate which is explicitly allowed was detected.

not-allowed

When using bans.allow, a crate was detected that wasn't in that list.

duplicate

One or more duplicate versions of the same crate were detected.

skipped

A crate version that matched an entry in bans.skip was encountered.

wildcard

A crate was included via a wildcard dependency by one or more crates.

unmatched-skip

A crate version in bans.skip was not encountered.

allowed-by-wrapper

A crate in bans.deny was allowed since it was directly depended on by a wrappers crate.

unmatched-wrapper

A crate in bans.deny had one or more wrappers crates, but a crate not in that list had a direct dependency on the banned crate.

skipped-by-root

A crate was skipped from being checked as a duplicate due to being transitively referenced by a crate version in bans.skip-tree.

unmatched-root

A crate version in bans.skip-tree was not encountered.

build-script-not-allowed

A crate which has been denied because it has a build script but is not part of the bans.allow-build-script list.

exact-features-mismatch

A crate's features do not exactly match the configured feature set, and bans.features.exact is true.

feature-banned

An enabled crate feature is present in the bans.features.deny list.

unknown-feature

A feature in either bans.features.deny or bans.features.allow does not exist for the crate.

default-feature-enabled

The default feature was enabled on a crate, and the bans.external-default-features or bans.workspace-default-features was configured.

path-bypassed

A path specified by bans.build.bypass.allow.path was bypassed, optionally ensuring its contents matched a SHA-256 checksum.

path-bypassed-by-glob

A path was bypassed due to matching one or more glob patterns.

checksum-match

The SHA-256 checksum calculated for the contents of a file matched the checksum in the configuration.

checksum-mismatch

The SHA-256 checksum calculated for the contents of a file did not match the checksum in the configuration.

denied-by-extension

The file extension matched either a user specified or builtin extension.

detected-executable

A native executable was detected.

detected-executable-script

An interpreted script was detected.

unable-to-check-path

An I/O error occurred when opening or reading a file from disk.

features-enabled

One or more required-features were enabled, causing the build-script bypass to be ignored.

unmatched-bypass

A crate bypass did not match any crate in the graph.

unmatched-path-bypass

A path bypass did not match a file in the crate.

unmatched-glob

A glob bypass did not match any files in the crate.

licenses

The licenses check is used to verify that every crate you use has license terms you find acceptable. cargo-deny does this by evaluating the license requirements specified by each crate against the configuration you've specified, to determine if your project meets that crate's license requirements.

cargo deny check licenses

SPDX

cargo-deny uses SPDX license expressions to interpret the license requirements of a crate. In the event that it cannot obtain an SPDX license expression directly from metadata, it tries to derive such within the confidence threshold you specify. Note that cargo-deny currently does not exhaustively search the entirety of the source code of every crate to find every possible license that could be attributed to the crate. There are many edge cases to that approach, and human ingenuity, or even human error, can always outwit a statically-compiled program.

cargo-deny makes a good-faith assumption each crate correctly defines its license requirements. In the (in our experience, rare) circumstance such data cannot be gathered automatically, it provides a mechanism for manually specifying the license requirements for crates.

Expression Source Precedence

The source of the SPDX expression used to evaluate the crate's licensing requirement is obtained in the following order:

  1. If the crate in question has a Clarification applied to it, and the source file(s) in the crate's source still match, the expression from the clarification will be used.
  2. The license field from the crate's Cargo.toml manifest will be used if it exists.
  3. The license-file field, as well as all other LICENSE(-*)? files will be parsed to determine the SPDX license identifier, and then all of those identifiers will be joined with the AND operator, meaning that you must accept all of the licenses detected.

Importantly, this precedence, combined with the trust that licensing data is handled correctly, means the following edge cases are not handled. This is not an exhaustive list, but are rather a sample of the kinds of things a program is not able to completely prevent, even if more checks are added:

  1. Absences: If the package contains inadequate licensing data, in e.g. the event of a crate not reflecting the license of code it is linked with.
  2. Mismatches: If the Cargo.toml documents a given SPDX expression that does not match the actual license files in the package, this is not checked.
  3. Inventiveness: It is possible to place licensing data somewhere that is not in these locations, or have names that start with things other than LICENSE. There is no guarantee such placements inside a package would lose their legal force, even if there is other licensing data that cargo-deny may detect first and assume is comprehensive.

Evaluation Precedence

Currently, the precedence for determining whether a particular license is accepted or rejected is as follows:

  1. A license specified in the deny list is always rejected.
  2. A license specified in the allow list is always accepted.
  3. If the license is considered copyleft, the [licenses.copyleft] configuration determines its status
  4. If the license is OSI Approved or FSF Free/Libre, the [licenses.allow-osi-fsf-free] configuration determines its status, if it is neither the check continues
  5. If the license does not match any of the above criteria, the [licenses.default] configuration determines its status

Example output

licenses output

The [licenses] section

Contains all of the configuration for cargo deny check license.

Example

[licenses]
unlicensed = "warn"
allow-osi-fsf-free = "both"
copyleft = "deny"
default = "warn"
unused-allowed-license = "warn"
confidence-threshold = 0.95
deny = [
    "Nokia",
    "BSD-2-Clause",
]
allow = [
    "EUPL-1.2",
    "Apache-2.0 WITH LLVM-exception",
]

[licenses.private]
ignore = true
registries = ["sekrets"]

[[licenses.exceptions]]
allow = ["Zlib"]
name = "adler32"
version = "0.1.1"

[[licenses.clarify]]
name = "ring"
expression = "MIT AND ISC AND OpenSSL"
license-files = [
    { path = "LICENSE", hash = 0xbd0eed23 }
]

SPDX Identifiers

All identifiers used in the license configuration section are expected to be valid SPDX v2.1 short identifiers, either from version 3.11 of the SPDX License List, or use a custom identifier by prefixing it with LicenseRef-.

allow = [
    # The Apache license identifier
    "Apache-2.0",
    # A custom license identifier
    "LicenseRef-Embark-Custom",
]

# Custom license refs can be specified for crates which don't use a license
# in the SPDX list
[[licenses.clarify]]
crate = "a-crate"
expression = "LicenseRef-Embark-Custom"
license-files = [
    { path = "LICENSE", hash = 0x001c7e6c },
]

License identifiers can also be coupled with an optional exception by appending WITH <exception-id> to the license identifier. Licenses coupled with exceptions are considered distinct from the same license without the exception.

allow = [
    # The Apache license identifier
    "Apache-2.0",
    # The Apache license + LLVM-exception
    "Apache-2.0 WITH LLVM-exception",
]

The include-dev field (optional)

If true, licenses are checked even for dev-dependencies. By default this is false as dev-dependencies are not used by downstream crates, nor part of binary artifacts.

The version field (optional)

version = 2

The licenses section has an upcoming breaking change, with deprecation warnings for several fields that will be removed. Setting version = 2 will opt-in to the future default behavior.

The breaking change is as follows:

  • unlicensed - Removed, if a crate is unlicensed you should open an issue/PR to fix it, and in the meantime, you may add a clarification.
  • deny - Removed, all licenses are denied unless explicitly allowed
  • copyleft - Removed, all licenses are denied unless explicitly allowed
  • allow-osi-fsf-free - Removed, all licenses are denied unless explicitly allowed
  • default - Removed, all licenses are denied unless explicitly allowed

The unlicensed field (optional)

Determines what happens when a crate has not explicitly specified its license terms, and no license information could be confidently detected via LICENSE* files in the crate's source.

  • deny (default) - All unlicensed crates will emit an error and fail the license check
  • allow - All unlicensed crates will show a note, but will not fail the license check
  • warn - All unlicensed crates will show a warning, but will not fail the license check

The allow and deny fields (optional)

The licenses that should be allowed or denied, note that the same license cannot appear in both the allow and deny lists.

deny is DEPRECATED

Note on GNU licenses

  • GPL
  • AGPL
  • LGPL
  • GFDL

The GNU licenses are, of course, different from all the other licenses in the SPDX list which makes them annoying to deal with. When supplying one of the above licenses, to either allow or deny, you must not use the suffixes -only or -or-later, as they can only be used by the license holder themselves to decide under which terms to license their code.

So, for example, if you wanted to disallow GPL-2.0 licenses, but allow GPL-3.0 licenses, you could use the following configuration.

[licenses]
allow = [ "GPL-3.0" ]
deny = [ "GPL-2.0" ]

This gets worse with the GFDL licenses, which also have an invariants modifier. Before licenses are checked they are normalized to make them consistent for all licenses.

Let's use GFDL-1.2 to show how license requirements are normalized.

  • GFDL-1.2-invariants-only => GFDL-1.2-invariants
  • GFDL-1.2-invariants-or-later => GFDL-1.2-invariants+
  • GFDL-1.2-no-invariants-only => GFDL-1.2
  • GFDL-1.2-no-invariants-or-later => GFDL-1.2+
  • GFDL-1.2-only => GFDL-1.2
  • GFDL-1.2-or-later => GFDL-1.2+

So, for example, if you wanted to allow all version (1.1, 1.2, and 1.3), but only invariants for 1.3 you could use the following configuration.

[licenses]
allow = [ "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GFDL-1.3-variants"]

The exceptions field (optional)

The license configuration generally applies to the entire crate graph, but this means that allowing any one license applies to all possible crates, even if only 1 crate actually uses that license. The exceptions field is meant to allow additional licenses only for particular crates, to make a clear distinction between licenses which you are fine with everywhere, versus ones which you want to be more selective about, and not have implicitly allowed in the future.

This field uses PackageSpecs to select the crate the exception applies to.

Additional exceptions configuration file

In some cases it's useful to have global cargo-deny config and project-local exceptions. This can be accomplished with a project exceptions file in any of these locations relative to your top level Cargo.toml manifest file.

cargo-deny will look for the following files: <cwd>/deny.exceptions.toml, <cwd>/.deny.exceptions.toml and <cwd>/.cargo/deny.exceptions.toml

Only the exceptions field should be set:

exceptions = [
    # Each entry is the crate and version constraint, and its specific allow list.
    { allow = ["CDDL-1.0"], crate = "inferno" },
]

The allow field

This is the exact same as the general allow field.

[licenses]
allow = [
    "Apache-2.0",
    "MIT",
]
exceptions = [
    # This is the only crate that cannot be licensed with either Apache-2.0
    # or MIT, so we just add an exception for it, meaning we'll get a warning
    # if we add another crate that also requires this license
    { crate = "cloudabi", allow = ["BSD-2-Clause"] },
]

The copyleft field (optional)

DEPRECATED

Determines what happens when a license that is considered copyleft is encountered.

  • warn (default) - Will emit a warning that a copyleft license was detected, but will not fail the license check
  • deny - The license is not accepted if it is copyleft, but the license check might not fail if the expression still evaluates to true
  • allow - The license is accepted if it is copyleft

The allow-osi-fsf-free field (optional)

DEPRECATED

Determines what happens when licenses aren't explicitly allowed or denied, but are marked as OSI Approved or FSF Free/Libre in version 3.23 of the SPDX License List.

  • both - The license is accepted if it is both OSI approved and FSF Free
  • either - The license is accepted if it is either OSI approved or FSF Free
  • osi - The license is accepted if it is OSI approved
  • fsf - The license is accepted if it is FSF Free
  • osi-only - The license is accepted if it is OSI approved and not FSF Free
  • fsf-only - The license is accepted if it is FSF Free and not OSI approved
  • neither (default) - No special consideration is given the license

The default field (optional)

DEPRECATED

Determines what happens when a license is encountered that:

  1. Isn't in the allow or deny lists
  2. Isn't copyleft
  3. Isn't OSI Approved nor FSF Free/Libre, or allow-osi-fsf-free = "neither"
  • warn - Will emit a warning that the license was detected, but will not fail the license check
  • deny (default) - The license is not accepted, but the license check might not fail if the expression still evaluates to true
  • allow - The license is accepted

The confidence-threshold field (optional)

cargo-deny uses askalono to determine the license of a LICENSE file. Due to variability in license texts because of things like authors, copyright year, and so forth, askalano assigns a confidence score to its determination, from 0.0 (no confidence) to 1.0 (perfect match). The confidence threshold value is used to reject the license determination if the score does not match or exceed the threshold.

0.0 - 1.0 (default 0.8)

The clarify field (optional)

In some exceptional cases, a crate will not have easily machine readable license information, and would by default be considered "unlicensed" by cargo-deny. As a (hopefully) temporary patch for using the crate, you can specify a clarification for the crate by manually assigning its SPDX expression, based on one or more files in the crate's source. cargo-deny will use that expression for as long as the source files in the crate exactly match the clarification's hashes.

This field uses PackageSpecs to select the crate the clarification applies to.

[[licenses.clarify]]
crate = "webpki"
expression = "ISC"
license-files = [
    { path = "LICENSE", hash = 0x001c7e6c },
]

The expression field

The SPDX license expression you are specifying as the license requirements for the crate.

The license-files field

Contains one or more files that will be checked to ensure the license expression still applies to a version of the crate.

The path field

The crate relative path to a file to be used as a source of truth.

The hash field

An opaque hash calculated from the file contents. This hash can be obtained from the output of the license check when cargo-deny can't determine the license of the file in question.

The private field (optional)

It's often not useful or wanted to check for licenses in your own private workspace crates. So the private field allows you to do so.

The ignore field

If true, workspace members will not have their license expression checked if they are not published.

# Cargo.toml
[package]
name = "sekret"
license = "¯\_(ツ)_/¯"
publish = false # "private"!
# deny.toml
[licenses]
# The sekret package would be ignored now
private = { ignore = true }

The registries field

A list of private registries you may publish your workspace crates to. If a workspace member only publishes to private registries, it will also be ignored if private.ignore = true

# Cargo.toml
[package]
name = "sekret"
license = "¯\_(ツ)_/¯"
publish = ["sauce"]
# deny.toml
[licenses]
# Still ignored!
private = { ignore = true, registries = ["sauce"] }

The ignore-sources field

A list of registries that crates can be sourced from that will not have their licenses checked.

# deny.toml
[licenses.private]
ignore = true
ignore-sources = ["https://sekretz.com/super/secret-index"]

The unused-allowed-license field (optional)

Determines what happens when one of the licenses that appears in the allow list is not encountered in the dependency graph.

  • warn (default) - A warning is emitted for each license that appears in license.allow but which is not used in any crate.
  • allow - Unused licenses in the licenses.allow list are ignored.
  • deny - An unused license in the licenses.allow list triggers an error, and cause the license check to fail.

Licenses Diagnostics

rejected

One or more licenses for a crate were rejected because they were not configured to be allowed.

accepted

The license expression for a crate was allowed, though there may be warnings.

unlicensed

No license expression could be found for a crate and it is considered unlicensed.

skipped-private-workspace-crate

A workspace member is publish = false and was skipped.

license-not-encountered

A license in licenses.allow was not found in any crate.

This diagnostic can be silenced by configuring the licenses.unused-allowed-license field to "allow".

license-exception-not-encountered

A licenses.exception was not used as the crate it applied to was not encountered.

sources

The sources check ensures crates only come from sources you trust.

cargo deny check sources

Use Case - Only allowing known/trusted sources

Cargo can retrieve crates from a variety of sources, namely registries, git repositories, or local file paths. This is great in general and very flexible for development. But, especially when re-routing dependencies to git repositories, increasing the amount of sources that a project has to trust may be something a repository wants to explicitly opt-in to.

See Why npm lockfiles can be a security blindspot for injecting malicious modules for the motivating reason for why this check was added.

Use Case - Only using vendored file dependencies

A project may want to only support local file dependencies, such as having all dependencies vendored into the repository for full control and offline building. This can be achieved by disallowing all git and registry sources to ensure that every dependency is added into your source control rather than via an external source.

Example output

sources output

The [sources] section

Contains all of the configuration for cargo deny check sources

Example Config

[sources]
unknown-registry = "allow"
unknown-git = "deny"
required-git-spec = "tag"
allow-registry = [
    "https://sekretz.com/registry/index",
    "sparse+https://fake.sparse.com",
]
allow-git = [
    "https://notgithub.com/orgname/reponame.git",
]
private = [
    "https://internal-host/repos",
]
[sources.allow-org]
github = [
    "yourghid",
    "YourOrg",
]
gitlab = [
    "gitlab-org",
]
bitbucket = [
    "atlassian",
]

The unknown-registry field (optional)

Determines what happens when a crate from a crate registry that is not in the allow-registry list is encountered.

  • deny - Will emit an error with the URL of the source, and fail the check.
  • warn (default) - Prints a warning for each crate, but does not fail the check.
  • allow - Prints a note for each crate, but does not fail the check.

The unknown-git field (optional)

Determines what happens when a crate from a git repository not in the allow-git list is encountered.

  • deny - Will emit an error with the URL of the repository, and fail the check.
  • warn (default) - Prints a warning for each crate, but does not fail the check.
  • allow - Prints a note for each crate, but does not fail the check.

The required-git-spec (optional)

Determines which specifiers are required for git sources. Git sources are a convenient way to use patched code temporarily, but they have downsides for long term maintenance, as the specifier you use for the source determines what happens when you do a cargo update, and in the default case, this means you essentially have a wildcard dependency on the repository.

This configuration value allows you to control what specifiers you want to allow for your git sources to reduce surprises. The following values are listed in order from least to most specific, and using a less specific specifier will also allow all of the more specific ones.

  • any (default) - Allows all git specs, including the default of not having any specifier, which tracks the latest commit on the master branch of the repo
  • branch - Allows the branch = "<branch_name>" specifier.
  • tag - Allows the tag = "<tag_name>" specifier.
  • rev - Allows the rev = "<commit_sha>" specifier.

The allow-git field (optional)

Configure which git urls are allowed for crate sources. If a crate's source is not in one of the listed urls, then the unknown-git setting will determine how it is handled. Note that the url must match exactly, though .git is stripped if it exists to match the logic of cargo.

[sources]
allow-git = [
    "https://github.com/EmbarkStudios/cargo-deny",
]

The private field (optional)

Similarly to allow-git, allows you to configure urls, however, unlike allow-git which is meant for a single, exact, url, private urls actually allow any git repo url which matches the host and begins with the same path. This field is primarily meant to support the use of internal/private git hosts (usually on a VPN) without needing to specify each individual repo. Of course, this can be used to also just allow every repo on Github, but this is not recommended. 😉

[sources]
private = [
    "https://super-duper-sekret",
]

The allow-registry field (optional)

The list of registries that are allowed. If a crate is not found in one of the listed registries, then the unknown-registry setting will determine how it is handled.

If not specified, this list will by default contain the crates.io registry, equivalent to this:

[sources]
allow-registry = [
    "https://github.com/rust-lang/crates.io-index",
]

To not allow any crates registries, set it to empty:

[sources]
unknown-registry = "deny"
allow-registry = []

The allow-org field (optional)

Generally, I think most projects in the Rust space probably follow a similar procedure as we do when they want to fix a bug or add a feature to one of their dependencies, which is basically.

  1. Fork the crate to make your changes
  2. Hack away locally, probably just patching your project(s) to use a path dependency to the cloned fork
  3. Push changes to your fork, and once you're happy, change the path dependency to a git dependency and point it to your fork for others/CI to be able to use the same changes easily
  4. Eventually (hopefully!) make a PR to the original repo with your changes
  5. Hopefully get your changes merged to the original repo
  6. Wait until a release is made that incorporates your changes, possibly changing the git source to point to the original repo
  7. Remove the git source and instead point at the new version of the crate with your changes
  8. Profit!

When working in a company or organization, it is often the case that all crates will be forked to a shared organization account rather than a personal Github account. However, if you lint your git sources, every new and deleted fork needs to keep that list updated, which is tedious, even if all the forks fall under the same organization (in Github terminology), even though presumably only people you trust have permission to create forks there, and you would like to just blanket trust any repo under that org.

The allow-org object allows you to specify 1 or more organizations or users in several VCS providers to more easily configure git sources for your projects.

The github field (optional)

Allows you to specify one or more github.com organizations to allow as git sources.

[sources.allow-org]
github = ["YourCoolOrgGoesHere"]

The gitlab field (optional)

Allows you to specify one or more gitlab.com organizations to allow as git sources.

[sources.allow-org]
gitlab = ["YourCoolOrgGoesHere"]

The bitbucket field (optional)

Allows you to specify one or more bitbucket.org organizations to allow as git sources.

[sources.allow-org]
bitbucket = ["YourCoolOrgGoesHere"]

Sources diagnostics

git-source-underspecified

A git source uses a specification that doesn't meet the minimum specifier required by sources.required-git-spec.

allowed-source

A crate source is explicitly allowed by sources.allow-git or sources.allow-registry.

allowed-by-organization

A crate source was explicitly allowed by an entry in sources.allow-org.

source-not-allowed

A crate's source was not explicitly allowed.

unmatched-source

An allowed source in sources.allow-git or sources.allow-registry was not encountered.

unmatched-organization

An allowed source in sources.allow-org was not encountered.