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 emittederror
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 humansjson
- 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 TTYalways
- Coloring is always appliednever
- 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.
--exclude-unpublished
If set, exclude unpublished workspace members from graph roots.
Workspace members are considered unpublished if they they are explicitly marked with publish = false
. Note that the excluded workspace members are still used for the initial dependency resolution by cargo, which might affect the exact version of used dependencies.
--allow-git-index
If set, the crates.io git index is initialized for use in fetching crate information, otherwise it is enabled only if using a cargo < 1.70.0 without the sparse protocol enabled
--locked
Asserts that the exact same dependencies and versions are used as when the existing Cargo.lock file was originally generated. Cargo will exit with an error when either of the following scenarios arises:
- The lock file is missing.
- Cargo attempted to change the lock file due to a different dependency resolution.
--offline
Prevents Cargo and cargo-deny
from accessing the network for any reason. Without this flag, Cargo will stop with an error if it needs to access the network and the network is not available. With this flag, Cargo will attempt to proceed without the network if possible.
Beware that this may result in different dependency resolution than online mode. Cargo will restrict itself to crates that are downloaded locally, even if there might be a newer version as indicated in the local copy of the index. See the cargo-fetch(1) command to download dependencies before going offline.
cargo-deny
will also not fetch advisory databases with this option, meaning that any new or updated advisories since the last time the database(s) were fetched won't be known and thus won't be checked against the dependency graph.
--frozen
Equivalent to specifying both --locked
and --offline
.
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]
# github.com organizations to allow git sources for
github = []
# gitlab.com organizations to allow git sources for
gitlab = []
# 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
-c, --config <CONFIG>
Path to the config to use
Defaults to <cwd>/deny.toml
if not specified
-f, --format
The format of the output
human
(default) - Simple format where each crate or license is its own linejson
tsv
--color
Colors:
- SPDX identifier -
- Crate with 1 license -
- Crate with 2 or more licenses -
- Crate with 0 licenses -
-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 licensecrate
- 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)
layout = crate, format = human
layout = license, format = json
layout = license, format = tsv
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 = [
]
[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 = "hashbrown@0.14.5", reason = "gix uses this old version" },
{ crate = "core-foundation@0.9.4", reason = "reqwest -> system-configuration uses this old version" },
]
skip-tree = [
{ crate = "windows-sys@0.52.0", reason = "a foundational crate for many that bumps far too frequently to ever have a shared version" },
{ crate = "thiserror@1.0.69", reason = "gix depends on both the 1.0 and 2.0 versions" },
]
[sources]
unknown-registry = "deny"
unknown-git = "deny"
[licenses]
# 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",
"Unicode-3.0",
]
exceptions = [
# Use exceptions for these as they only have a single user
{ allow = ["Zlib"], crate = "tinyvec" },
{ 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_feature
s 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 exclude-unpublished
field (optional)
If set to true
, workspace crates marked as publish = false
will not be used as roots in the dependency graph, meaning they, and any dependencies they have that aren't directly or indirectly referenced by workspace crates that are published, will be excluded from the dependency graph that checks are executed against.
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
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"]
ignore = [
"RUSTSEC-0000-0000",
"crate@0.1",
{ crate = "yanked", reason = "a new version has not been released" },
]
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:
~
- Expands tohome::home_dir
$VARNAME
- Expands tostd::env::var("VARNAME")
${VARNAME}
- Expands tostd::env::var("VARNAME")
${VARNAME:-fallback}
- Expands tostd::env::var("VARNAME")
or the fallback value if it doesn't exist (everything between the:-
and}
)$CARGO_HOME
- Expands tostd::env::var("CARGO_HOME")
if it exists, otherwise expands to$(home::home_dir())/.cargo
Note that the path must be valid utf-8, after expansion.
Default: $CARGO_HOME/advisory-dbs
The version
field (optional)
version = 2
The version field is (at the time of this writing) no longer used, the following fields have been removed and will now emit errors.
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 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 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 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 viagix
true
- Fetches advisory databases usinggit
. Git must be installed and inPATH
.
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.
- 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.
- 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.
- More packages must be fetched, which especially impacts CI, as well as devs.
- Compile times increase, which impacts CI and devs.
- Target directory size increases, which can impact devs, or static CI environments.
- 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
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.workspace-dependencies]
duplicates = "allow"
include-path-dependencies = false
unused = "allow"
[[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
andbuild-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 workspace-dependencies
field (optional)
Used to configure how [workspace.dependencies]
are treated.
[bans.workspace-dependencies]
duplicates = 'deny'
include-path-dependencies = true
unused = 'deny'
The duplicates
field (optional)
Determines what happens when more than 1 direct workspace dependency is resolved to the same crate and 1 or more declarations do not use workspace = true
deny
(default) - Will emit an error for each dependency declaration that does not useworkspace = true
warn
- Will emit a warning for each dependency declaration that does not useworkspace = true
, but does not fail the check.allow
- Ignores checking forworkspace = true
for dependencies in workspace crates
The include-path-dependencies
field (optional)
If true, path dependencies will be included in the duplication check, otherwise they are completely ignored.
The unused
field (optional)
Determines what happens when a dependency in [workspace.dependencies]
is not used in the workspace.
deny
(default) - Will emit an error for each dependency that is not actually used in the workspace.warn
- Will emit a warning for each dependency that is not actually used in the workspace, but does not fail the check.allow
- Ignores checking for unused workspace 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 insimplest-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 .all
- Highlights both thelowest-version
andsimplest-path
. If they are the same, they are only highlighted in .
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.
workspace-duplicate
A direct workspace dependency was referred to more than once and all declarations did not use workspace = true
unresolved-workspace-dependency
We were unable to determine the exact crate a workspace dependency (or patch) was resolved to. This most likely indicates a bug in cargo-deny.
unused-workspace-dependency
A [workspace.dependencies]
was declared, but not actually used anywhere in the workspace.
unmatched-skip
A crate version in bans.skip
was not encountered.
unnecessary-skip
A crate specified in bans.skip
was in the graph, but that crate only had one version, making the skip
entry useless.
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:
- 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.
- The
license
field from the crate's Cargo.toml manifest will be used if it exists. - The
license-file
field, as well as all otherLICENSE(-*)?
files will be parsed to determine the SPDX license identifier, and then all of those identifiers will be joined with theAND
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:
- 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.
- 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.
- 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.
Example output
The [licenses]
section
Contains all of the configuration for cargo deny check license
.
Example
[licenses]
unused-allowed-license = "warn"
confidence-threshold = 0.95
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 version field is (at the time of this writing) no longer used, the following fields have been removed and will now emit errors.
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 allowedcopyleft
- Removed, all licenses are denied unless explicitly allowedallow-osi-fsf-free
- Removed, all licenses are denied unless explicitly alloweddefault
- Removed, all licenses are denied unless explicitly allowed
The allow
field (optional)
The licenses that are explicitly allowed.
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" ]
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 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 inlicense.allow
but which is not used in any crate.allow
- Unused licenses in thelicenses.allow
list are ignored.deny
- An unused license in thelicenses.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
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 themaster
branch of the repobranch
- Allows thebranch = "<branch_name>"
specifier.tag
- Allows thetag = "<tag_name>"
specifier.rev
- Allows therev = "<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.
- Fork the crate to make your changes
- Hack away locally, probably just patching your project(s) to use a
path
dependency to the cloned fork - Push changes to your fork, and once you're happy, change the
path
dependency to agit
dependency and point it to your fork for others/CI to be able to use the same changes easily - Eventually (hopefully!) make a PR to the original repo with your changes
- Hopefully get your changes merged to the original repo
- Wait until a release is made that incorporates your changes, possibly changing the
git
source to point to the original repo - Remove the
git
source and instead point at the new version of the crate with your changes - 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 multiple organizations or users in several VCS providers to more easily configure git sources for your projects.
The github
field (optional)
Allows you to specify multiple github.com
organizations to allow as git sources.
[sources.allow-org]
github = ["YourCoolOrgGoesHere"]
The gitlab
field (optional)
Allows you to specify multiple gitlab.com
organizations to allow as git sources.
[sources.allow-org]
gitlab = ["YourCoolOrgGoesHere"]
The bitbucket
field (optional)
Allows you to specify multiple 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.