From e78e696173067f0cda2809ef2587a54cfa3c44a1 Mon Sep 17 00:00:00 2001 From: Alex Franchuk Date: Thu, 7 Dec 2023 16:36:39 -0500 Subject: [PATCH] Prune non-importable audits. This addresses part of #580. --- src/cli.rs | 3 + src/main.rs | 53 ++++++---- src/resolver.rs | 85 ++++++++++++---- src/tests/certify.rs | 97 ++++++++++++++++++- src/tests/import.rs | 13 ++- src/tests/mod.rs | 55 ++++++++++- src/tests/regenerate_unaudited.rs | 2 + ...tify__mock-prune-non-importable-audit.snap | 79 +++++++++++++++ src/tests/unpublished.rs | 10 ++ tests/snapshots/test_cli__markdown-help.snap | 4 +- 10 files changed, 354 insertions(+), 47 deletions(-) create mode 100644 src/tests/snapshots/cargo_vet__tests__certify__mock-prune-non-importable-audit.snap diff --git a/src/cli.rs b/src/cli.rs index 11213a6c..18811fa4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -682,6 +682,9 @@ pub struct PruneArgs { /// Don't prune unused exemptions #[clap(long, action)] pub no_exemptions: bool, + /// Don't prune unused non-importable audits. + #[clap(long, action)] + pub no_audits: bool, } #[derive(clap::Args)] diff --git a/src/main.rs b/src/main.rs index 7e6bee33..ca4b2964 100644 --- a/src/main.rs +++ b/src/main.rs @@ -541,6 +541,7 @@ fn cmd_init(_out: &Arc, cfg: &Config, _sub_args: &InitArgs) -> Result<( resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); @@ -982,10 +983,10 @@ fn do_cmd_certify( .validate(cfg.today(), false) .expect("the new audit entry made the store invalid?"); - // Minimize exemptions after adding the new audit. This will be used to - // potentially update imports, and remove now-unnecessary exemptions for the - // target package. We only prefer fresh imports and prune exemptions for the - // package we certified, to avoid unrelated changes. + // Minimize exemptions after adding the new audit. This will be used to potentially update + // imports, and remove now-unnecessary exemptions and audits for the target package. We only + // prefer fresh imports and prune exemptions for the package we certified, to avoid unrelated + // changes. resolver::update_store(cfg, store, |name| resolver::UpdateMode { search_mode: if name == &package[..] { resolver::SearchMode::PreferFreshImports @@ -993,6 +994,7 @@ fn do_cmd_certify( resolver::SearchMode::PreferExemptions }, prune_exemptions: name == &package[..], + prune_non_importable_audits: name == &package[..], prune_imports: false, }); @@ -1237,11 +1239,11 @@ fn cmd_import( let cache = Cache::acquire(cfg)?; tokio::runtime::Handle::current().block_on(store.go_online(cfg, &network, &cache, false))?; - // Update the store state, pruning unnecessary exemptions, and cleaning out - // unnecessary old imports. + // Update the store state, pruning unnecessary exemptions, audits, and imports. resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); @@ -1493,10 +1495,10 @@ fn apply_cmd_trust( .validate(cfg.today(), false) .expect("the new trusted entry made the store invalid?"); - // Minimize exemptions after adding the new trust entry. This will be used - // to potentially update imports, and remove now-unnecessary exemptions for - // the target package. We only prefer fresh imports and prune exemptions for - // the package we trusted, to avoid unrelated changes. + // Minimize exemptions and audits after adding the new trust entry. This will be used to + // potentially update imports, and remove now-unnecessary exemptions for the target package. We + // only prefer fresh imports and prune exemptions for the package we trusted, to avoid + // unrelated changes. resolver::update_store(cfg, store, |name| resolver::UpdateMode { search_mode: if name == package { resolver::SearchMode::PreferFreshImports @@ -1504,6 +1506,7 @@ fn apply_cmd_trust( resolver::SearchMode::PreferExemptions }, prune_exemptions: name == package, + prune_non_importable_audits: name == package, prune_imports: false, }); Ok(()) @@ -1689,11 +1692,11 @@ fn cmd_regenerate_imports( let network = Network::acquire(cfg); let mut store = Store::acquire(cfg, network.as_ref(), true)?; - // Update the store state, pruning unnecessary exemptions, and cleaning out - // unnecessary old imports. + // Update the store state, pruning unnecessary exemptions, audits, and imports. resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); @@ -1753,6 +1756,7 @@ fn cmd_regenerate_unpublished( resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }); @@ -1954,6 +1958,7 @@ fn cmd_regenerate_exemptions( resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); @@ -2130,33 +2135,38 @@ fn cmd_check( if !cfg.cli.locked { // Simulate a full `fetch-imports` run, and record the potential // pruned imports and exemptions. - let (pruned_imports, pruned_exemptions) = - resolver::get_store_updates(cfg, &store, |_| resolver::UpdateMode { - search_mode: resolver::SearchMode::PreferFreshImports, - prune_exemptions: true, - prune_imports: true, - }); + let updates = resolver::get_store_updates(cfg, &store, |_| resolver::UpdateMode { + search_mode: resolver::SearchMode::PreferFreshImports, + prune_exemptions: true, + prune_non_importable_audits: true, + prune_imports: true, + }); // Perform a minimal store update to pull in necessary imports, // while avoiding any other changes to exemptions or imports. resolver::update_store(cfg, &mut store, |_| resolver::UpdateMode { search_mode: resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }); // XXX: Consider trying to be more precise here? Would require some // more clever comparisons. - if store.config.exemptions != pruned_exemptions { + if store.config.exemptions != updates.exemptions { warn!("Your supply-chain has unnecessary exemptions which could be relaxed or pruned."); warn!(" Consider running `cargo vet prune` to prune unnecessary exemptions and imports."); - } else if store.imports != pruned_imports { + } else if store.imports != updates.imports { warn!("Your supply-chain has unnecessary imports which could be pruned."); warn!(" Consider running `cargo vet prune` to prune unnecessary imports."); + } else if store.audits.audits != updates.audits { + warn!("Your supply-chain has unnecessary audits which could be pruned."); + warn!(" Consider running `cargo vet prune` to prune unnecessary imports."); } // Check if we have `unpublished` entries for crates which have since been published. - let since_published: Vec<_> = pruned_imports + let since_published: Vec<_> = updates + .imports .unpublished .iter() .filter(|(_, unpublished)| unpublished.iter().any(|u| !u.still_unpublished)) @@ -2293,6 +2303,7 @@ fn cmd_prune( resolver::SearchMode::PreferFreshImports }, prune_exemptions: !sub_args.no_exemptions, + prune_non_importable_audits: !sub_args.no_audits, prune_imports: !sub_args.no_imports, }); diff --git a/src/resolver.rs b/src/resolver.rs index 3f927fbe..46b5bee7 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -282,7 +282,10 @@ pub struct AuditGraph<'a> { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum DeltaEdgeOrigin { /// This edge represents an audit from the local audits.toml. - StoredLocalAudit { audit_index: usize }, + StoredLocalAudit { + audit_index: usize, + importable: bool, + }, /// This edge represents an audit imported from a peer, potentially stored /// in the local imports.lock. ImportedAudit { @@ -307,10 +310,13 @@ pub enum DeltaEdgeOrigin { FreshExemption { version: VetVersion }, } -/// An indication of a required imported entry or exemption. Used to compute the -/// minimal set of possible imports for imports.lock. +/// An indication of a required local audit, imported entry, or exemption. Used to compute the +/// minimal set of possible imports for imports.lock and for pruning unused audits and exemptions. #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum RequiredEntry { + LocalAudit { + audit_index: usize, + }, Audit { import_index: usize, audit_index: usize, @@ -1070,7 +1076,10 @@ impl<'a> AuditGraph<'a> { import_index, audit_index, }, - None => DeltaEdgeOrigin::StoredLocalAudit { audit_index }, + None => DeltaEdgeOrigin::StoredLocalAudit { + audit_index, + importable: audit.importable, + }, }, audit, ) @@ -1437,6 +1446,7 @@ fn search_for_path( #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] enum CaveatLevel { None, + NonImportableAudit, PreferredExemption, PreferredUnpublished, FreshImport, @@ -1558,6 +1568,9 @@ fn search_for_path( // Compute the level of caveats which are being added by the current edge let edge_caveat_level = match &edge.origin { + DeltaEdgeOrigin::StoredLocalAudit { importable, .. } if !importable => { + CaveatLevel::NonImportableAudit + } DeltaEdgeOrigin::Exemption { .. } if mode == SearchMode::PreferExemptions => { CaveatLevel::PreferredExemption } @@ -2670,8 +2683,7 @@ fn resolve_package_required_entries( ) -> Option> { assert_eq!(graph.nodes.len(), requirements.len()); - // Collect the list of third-party packages with the given name, along with - // their requirements. + // Collect the list of third-party packages with the given name, along with their requirements. let packages: Vec<_> = graph .nodes .iter() @@ -2679,8 +2691,7 @@ fn resolve_package_required_entries( .filter(|(package, _)| package.name == package_name && package.is_third_party) .collect(); - // If there are no third-party packages with the name, we definitely don't - // need any entries. + // If there are no third-party packages with the name, we definitely don't need any entries. if packages.is_empty() { return Some(SortedMap::new()); } @@ -2744,7 +2755,9 @@ fn resolve_package_required_entries( DeltaEdgeOrigin::Unpublished { unpublished_index } => { add_entry(RequiredEntry::Unpublished { unpublished_index }) } - DeltaEdgeOrigin::StoredLocalAudit { .. } => {} + DeltaEdgeOrigin::StoredLocalAudit { audit_index, .. } => { + add_entry(RequiredEntry::LocalAudit { audit_index }) + } } } } @@ -2759,21 +2772,32 @@ fn resolve_package_required_entries( pub struct UpdateMode { pub search_mode: SearchMode, pub prune_exemptions: bool, + pub prune_non_importable_audits: bool, pub prune_imports: bool, } +pub(crate) struct StoreUpdates { + pub audits: SortedMap>, + pub imports: ImportsFile, + pub exemptions: SortedMap>, +} + +impl StoreUpdates { + pub fn apply(self, store: &mut Store) { + store.audits.audits = self.audits; + store.imports = self.imports; + store.config.exemptions = self.exemptions; + } +} + /// Refresh the state of the store, importing required audits, and optionally -/// pruning unnecessary exemptions and/or imports. +/// pruning unnecessary exemptions, audits, and/or imports. pub fn update_store( cfg: &Config, store: &mut Store, mode: impl FnMut(PackageStr<'_>) -> UpdateMode, ) { - let (new_imports, new_exemptions) = get_store_updates(cfg, store, mode); - - // Update the store to reflect the new imports and exemptions files. - store.imports = new_imports; - store.config.exemptions = new_exemptions; + get_store_updates(cfg, store, mode).apply(store); } /// The non-mutating core of `update_store` for use in non-mutating situations. @@ -2781,7 +2805,7 @@ pub(crate) fn get_store_updates( cfg: &Config, store: &Store, mut mode: impl FnMut(PackageStr<'_>) -> UpdateMode, -) -> (ImportsFile, SortedMap>) { +) -> StoreUpdates { // Compute the set of required entries from the store for all packages in // the dependency graph. let graph = DepGraph::new( @@ -2806,6 +2830,29 @@ pub(crate) fn get_store_updates( }); } + // Remove unused non-importable audits. + let mut new_audits = store.audits.audits.clone(); + for (pkg, entries) in &required_entries { + if !mode(pkg).prune_non_importable_audits { + continue; + } + let Some(entries) = entries else { continue }; + + if let Some(audit_entries) = new_audits.get_mut(*pkg) { + *audit_entries = std::mem::take(audit_entries) + .into_iter() + .enumerate() + .filter(|&(audit_index, ref entry)| { + // Keep the entry if it's importable (i.e. it could be used externally) or it's + // used locally. + entry.importable + || entries.contains_key(&RequiredEntry::LocalAudit { audit_index }) + }) + .map(|(_, entry)| entry) + .collect(); + } + } + // Dummy value to use if a package isn't found in `required_entries` - no // edges will be required. let no_required_entries = Some(SortedMap::new()); @@ -3096,5 +3143,9 @@ pub(crate) fn get_store_updates( exemptions.sort(); } - (new_imports, all_new_exemptions) + StoreUpdates { + audits: new_audits, + imports: new_imports, + exemptions: all_new_exemptions, + } } diff --git a/src/tests/certify.rs b/src/tests/certify.rs index 9c25d42d..9e185729 100644 --- a/src/tests/certify.rs +++ b/src/tests/certify.rs @@ -212,12 +212,16 @@ fn mock_delta_certify_flow() { #[test] fn mock_delta_git_certify_flow() { - let mock = MockMetadata::simple(); + let mock = MockMetadata::simple_local_git(); let _enter = TEST_RUNTIME.enter(); let metadata = mock.metadata(); - let (config, audits, imports) = files_inited(&metadata); + let (mut config, audits, imports) = files_inited(&metadata); + + config + .policy + .insert("third-party1".to_string(), audit_as_policy(Some(true))); let mut store = Store::mock(config, audits, imports); @@ -272,6 +276,95 @@ fn mock_delta_git_certify_flow() { insta::assert_snapshot!("mock-delta-git-certify-flow", result); } +#[test] +fn mock_prune_non_importable_audit() { + let mock = MockMetadata::simple(); + + let _enter = TEST_RUNTIME.enter(); + let metadata = mock.metadata(); + + let (config, mut audits, imports) = files_inited(&metadata); + + audits.audits.insert( + "third-party1".to_owned(), + vec![ + { + let mut entry = delta_audit( + ver(10), + "10.0.0@git:00112233445566778899aabbccddeeff00112233" + .parse() + .unwrap(), + "safe-to-deploy", + ); + entry.importable = false; + entry + }, + { + let mut entry = delta_audit( + ver(10), + "10.0.0@git:ffeeddccbbaa99887766554433221100ffeeddcc" + .parse() + .unwrap(), + "safe-to-deploy", + ); + entry.notes = Some("This entry intentionally left importable.".into()); + entry + }, + ], + ); + + let mut store = Store::mock(config, audits, imports); + + let output = BasicTestOutput::with_callbacks( + |_| Ok("\n".to_owned()), + |_| { + Ok("\ + I, testing, certify that I have audited version 10.0.0 of third-party1 in accordance with the above criteria.\n\ + \n\ + These are testing notes. They contain some\n\ + newlines. Trailing whitespace \n \ + and leading whitespace\n\ + \n".to_owned()) + }, + ); + + let cfg = mock_cfg_args( + &metadata, + [ + "cargo", + "vet", + "certify", + "third-party1", + "10.0.0", + "--who", + "testing", + "--criteria", + "safe-to-deploy", + ], + ); + let sub_args = if let Some(crate::cli::Commands::Certify(sub_args)) = &cfg.cli.command { + sub_args + } else { + unreachable!(); + }; + + crate::do_cmd_certify( + &output.clone().as_dyn(), + &cfg, + sub_args, + &mut store, + None, + None, + ) + .expect("do_cmd_certify failed"); + + let audits = crate::serialization::to_formatted_toml(&store.audits, None).unwrap(); + + let result = format!("OUTPUT:\n{output}\nAUDITS:\n{audits}"); + + insta::assert_snapshot!("mock-prune-non-importable-audit", result); +} + #[test] fn mock_wildcard_certify_flow() { let mock = MockMetadata::simple(); diff --git a/src/tests/import.rs b/src/tests/import.rs index 88051cb7..26381a42 100644 --- a/src/tests/import.rs +++ b/src/tests/import.rs @@ -7,8 +7,7 @@ fn get_imports_file_changes( store: &Store, mode: impl FnMut(PackageStr<'_>) -> crate::resolver::UpdateMode, ) -> String { - let (new_imports, _new_exemptions) = - crate::resolver::get_store_updates(&mock_cfg(metadata), store, mode); + let updates = crate::resolver::get_store_updates(&mock_cfg(metadata), store, mode); // Format the old and new files as TOML, and write out a diff using `similar`. let old_imports = crate::serialization::to_formatted_toml( @@ -18,8 +17,8 @@ fn get_imports_file_changes( .unwrap() .to_string(); let new_imports = crate::serialization::to_formatted_toml( - &new_imports, - Some(&crate::storage::user_info_map(&new_imports)), + &updates.imports, + Some(&crate::storage::user_info_map(&updates.imports)), ) .unwrap() .to_string(); @@ -31,6 +30,7 @@ fn get_imports_file_changes_prune(metadata: &Metadata, store: &Store) -> String get_imports_file_changes(metadata, store, |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: true, }) } @@ -39,6 +39,7 @@ fn get_imports_file_changes_noprune(metadata: &Metadata, store: &Store) -> Strin get_imports_file_changes(metadata, store, |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }) } @@ -948,6 +949,7 @@ fn peer_audits_exemption_minimize() { ("prune", |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }), ("certify", |name| { @@ -955,12 +957,14 @@ fn peer_audits_exemption_minimize() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: false, } } else { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, } } @@ -968,6 +972,7 @@ fn peer_audits_exemption_minimize() { ("vet", |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }), ]; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 0ac4d4f8..1bd26ce3 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -451,6 +451,49 @@ impl MockMetadata { ]) } + fn simple_local_git() -> Self { + // Identical to `simple` except that `third-party1` is a local git version. + MockMetadata::new(vec![ + MockPackage { + name: "root-package", + is_workspace: true, + is_first_party: true, + deps: vec![dep("first-party")], + ..Default::default() + }, + MockPackage { + name: "first-party", + is_first_party: true, + deps: vec![ + MockDependency { + name: "third-party1", + version: "10.0.0@git:00112233445566778899aabbccddeeff00112233" + .parse() + .unwrap(), + }, + dep("third-party2"), + ], + ..Default::default() + }, + MockPackage { + name: "third-party1", + version: "10.0.0@git:00112233445566778899aabbccddeeff00112233" + .parse() + .unwrap(), + deps: vec![dep("transitive-third-party1")], + ..Default::default() + }, + MockPackage { + name: "third-party2", + ..Default::default() + }, + MockPackage { + name: "transitive-third-party1", + ..Default::default() + }, + ]) + } + fn complex() -> Self { // A Complex dependency tree to test more weird interactions and corner cases: // @@ -716,6 +759,11 @@ impl MockMetadata { "{} {} (path+file:///C:/FAKE/{})", package.name, package.version, package.name ) + } else if let Some(git_rev) = &package.version.git_rev { + format!( + "{} {} (git+https://github.com/owner/{}#{})", + package.name, package.version.semver, package.name, git_rev + ) } else { format!( "{} {} (registry+https://github.com/rust-lang/crates.io-index)", @@ -757,6 +805,8 @@ impl MockMetadata { fn source(&self, package: &MockPackage) -> Value { if package.is_first_party { json!(null) + } else if let Some(git_rev) = &package.version.git_rev { + format!("git+https://github.com/owner/{}#{}", package.name, git_rev).into() } else { json!("registry+https://github.com/rust-lang/crates.io-index") } @@ -766,7 +816,7 @@ impl MockMetadata { let meta_json = json!({ "packages": self.packages.iter().map(|package| json!({ "name": package.name, - "version": package.version.to_string(), + "version": package.version.semver.to_string(), "id": self.pkgid(package), "license": "MIT", "license_file": null, @@ -775,7 +825,7 @@ impl MockMetadata { "dependencies": package.deps.iter().chain(&package.dev_deps).chain(&package.build_deps).map(|dep| json!({ "name": dep.name, "source": self.source(self.package_by(dep.name, &dep.version)), - "req": format!("={}", dep.version), + "req": format!("={}", dep.version.semver), "kind": null, "rename": null, "optional": false, @@ -909,6 +959,7 @@ fn init_files( crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, } }); diff --git a/src/tests/regenerate_unaudited.rs b/src/tests/regenerate_unaudited.rs index 04bb622d..a10a4d18 100644 --- a/src/tests/regenerate_unaudited.rs +++ b/src/tests/regenerate_unaudited.rs @@ -15,6 +15,7 @@ fn basic_regenerate(cfg: &Config, store: &mut Store) { crate::resolver::update_store(cfg, store, |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); } @@ -23,6 +24,7 @@ fn basic_minimize(cfg: &Config, store: &mut Store) { crate::resolver::update_store(cfg, store, |_| crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }); } diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock-prune-non-importable-audit.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock-prune-non-importable-audit.snap new file mode 100644 index 00000000..e6e7fb62 --- /dev/null +++ b/src/tests/snapshots/cargo_vet__tests__certify__mock-prune-non-importable-audit.snap @@ -0,0 +1,79 @@ +--- +source: src/tests/certify.rs +expression: result +--- +OUTPUT: +<<>> +# Please read the following criteria and then follow the instructions below: + +# === BEGIN CRITERIA "safe-to-deploy" === +# +# This crate will not introduce a serious security vulnerability to production +# software exposed to untrusted input. +# +# Auditors are not required to perform a full logic review of the entire crate. +# Rather, they must review enough to fully reason about the behavior of all unsafe +# blocks and usage of powerful imports. For any reasonable usage of the crate in +# real-world software, an attacker must not be able to manipulate the runtime +# behavior of these sections in an exploitable or surprising way. +# +# Ideally, all unsafe code is fully sound, and ambient capabilities (e.g. +# filesystem access) are hardened against manipulation and consistent with the +# advertised behavior of the crate. However, some discretion is permitted. In such +# cases, the nature of the discretion should be recorded in the `notes` field of +# the audit record. +# +# For crates which generate deployed code (e.g. build dependencies or procedural +# macros), reasonable usage of the crate should output code which meets the above +# criteria. +# +# === END CRITERIA === +# +# Uncomment the following statement: + +# I, testing, certify that I have audited version 10.0.0 of third-party1 in accordance with the above criteria. + +# Add any notes about your audit below this line: + + +<<>> +I, testing, certify that I have audited version 10.0.0 of third-party1 in accordance with the above criteria. + +These are testing notes. They contain some +newlines. Trailing whitespace + and leading whitespace + + +<<>> + +AUDITS: + +[criteria.fuzzed] +description = "fuzzed" + +[criteria.reviewed] +description = "reviewed" +implies = "weak-reviewed" + +[criteria.strong-reviewed] +description = "strongly reviewed" +implies = "reviewed" + +[criteria.weak-reviewed] +description = "weakly reviewed" + +[[audits.third-party1]] +criteria = "safe-to-deploy" +delta = "10.0.0 -> 10.0.0@git:ffeeddccbbaa99887766554433221100ffeeddcc" +notes = "This entry intentionally left importable." + +[[audits.third-party1]] +who = "testing" +criteria = "safe-to-deploy" +version = "10.0.0" +notes = """ +These are testing notes. They contain some +newlines. Trailing whitespace + and leading whitespace +""" + diff --git a/src/tests/unpublished.rs b/src/tests/unpublished.rs index 733eb737..a2c9e2b2 100644 --- a/src/tests/unpublished.rs +++ b/src/tests/unpublished.rs @@ -94,6 +94,7 @@ fn audit_as_crates_io_unpublished_blank_regenerate_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -107,6 +108,7 @@ fn audit_as_crates_io_unpublished_full_regenerate_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -120,6 +122,7 @@ fn audit_as_crates_io_unpublished_full_prune() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -133,6 +136,7 @@ fn audit_as_crates_io_unpublished_full_prefer_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }, ); @@ -146,6 +150,7 @@ fn audit_as_crates_io_unpublished_wildcard_regenerate_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -159,6 +164,7 @@ fn audit_as_crates_io_unpublished_wildcard_prune() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -172,6 +178,7 @@ fn audit_as_crates_io_unpublished_wildcard_prefer_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }, ); @@ -185,6 +192,7 @@ fn audit_as_crates_io_unpublished_wildcard_nounpublished_regenerate_exemptions() crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::RegenerateExemptions, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -198,6 +206,7 @@ fn audit_as_crates_io_unpublished_wildcard_nounpublished_prune() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferFreshImports, prune_exemptions: true, + prune_non_importable_audits: true, prune_imports: true, }, ); @@ -211,6 +220,7 @@ fn audit_as_crates_io_unpublished_wildcard_nounpublished_prefer_exemptions() { crate::resolver::UpdateMode { search_mode: crate::resolver::SearchMode::PreferExemptions, prune_exemptions: false, + prune_non_importable_audits: false, prune_imports: false, }, ); diff --git a/tests/snapshots/test_cli__markdown-help.snap b/tests/snapshots/test_cli__markdown-help.snap index 080db015..fa03a424 100644 --- a/tests/snapshots/test_cli__markdown-help.snap +++ b/tests/snapshots/test_cli__markdown-help.snap @@ -1,6 +1,5 @@ --- source: tests/test-cli.rs -assertion_line: 134 expression: format_outputs(&output) --- stdout: @@ -772,6 +771,9 @@ Don't prune unused imports #### `--no-exemptions` Don't prune unused exemptions +#### `--no-audits` +Don't prune unused non-importable audits + #### `-h, --help` Print help information