Files
advisory-db/src/main.rs
Tony Arcieri cb4f7d11af lint: Check that affected_paths start with crate name
Uses the crate name as fetched from the crates.io API to ensure all
`affected_paths` begin with the crate name (i.e. are canonical)
2019-01-13 17:53:43 -08:00

121 lines
3.3 KiB
Rust

#![allow(clippy::never_loop)]
use gumdrop::Options;
use rustsec::{AdvisoryDatabase, Repository};
use std::{env, process::exit};
const MIN_EXPECTED_ADVISORIES: usize = 5;
/// Subcommands
#[derive(Debug, Options)]
enum Opts {
#[options(help = "show help for a command")]
Help(HelpOpts),
#[options(help = "check the advisory DB is well-formed")]
Check(CheckOpts),
}
/// Options for the `help` command
#[derive(Debug, Default, Options)]
struct HelpOpts {
#[options(free)]
commands: Vec<String>,
}
/// Options for the `check` command
#[derive(Debug, Default, Options)]
struct CheckOpts {}
fn main() {
let args: Vec<_> = env::args().collect();
let opts = Opts::parse_args_default(&args[1..]).unwrap_or_else(|e| {
match e.to_string().as_ref() {
// Show usage if no command name is given or if "help" is given
"missing command name" => help(&[]),
string => eprintln!("{}: {}", args[0], string),
}
exit(2);
});
match opts {
Opts::Help(opts) => help(&opts.commands),
Opts::Check(_) => check(),
}
exit(0);
}
/// Print help message
fn help(_commands: &[String]) {
println!("Usage: {} [COMMAND] [OPTIONS]", env::args().next().unwrap());
println!();
println!("Available commands:");
println!();
println!("{}", Opts::command_list().unwrap());
println!();
}
fn check() {
let repo = Repository::open(".").unwrap();
// Ensure Advisories.toml parses
let db = AdvisoryDatabase::from_repository(&repo).unwrap();
let advisories = db.advisories();
// Ensure we're parsing some advisories
if advisories.len() > MIN_EXPECTED_ADVISORIES {
println!(
"*** Check succeeded! Successfully parsed {} advisories.",
advisories.len()
);
} else {
panic!(
"Missing advisories! Expected at least {}, but got {}",
MIN_EXPECTED_ADVISORIES,
advisories.len()
);
}
let cratesio_client = crates_io_api::SyncClient::new();
for advisory in advisories {
check_advisory(&cratesio_client, advisory);
}
println!("*** Check succeeded! All advisories refer to valid crates.");
}
fn check_advisory(cratesio_client: &crates_io_api::SyncClient, advisory: &rustsec::Advisory) {
let response = cratesio_client
.get_crate(advisory.package.as_str())
.unwrap_or_else(|_| {
panic!(
"Failed to get package from crates.io: {}",
advisory.package.as_str()
)
});
if response.crate_data.name != advisory.package.as_str() {
panic!(
"crates.io package name does not match package name in advisory for {}",
advisory.package.as_str()
);
}
// Check that each path in `affected_paths` starts with the crate name
if let Some(ref version_req_paths) = advisory.affected_paths {
for (_, paths) in version_req_paths.iter() {
for path in paths {
if path.crate_name() != response.crate_data.name {
panic!(
"{}: affected_path does not begin with crate name: {}",
response.crate_data.name,
path.crate_name()
)
}
}
}
}
}