aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeonardo Neumann <leonardo@neumann.dev.br>2021-08-14 18:05:37 -0300
committerGeorgy Yakovlev <gyakovlev@gentoo.org>2021-08-26 00:40:36 -0700
commit1f8c7eb03ace33caba40822651c822d140f2840c (patch)
treec90a99f3208f03956d1ef9ffc1c6010d1ebb5c53
parentUse path references instead of owned counterparts (diff)
downloadcargo-ebuild-1f8c7eb03ace33caba40822651c822d140f2840c.tar.gz
cargo-ebuild-1f8c7eb03ace33caba40822651c822d140f2840c.tar.bz2
cargo-ebuild-1f8c7eb03ace33caba40822651c822d140f2840c.zip
Implement audit functionality using rustsec
Closes: https://github.com/gentoo/cargo-ebuild/pull/15 Closes: https://github.com/gentoo/cargo-ebuild/issues/2 Signed-off-by: Leonardo Neumann <leonardo@neumann.dev.br> Signed-off-by: Georgy Yakovlev <gyakovlev@gentoo.org>
-rw-r--r--Cargo.lock259
-rw-r--r--Cargo.toml1
-rw-r--r--src/audit.rs96
-rw-r--r--src/lib.rs8
-rw-r--r--src/main.rs7
5 files changed, 368 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 431eb9d..06293a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,12 @@ dependencies = [
]
[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
name = "base-x"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -115,6 +121,7 @@ dependencies = [
"cargo_metadata",
"itertools",
"phf",
+ "rustsec",
"serde",
"structopt",
"tera",
@@ -156,6 +163,15 @@ dependencies = [
]
[[package]]
+name = "cc"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
+dependencies = [
+ "jobserver",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -183,6 +199,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
[[package]]
+name = "crates-index"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ad4af5c8dd9940a497ef4473e6e558b660a4a1b6e5ce2cb9d85454e2aaaf947"
+dependencies = [
+ "git2",
+ "glob",
+ "hex",
+ "home",
+ "memchr",
+ "semver 1.0.4",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smartstring",
+]
+
+[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -193,6 +227,15 @@ dependencies = [
]
[[package]]
+name = "cvss"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829862dabeab142ae0efd558d42d8fd874659268ccd810809ac6f1ee6bfcbd3f"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,6 +279,12 @@ dependencies = [
]
[[package]]
+name = "fs-err"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0"
+
+[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -256,6 +305,27 @@ dependencies = [
]
[[package]]
+name = "git2"
+version = "0.13.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "659cd14835e75b64d9dba5b660463506763cf0aa6cb640aeeb0e98d841093490"
+dependencies = [
+ "bitflags",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "openssl-probe",
+ "openssl-sys",
+ "url",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
+[[package]]
name = "globset"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -298,6 +368,40 @@ dependencies = [
]
[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "home"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "humantime-serde"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
+dependencies = [
+ "humantime",
+ "serde",
+]
+
+[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -342,6 +446,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
+name = "jobserver"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -354,6 +467,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
[[package]]
+name = "libgit2-sys"
+version = "0.12.22+1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89c53ac117c44f7042ad8d8f5681378dfbc6010e49ec2c0d1f11dfedc7a4a1c3"
+dependencies = [
+ "cc",
+ "libc",
+ "libssh2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "libssh2-sys"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -393,6 +546,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
+name = "openssl-probe"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -486,6 +658,21 @@ dependencies = [
]
[[package]]
+name = "pkg-config"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
+[[package]]
+name = "platforms"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -606,6 +793,29 @@ dependencies = [
]
[[package]]
+name = "rustsec"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c29c220a60ceaeedb2c5bf51826b3d3c5d77b2523693f0579c8a85dd03f11947"
+dependencies = [
+ "cargo-lock",
+ "crates-index",
+ "cvss",
+ "fs-err",
+ "git2",
+ "home",
+ "humantime",
+ "humantime-serde",
+ "platforms",
+ "semver 1.0.4",
+ "serde",
+ "smol_str",
+ "thiserror",
+ "toml",
+ "url",
+]
+
+[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -700,6 +910,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
[[package]]
+name = "smartstring"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31aa6a31c0c2b21327ce875f7e8952322acfcfd0c27569a6e18a647281352c9b"
+dependencies = [
+ "serde",
+ "static_assertions",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
+
+[[package]]
name = "standback"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -709,6 +935,12 @@ dependencies = [
]
[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -824,6 +1056,26 @@ dependencies = [
]
[[package]]
+name = "thiserror"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -999,9 +1251,16 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
+ "serde",
]
[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index ef4be19..84e7883 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ anyhow = "^1"
cargo-lock = "^7.0"
cargo_metadata = "^0.14"
itertools = "^0.10"
+rustsec = "0.24.2"
structopt = "^0.3"
serde = { version = "1.0", features = ["derive"] }
time = "^0.2"
diff --git a/src/audit.rs b/src/audit.rs
new file mode 100644
index 0000000..8ea8e43
--- /dev/null
+++ b/src/audit.rs
@@ -0,0 +1,96 @@
+use std::env;
+use std::path::Path;
+use std::process::Command;
+
+use anyhow::{format_err, Context, Result};
+use rustsec::lockfile::Lockfile;
+use rustsec::report::{Settings, VulnerabilityInfo};
+use rustsec::{Database, Report, Vulnerability};
+
+fn generate_lockfile(workspace_root: &Path, manifest_path: Option<&Path>) -> Result<Lockfile> {
+ let lockfile = workspace_root.join("Cargo.lock");
+ let mut command = Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
+
+ if lockfile.exists() {
+ return Lockfile::load(lockfile).context("Failed to load lockfile");
+ }
+
+ command.arg("generate-lockfile");
+
+ if let Some(path) = manifest_path {
+ command.arg("--manifest-path");
+ command.arg(path.as_os_str());
+ }
+
+ let status = command
+ .status()
+ .context("Failed to run `cargo generate-lockfile`")?;
+
+ match status.code() {
+ Some(0) => Lockfile::load(lockfile).context("Failed to load lockfile"),
+ Some(code) => Err(format_err!(
+ "Non-zero status ({}) on `cargo generate-lockfile`",
+ code,
+ )),
+ None => Err(format_err!(
+ "Unexpected termination on `cargo generate-lockfile`",
+ )),
+ }
+}
+
+pub fn audit_package(workspace_root: &Path, manifest_path: Option<&Path>) -> Result<()> {
+ let database = Database::fetch().context("Failed to fetch security advisory database")?;
+ let lockfile = generate_lockfile(workspace_root, manifest_path)?;
+ let settings = Settings::default();
+ let report = Report::generate(&database, &lockfile, &settings);
+
+ if report.vulnerabilities.found {
+ let VulnerabilityInfo { count, list, .. } = report.vulnerabilities;
+
+ let mut message = match count {
+ 1 => format!("Found {} vulnerability:\n", count),
+ _ => format!("Found {} vulnerabilities:\n", count),
+ };
+
+ for Vulnerability {
+ package,
+ versions,
+ advisory,
+ ..
+ } in list
+ {
+ message.push('\n');
+ message.push_str(&format!("Crate: {}\n", package.name));
+ message.push_str(&format!("Version: {}\n", package.version.to_string()));
+ message.push_str(&format!("Title: {}\n", advisory.title));
+ message.push_str(&format!("Date: {}\n", advisory.date.as_str()));
+ message.push_str(&format!("ID: {}\n", advisory.id));
+
+ if let Some(url) = advisory.id.url() {
+ message.push_str(&format!("URL: {}\n", url));
+ } else if let Some(url) = &advisory.url {
+ message.push_str(&format!("URL: {}\n", url));
+ }
+
+ if versions.patched().is_empty() {
+ message.push_str("Solution: No solution available\n");
+ } else {
+ let patched = versions
+ .patched()
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>()
+ .as_slice()
+ .join(" or ");
+
+ message.push_str(&format!("Solution: Upgrade to {}\n", patched));
+ }
+ }
+
+ message.push_str("\nPlease fix the issues or use \"--noaudit\" flag.\n");
+
+ return Err(format_err!(message));
+ }
+
+ Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index 6b2d2d3..f478bc0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,6 +8,7 @@
* except according to those terms.
*/
+mod audit;
mod license;
mod metadata;
@@ -18,10 +19,11 @@ use std::collections::BTreeSet;
use std::fs::OpenOptions;
use std::path::Path;
+use audit::audit_package;
use license::{normalize_license, split_spdx_license};
use metadata::EbuildConfig;
-pub fn gen_ebuild_data(manifest_path: Option<&Path>) -> Result<EbuildConfig> {
+pub fn gen_ebuild_data(manifest_path: Option<&Path>, audit: bool) -> Result<EbuildConfig> {
let mut cmd = MetadataCommand::new();
cmd.features(CargoOpt::AllFeatures);
@@ -44,6 +46,10 @@ pub fn gen_ebuild_data(manifest_path: Option<&Path>) -> Result<EbuildConfig> {
.as_ref()
.ok_or_else(|| format_err!("cargo metadata failed to resolve the root package"))?;
+ if audit {
+ audit_package(metadata.workspace_root.as_ref(), manifest_path)?;
+ }
+
let mut licenses = BTreeSet::new();
let mut crates = Vec::new();
let mut root_pkg = None;
diff --git a/src/main.rs b/src/main.rs
index 12dd0e0..1072094 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,9 +22,13 @@ struct Args {
#[structopt(name = "PATH", long = "manifest-path", parse(from_os_str))]
/// Path to Cargo.toml.
manifest_path: Option<PathBuf>,
+
#[structopt(name = "TEMPLATE", long = "template-path", short)]
/// Non-standard template
template_path: Option<PathBuf>,
+
+ #[structopt(long)]
+ noaudit: bool,
}
#[derive(StructOpt, Debug)]
@@ -45,8 +49,7 @@ fn main() -> Result<()> {
let Opt::Ebuild(opt) = Opt::from_args();
// compute the data from the package that the build needs
- let ebuild_data = gen_ebuild_data(opt.manifest_path.as_deref())?;
-
+ let ebuild_data = gen_ebuild_data(opt.manifest_path.as_deref(), !opt.noaudit)?;
let ebuild_path = format!("{}-{}.ebuild", ebuild_data.name, ebuild_data.version);
write_ebuild(ebuild_data, ebuild_path.as_ref(), opt.template_path.as_deref())?;