diff --git a/CHANGELOG.md b/CHANGELOG.md index b1d970fe..845a5f5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Changed +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) did a major refactor of configuration, both how it is deserialized and changing (hopefully improving) many options. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) moved `targets`, `exclude`, `all-features`, `features`, `no-default-features`, and `exclude` into the `[graph]` table. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) moved `feature-depth` into the `[output]` table. + +### Fixed +- [PR#601](https://github.com/EmbarkStudios/cargo-deny/pull/601) resolved [#600](https://github.com/EmbarkStudios/cargo-deny/issues/600) by outputting the correct spans when a license was both allowed and denied. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) resolved [#264](https://github.com/EmbarkStudios/cargo-deny/issues/264) be replacing `toml` and `serde` with `toml-span`. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) resolved [#539](https://github.com/EmbarkStudios/cargo-deny/issues/539) by simplifying the very common `name = "", version = ""` used to target specific crates into either a plain [package spec string](https://embarkstudios.github.io/cargo-deny/checks/cfg.html#string-format) or the simpler `crate = ""`. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) resolved [#578](https://github.com/EmbarkStudios/cargo-deny/issues/578) by adding a `reason = ""` field to _many_ fields within the configuration that are provided in diagnostics. `[bans.deny]` also has an additional `use-instead = ""`. +- [PR#605](https://github.com/EmbarkStudios/cargo-deny/pull/605) resolved [#579](https://github.com/EmbarkStudios/cargo-deny/issues/579) by allowing yanked crates to be ignored by specifying a [PackageSpec](https://embarkstudios.github.io/cargo-deny/checks/cfg.html#package-specs) in the `[advisories.ignore]` array. + ## [0.14.11] - 2024-02-05 ### Fixed - [PR#599](https://github.com/EmbarkStudios/cargo-deny/pull/599) resolved [#488](https://github.com/EmbarkStudios/cargo-deny/issues/488) by treating git and path sources differently. Thanks [@kpreid](https://github.com/kpreid)! diff --git a/Cargo.lock b/Cargo.lock index d9a1259a..883303c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "arc-swap" @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" [[package]] name = "byteorder" @@ -304,11 +304,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" dependencies = [ - "jobserver", "libc", ] @@ -330,9 +329,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -340,9 +339,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -1669,9 +1668,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.34.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2" dependencies = [ "console", "lazy_static", @@ -1703,15 +1702,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jobserver" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.68" @@ -2116,16 +2106,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2237,9 +2228,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -2320,27 +2311,27 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -2349,9 +2340,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -2512,9 +2503,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -2828,9 +2819,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] diff --git a/deny.template.toml b/deny.template.toml index d860d514..73c7b307 100644 --- a/deny.template.toml +++ b/deny.template.toml @@ -11,6 +11,9 @@ # 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 @@ -22,7 +25,7 @@ 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 - #{ triple = "x86_64-unknown-linux-musl" }, + #"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. @@ -46,6 +49,9 @@ 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 @@ -73,17 +79,10 @@ notice = "warn" # 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" }, ] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - 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 -#severity-threshold = - # 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. @@ -104,12 +103,6 @@ allow = [ #"Apache-2.0", #"Apache-2.0 WITH LLVM-exception", ] -# List of explicitly disallowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -deny = [ - #"Nokia", -] # Lint level for licenses considered copyleft copyleft = "warn" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses @@ -136,17 +129,15 @@ confidence-threshold = 0.8 exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, + #{ 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 name of the crate the clarification applies to -#name = "ring" -# The optional version constraint for the crate -#version = "*" +# 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 @@ -155,8 +146,8 @@ exceptions = [ # 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 } +# Each entry is a crate relative path, and the (opaque) hash of its contents +#{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] @@ -196,24 +187,23 @@ workspace-default-features = "allow" external-default-features = "allow" # List of crates that are allowed. Use with care! allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #"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 = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. - #{ name = "ansi_term", version = "=0.11.0" }, - # + #"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 - #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, + #{ 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]] -#name = "reqwest" +#crate = "reqwest" # Features to not allow #deny = ["json"] # Features to allow @@ -234,14 +224,16 @@ deny = [ # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #"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 = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, + #"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`. diff --git a/deny.toml b/deny.toml index 6cf4e0b2..d1006505 100644 --- a/deny.toml +++ b/deny.toml @@ -33,11 +33,10 @@ deny = [ { crate = "windows", reason = "bloated and unnecessary", use-instead = "ideally inline bindings, practically, windows-sys" }, ] skip = [ - # https://github.com/seanmonstar/reqwest/pull/2130 should be in the next reqwest release - { crate = "bitflags@1.3.2", reason = "reqwest -> system-configuration uses this old version" }, + { crate = "bitflags@1.3.2", reason = "https://github.com/seanmonstar/reqwest/pull/2130 should be in the next version" }, ] skip-tree = [ - { crate = "windows-sys:<=0.48", reason = "a foundational crate for many that bumps far too frequently to ever have a shared version" }, + { crate = "windows-sys@0.48.0", reason = "a foundational crate for many that bumps far too frequently to ever have a shared version" }, ] [sources] diff --git a/docs/src/checks/advisories/cfg.md b/docs/src/checks/advisories/cfg.md index bde0feb2..b7676967 100644 --- a/docs/src/checks/advisories/cfg.md +++ b/docs/src/checks/advisories/cfg.md @@ -64,8 +64,19 @@ Determines what happens when a crate with a `notice` advisory is encountered. ### The `ignore` field (optional) +```ini +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](../cfg.md#package-spec) with an optional `reason`. + ### The `severity-threshold` field (optional) The threshold for security vulnerabilities to be turned into notes instead of warnings or errors, depending upon its [CVSS](https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System) 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]`. diff --git a/docs/src/checks/bans/cfg.md b/docs/src/checks/bans/cfg.md index b897c298..3b60ec71 100644 --- a/docs/src/checks/bans/cfg.md +++ b/docs/src/checks/bans/cfg.md @@ -48,50 +48,62 @@ When multiple versions of the same crate are encountered and `multiple-versions` ![Imgur](https://i.imgur.com/xtarzeU.png) -### Crate specifier - -The `allow`, `deny`, `features`, `skip`, `skip-tree`, and `build.allow` fields all use a crate identifier to specify what crate(s) they want to match against. +### The `deny` field (optional) ```ini -{ name = "some-crate-name-here", version = "<= 0.7.0" } +deny = ["package-spec"] ``` -#### The `name` field +Determines specific crates that are denied. Each entry uses the same [PackageSpec](../cfg.md#package-specs) as other parts of cargo-deny's configuration. -The name of the crate. +#### The `wrappers` field (optional) -#### The `version` field (optional) +```ini +deny = [{ crate = "crate-you-don't-want:<=0.7.0", wrappers = ["this-can-use-it"] }] +``` -An optional version constraint specifying the range of crate versions that will match. Defaults to any version. +This field allows specific crates to have a direct dependency on the banned crate but denies all transitive dependencies on it. -### The `deny` field (optional) +#### The `deny-multiple-versions` field (optional) ```ini -deny = [{ name = "crate-you-don't-want", version = "<= 0.7.0" }] +multiple-versions = 'allow' +deny = [{ crate = "crate-you-want-only-one-version-of", deny-multiple-versions = true }] ``` -Determines specific crates that are denied. +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 `wrappers` field (optional) +#### The `deny.reason` field (optional) ```ini -deny = [{ name = "crate-you-don't-want", version = "<= 0.7.0", wrappers = ["this-can-use-it"] }] +deny = [{ crate = "package-spec", reason = "the reason this crate is banned"}] ``` -This field allows specific crates to have a direct dependency on the banned crate but denies all transitive dependencies on it. +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-multiple-versions` field (optional) +#### The `deny.use-instead` field (optional) ```ini -multiple-versions = 'allow' -deny = [{ name = "crate-you-want-only-one-version-of", deny-multiple-versions = true }] +deny = [{ crate = "openssl", use-instead = "rustls"}] ``` -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`. +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) -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. +```ini +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](../cfg.md#package-specs) as other parts of cargo-deny's configuration. + +#### The `allow.reason` field (optional) + +```ini +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) @@ -104,7 +116,7 @@ For example, if `an-external-crate` had the `default` feature enabled it could b external-default-features = "deny" [[bans.features]] -name = "an-external-crate" +crate = "an-external-crate" allow = ["default"] ``` @@ -117,7 +129,7 @@ The workspace version of `external-default-features`. external-default-features = "allow" [[bans.features]] -name = "a-workspace-crate" +crate = "a-workspace-crate" deny = ["default"] ``` @@ -125,14 +137,13 @@ deny = ["default"] ```ini [[bans.features]] -name = "featured-krate" -version = "1.0" +crate = "featured-krate:1.0" deny = ["bad-feature"] allow = ["good-feature"] exact = true ``` -Allows specification of crate specific allow/deny lists of features. +Allows specification of crate specific allow/deny lists of features. Each entry uses the same [PackageSpec](../cfg.md#package-specs) as other parts of cargo-deny's configuration. #### The `features.deny` field (optional) @@ -148,19 +159,38 @@ If specified, requires that the features in `allow` exactly match the features e ### The `skip` field (optional) +```ini +skip = [ + "package-spec", + { crate = "package-spec", reason = "an old version is used by crate-x, see 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](../cfg.md#package-specs) as other parts of cargo-deny's configuration. + ### The `skip-tree` field (optional) +```ini +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. -**NOTE:** `skip-tree` is a very big hammer at the moment, and should be used with care. +Each entry uses the same [PackageSpec](../cfg.md#package-specs) 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) @@ -232,11 +262,11 @@ If `true`, archive files (eg. Windows .lib, Unix .a, C++ .o object files etc) ar 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 [Crate specifier](#crate-specifier) as other parts of cargo-deny's configuration. +Each entry uses the same [PackageSpec](../cfg.md#package-specs) as other parts of cargo-deny's configuration. ```ini [build.bypass] -name = "crate-name" +crate = "crate-name" ``` ##### The `build-script` and `required-features` field (optional) @@ -260,7 +290,7 @@ Bypasses scanning of files that match one or more of the glob patterns specified script-extensions = ["cs"] [[build.bypass]] -name = "crate-name" +crate = "crate-name" allow-globs = [ "scripts/*.cs", ] @@ -275,7 +305,7 @@ Bypasses scanning a single file. executables = "deny" [[build.bypass]] -name = "crate-name" +crate = "crate-name" allow = [ { path = "bin/x86_64-linux", checksum = "5392f0e58ad06e089462d93304dfe82337acbbefb87a0749a7dc2ed32af04af7" } ] @@ -288,5 +318,3 @@ The path, relative to the crate root, of the file to bypass scanning. ###### The `checksum` field (optional) The 64-character hexadecimal [SHA-256](https://en.wikipedia.org/wiki/SHA-2) checksum of the file. If the checksum does not match, an error is emitted. - -[]( diff --git a/docs/src/checks/cfg.md b/docs/src/checks/cfg.md index cc5f8f78..4dfc8a94 100644 --- a/docs/src/checks/cfg.md +++ b/docs/src/checks/cfg.md @@ -8,6 +8,24 @@ The top level config for cargo-deny, by default called `deny.toml`. {{#include ../../../deny.toml}} ``` +## The `graph` field (optional) + +The graph tables provides configuration options for how the dependency graph that the various checks are executed against is constructed. + +```ini +[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 @@ -24,7 +42,7 @@ But unless you are actually targeting `x86_64-fuchsia` or `aarch64-fuchsia`, the 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 `triple` field +#### The `targets.triple` field (optional) or `""` The [target triple](https://forge.rust-lang.org/release/platform-support.html) 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..dependencies]` style of target configuration, as `cfg()` expressions require us to know the details about the target. @@ -50,26 +68,92 @@ If set to `true`, `--no-default-features` will be used when collecting metadata. 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](../cli/common.md#--exclude-dev) or [after](../cli/check.md#--exclude-dev) 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`. -### The `exclude-dev` field (optional) +## Package Specs -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](../cli/common.md#--exclude-dev) or [after](../cli/check.md#--exclude-dev) the `check` subcommand. +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`](bans/cfg.md#the-deny-field-optional) 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 + +```ini +# 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 + +```ini +# 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 + +```ini +# 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)](https://docs.rs/semver/latest/semver/enum.Op.html#opexact). + +### Table format + +#### Crate format + +```ini +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 + +```ini +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 +## The `[licenses]` section See the [licenses config](licenses/cfg.html) for more info. -### The `[bans]` section +## The `[bans]` section See the [bans config](bans/cfg.html) for more info. -### The `[advisories]` section +## The `[advisories]` section See the [advisories config](advisories/cfg.html) for more info. -### The `[sources]` section +## The `[sources]` section See the [sources config](sources/cfg.html) for more info. diff --git a/docs/src/checks/licenses/cfg.md b/docs/src/checks/licenses/cfg.md index 00c07e32..b79a5165 100644 --- a/docs/src/checks/licenses/cfg.md +++ b/docs/src/checks/licenses/cfg.md @@ -23,7 +23,7 @@ allow = [ # Custom license refs can be specified for crates which don't use a license # in the SPDX list [[licenses.clarify]] -name = "a-crate" +crate = "a-crate" expression = "LicenseRef-Embark-Custom" license-files = [ { path = "LICENSE", hash = 0x001c7e6c }, @@ -97,13 +97,7 @@ allow = [ "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GFDL-1.3-variants"] 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. -#### The `exceptions.name` field - -The name of the crate that you are adding an exception for - -#### The `exceptions.version` field (optional) - -An optional version constraint specifying the range of crate versions you are excepting. Defaults to any version. +This field uses [PackageSpecs](../cfg.md#package-specs) to select the crate the exception applies to. ### Additional exceptions configuration file @@ -116,7 +110,7 @@ Only the exceptions field should be set: ```ini exceptions = [ # Each entry is the crate and version constraint, and its specific allow list. - { allow = ["CDDL-1.0"], name = "inferno", version = "*" }, + { allow = ["CDDL-1.0"], crate = "inferno" }, ] ``` @@ -134,7 +128,7 @@ 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 - { name = "cloudabi", allow = ["BSD-2-Clause"] }, + { crate = "cloudabi", allow = ["BSD-2-Clause"] }, ] ``` @@ -180,23 +174,17 @@ Determines what happens when a license is encountered that: 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](../cfg.md#package-specs) to select the crate the clarification applies to. + ```ini [[licenses.clarify]] -name = "webpki" +crate = "webpki" expression = "ISC" license-files = [ { path = "LICENSE", hash = 0x001c7e6c }, ] ``` -#### The `name` field - -The name of the crate that you are clarifying - -#### The `version` field (optional) - -An optional version constraint specifying the range of crate versions you are clarifying. Defaults to any version. - #### The `expression` field The [SPDX license expression][SPDX-expr] you are specifying as the license requirements for the crate. diff --git a/src/advisories.rs b/src/advisories.rs index a2582d19..18a18cc5 100644 --- a/src/advisories.rs +++ b/src/advisories.rs @@ -121,7 +121,7 @@ pub fn check( // Check for advisory identifers that were set to be ignored, but // are not actually in any database. for ignored in &ctx.cfg.ignore { - if !advisory_dbs.has_advisory(&ignored.value) { + if !advisory_dbs.has_advisory(&ignored.id.value) { sink.push(ctx.diag_for_unknown_advisory(ignored)); } } diff --git a/src/advisories/cfg.rs b/src/advisories/cfg.rs index 23fe47c1..8df2e3d2 100644 --- a/src/advisories/cfg.rs +++ b/src/advisories/cfg.rs @@ -5,9 +5,65 @@ use crate::{ }; use rustsec::advisory; use time::Duration; -use toml_span::{de_helpers::*, value::ValueInner}; +use toml_span::{de_helpers::*, value::ValueInner, Deserialize, Value}; use url::Url; +pub(crate) type AdvisoryId = Spanned; + +#[cfg_attr(test, derive(serde::Serialize))] +pub(crate) struct IgnoreId { + pub id: AdvisoryId, + pub reason: Option, +} + +impl<'de> Deserialize<'de> for IgnoreId { + fn deserialize(value: &mut Value<'de>) -> Result { + let mut th = TableHelper::new(value)?; + let ids = th.required_s::>("id")?; + let id = match ids.value.parse() { + Ok(id) => Spanned::with_span(id, ids.span), + Err(err) => { + return Err(toml_span::Error { + kind: toml_span::ErrorKind::Custom( + format!("failed to parse advisory id: {err}").into(), + ), + span: ids.span, + line_info: None, + } + .into()); + } + }; + let reason = th.optional_s::("reason"); + + th.finalize(None)?; + + Ok(Self { + id, + reason: reason.map(Reason::from), + }) + } +} + +impl Ord for IgnoreId { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl PartialOrd for IgnoreId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for IgnoreId { + fn eq(&self, other: &Self) -> bool { + self.id.eq(&other.id) + } +} + +impl Eq for IgnoreId {} + pub struct Config { /// Path to the root directory where advisory databases are stored (default: $CARGO_HOME/advisory-dbs) pub db_path: Option, @@ -24,7 +80,7 @@ pub struct Config { /// How to handle crates that have been marked with a notice in the advisory database pub notice: LintLevel, /// Ignore advisories for the given IDs - pub ignore: Vec, + ignore: Vec>, /// Ignore yanked crates pub ignore_yanked: Vec>>, /// CVSS Qualitative Severity Rating Scale threshold to alert at. @@ -70,11 +126,9 @@ impl Default for Config { const NINETY_DAYS: f64 = 90. * 24. * 60. * 60. * 60.; -impl<'de> toml_span::Deserialize<'de> for Config { - fn deserialize( - value: &mut toml_span::value::Value<'de>, - ) -> Result { - let mut th = toml_span::de_helpers::TableHelper::new(value)?; +impl<'de> Deserialize<'de> for Config { + fn deserialize(value: &mut Value<'de>) -> Result { + let mut th = TableHelper::new(value)?; let db_path = th.optional::("db-path").map(PathBuf::from); let db_urls = if let Some((_, mut urls)) = th.take("db-urls") { @@ -120,33 +174,57 @@ impl<'de> toml_span::Deserialize<'de> for Config { match ignore.take() { ValueInner::Array(ida) => { for mut v in ida { - let inner = v.take(); - if let ValueInner::String(s) = &inner { - // Attempt to parse an advisory id first, note we can't - // just immediately use parse as the from_str implementation - // for id will just blindly accept any string - if advisory::IdKind::detect(s.as_ref()) != advisory::IdKind::Other { - if let Ok(id) = s.parse::() { - u.push(Spanned::with_span(id, v.span)); - continue; + match v.take() { + ValueInner::String(s) => { + // Attempt to parse an advisory id first, note we can't + // just immediately use parse as the from_str implementation + // for id will just blindly accept any string + if advisory::IdKind::detect(s.as_ref()) != advisory::IdKind::Other { + if let Ok(id) = s.parse::() { + u.push(Spanned::with_span( + IgnoreId { + id: Spanned::with_span(id, v.span), + reason: None, + }, + v.span, + )); + continue; + } } - } - } - let found = inner.type_str(); - v.set(inner); + v.set(ValueInner::String(s)); + } + ValueInner::Table(tab) => { + if tab.contains_key(&"id".into()) { + v.set(ValueInner::Table(tab)); + match IgnoreId::deserialize(&mut v) { + Ok(iid) => u.push(Spanned::with_span(iid, v.span)), + Err(mut err) => { + th.errors.append(&mut err.errors); + } + } + continue; + } - match PackageSpecOrExtended::deserialize(&mut v) { - Ok(pse) => y.push(Spanned::with_span(pse, v.span)), - Err(_err) => { + v.set(ValueInner::Table(tab)); + } + other => { th.errors.push(toml_span::Error { kind: toml_span::ErrorKind::Wanted { expected: "an advisory id or package spec", - found, + found: other.type_str(), }, span: v.span, line_info: None, }); + continue; + } + } + + match PackageSpecOrExtended::deserialize(&mut v) { + Ok(pse) => y.push(Spanned::with_span(pse, v.span)), + Err(mut err) => { + th.errors.append(&mut err.errors); } } } @@ -288,7 +366,7 @@ impl crate::cfg::UnvalidatedConfig for Config { file_id: ctx.cfg_id, db_path: self.db_path, db_urls, - ignore, + ignore: ignore.into_iter().map(|s| s.value).collect(), ignore_yanked: ignore_yanked .into_iter() .map(|s| crate::bans::SpecAndReason { @@ -311,14 +389,12 @@ impl crate::cfg::UnvalidatedConfig for Config { } } -pub(crate) type AdvisoryId = Spanned; - #[cfg_attr(test, derive(serde::Serialize))] pub struct ValidConfig { pub file_id: FileId, pub db_path: Option, pub db_urls: Vec>, - pub(crate) ignore: Vec, + pub(crate) ignore: Vec, pub(crate) ignore_yanked: Vec, pub vulnerability: LintLevel, pub unmaintained: LintLevel, diff --git a/src/advisories/diags.rs b/src/advisories/diags.rs index 7fe23c6c..e0a5f74c 100644 --- a/src/advisories/diags.rs +++ b/src/advisories/diags.rs @@ -1,8 +1,22 @@ +use super::cfg::IgnoreId; use crate::{ - diag::{Check, Diagnostic, Label, Pack, Severity}, + diag::{Check, Diagnostic, FileId, Label, Pack, Severity}, LintLevel, }; -use rustsec::advisory::{Id, Informational, Metadata, Versions}; +use rustsec::advisory::{Informational, Metadata, Versions}; + +impl IgnoreId { + fn to_labels(&self, id: FileId, msg: impl Into) -> Vec