Repository: mozilla/cargo-vet
Pull request #433 (2023-03-10 21:22:12)

Map criteria into the local namespace eagerly when importing
This removes the need for criteria namespaces entirely, and also allows us to implement mapping behaviour over the built-in criteria without bloating the criteria set.

Unfortunately, this causes changes in the (quite-unstable) violation JSON output. We'll probably want to change it in the future to not partially use internal data structures so that it can be more stable.

Fixes https://github.com/mozilla/cargo-vet/issues/425
Fixes https://github.com/mozilla/cargo-vet/issues/430


Patch:
diff --git a/registry.toml b/registry.toml
index b1a9657..72466e1 100644
--- a/registry.toml
+++ b/registry.toml
@@ -1,15 +1,9 @@
 [registry.bytecode-alliance]
 url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml"
 
-[registry.chromeos]
-url = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/main/cargo-vet/audits.toml?format=TEXT"
-
 [registry.embark-studios]
 url = "https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml"
 
-[registry.fermyon]
-url = "https://raw.githubusercontent.com/fermyon/spin/main/supply-chain/audits.toml"
-
 [registry.isrg]
 url = "https://raw.githubusercontent.com/divviup/libprio-rs/main/supply-chain/audits.toml"
 
diff --git a/src/criteria.rs b/src/criteria.rs
new file mode 100644
index 0000000..0c38cad
--- /dev/null
+++ b/src/criteria.rs
@@ -0,0 +1,225 @@
+//! Helper types for working with criteria and criteria sets.
+
+use std::fmt;
+
+use crate::format::{
+    CriteriaEntry, CriteriaName, CriteriaStr, FastMap, SortedMap, SAFE_TO_DEPLOY, SAFE_TO_RUN,
+};
+
+/// Set of booleans, 64 should be Enough For Anyone (but abstracting in case not).
+///
+/// Note that this intentionally doesn't implement Default to allow the implementation
+/// to require the CriteriaMapper to provide the count of items at construction time.
+/// Which will be useful if we ever decide to give it ~infinite capacity and wrap
+/// a BitSet.
+#[derive(Clone)]
+pub struct CriteriaSet(u64);
+const MAX_CRITERIA: usize = u64::BITS as usize; // funnier this way
+
+/// A processed version of config.toml's criteria definitions, for mapping
+/// lists of criteria names to CriteriaSets.
+#[derive(Debug, Clone)]
+pub struct CriteriaMapper {
+    /// name -> index in all lists
+    index: FastMap<CriteriaName, usize>,
+    /// Names for every criteria
+    names: Vec<CriteriaName>,
+    /// The transitive closure of all criteria implied by each criteria (including self)
+    implied_criteria: Vec<CriteriaSet>,
+}
+
+impl CriteriaMapper {
+    pub fn new(criteria: &SortedMap<CriteriaName, CriteriaEntry>) -> CriteriaMapper {
+        // Fixed indices for built-in criteria
+        const SAFE_TO_RUN_IDX: usize = 0;
+        const SAFE_TO_DEPLOY_IDX: usize = 1;
+
+        // Build the list of possible criteria
+        let names: Vec<CriteriaName> = [SAFE_TO_RUN.to_owned(), SAFE_TO_DEPLOY.to_owned()]
+            .into_iter()
+            .chain(criteria.keys().cloned())
+            .collect();
+        assert_eq!(names[SAFE_TO_RUN_IDX], SAFE_TO_RUN);
+        assert_eq!(names[SAFE_TO_DEPLOY_IDX], SAFE_TO_DEPLOY);
+
+        // Populate the index from the list to allow fast by-name look-ups
+        let mut index = FastMap::with_capacity(names.len());
+        for (idx, name) in names.iter().enumerate() {
+            if index.insert(name.clone(), idx).is_some() {
+                // XXX: Consider producing a better error here?
+                panic!("Cannot specify multiple criteria with the name '{name}'");
+            }
+        }
+
+        // Create the list containing implied criteria and pre-populate it with
+        // the SAFE_TO_DEPLOY->SAFE_TO_RUN imply.
+        let mut direct_implies = vec![CriteriaSet::none(names.len()); names.len()];
+        direct_implies[SAFE_TO_DEPLOY_IDX].set_criteria(SAFE_TO_RUN_IDX);
+        for (name, entry) in criteria {
+            let idx = index[name];
+            for implied in &entry.implies {
+                direct_implies[idx].set_criteria(index[&**implied]);
+            }
+        }
+
+        let implied_criteria = (0..names.len())
+            .map(|idx| {
+                // Helper to recursively add all criteria implied by the given
+                // criteria to the CriteriaSet.
+                fn recurse_implies(
+                    result: &mut CriteriaSet,
+                    direct_implies: &[CriteriaSet],
+                    cur_idx: usize,
+                ) {
+                    for idx in direct_implies[cur_idx].indices() {
+                        if !result.has_criteria(idx) {
+                            result.set_criteria(idx);
+                            recurse_implies(result, direct_implies, idx);
+                        }
+                    }
+                }
+
+                // Determine all criteria implied by each index, ensure each
+                // criteria does not imply itself, and then complete the set.
+                let mut implied = CriteriaSet::none(names.len());
+                recurse_implies(&mut implied, &direct_implies, idx);
+                if implied.has_criteria(idx) {
+                    // XXX: Consider producing a better error here?
+                    panic!("criteria '{}' implies itself", names[idx]);
+                }
+                implied.set_criteria(idx);
+                implied
+            })
+            .collect();
+
+        CriteriaMapper {
+            index,
+            names,
+            implied_criteria,
+        }
+    }
+
+    /// Builds a CriteriaSet from a list of criteria.
+    pub fn criteria_from_list<'b, S: AsRef<str> + 'b + ?Sized>(
+        &self,
+        list: impl IntoIterator<Item = &'b S>,
+    ) -> CriteriaSet {
+        let mut result = self.no_criteria();
+        for criteria in list {
+            self.set_criteria(&mut result, criteria.as_ref());
+        }
+        result
+    }
+
+    /// Set the given named criteria and all criteria implied by it within the
+    /// given CriteriaSet.
+    pub fn set_criteria(&self, set: &mut CriteriaSet, criteria: CriteriaStr) {
+        set.unioned_with(&self.implied_criteria[self.index[criteria]])
+    }
+
+    /// An iterator over every criteria in order, with 'implies' fully applied.
+    pub fn all_criteria_iter(&self) -> impl Iterator<Item = &CriteriaSet> {
+        self.implied_criteria.iter()
+    }
+
+    /// Get the total number of criteria.
+    pub fn len(&self) -> usize {
+        self.names.len()
+    }
+
+    /// Get a CriteriaSet of the correct size for this CriteriaMap containing no criteria
+    pub fn no_criteria(&self) -> CriteriaSet {
+        CriteriaSet::none(self.len())
+    }
+
+    /// Like [`CriteriaSet::indices`] but uses knowledge of things like
+    /// `implies` relationships to remove redundant information. For
+    /// instance, if safe-to-deploy is set, we don't also yield safe-to-run.
+    pub fn minimal_indices<'a>(
+        &'a self,
+        criteria: &'a CriteriaSet,
+    ) -> impl Iterator<Item = usize> + 'a {
+        criteria.indices().filter(|&cur_idx| {
+            criteria.indices().all(|other_idx| {
+                // Ignore our own index
+                let is_identity = cur_idx == other_idx;
+                // Discard this criteria if it's implied by another
+                let isnt_implied = !self.implied_criteria[other_idx].has_criteria(cur_idx);
+                is_identity || isnt_implied
+            })
+        })
+    }
+
+    /// Yields all the names of the set criteria with implied members filtered out.
+    pub fn criteria_names<'a>(
+        &'a self,
+        criteria: &'a CriteriaSet,
+    ) -> impl Iterator<Item = CriteriaStr<'a>> + 'a {
+        self.minimal_indices(criteria).map(|idx| &*self.names[idx])
+    }
+
+    /// Yields the names for all criteria
+    pub fn all_criteria_names(&self) -> impl Iterator<Item = CriteriaStr<'_>> + '_ {
+        self.names.iter().map(|s| &s[..])
+    }
+
+    /// Yields the name of a specific criteria by index.
+    pub fn criteria_name(&self, criteria_idx: usize) -> CriteriaStr<'_> {
+        &self.names[criteria_idx][..]
+    }
+
+    /// Yields the index for the given criteria
+    pub fn criteria_index(&self, criteria_name: CriteriaStr<'_>) -> usize {
+        self.index[criteria_name]
+    }
+}
+
+impl CriteriaSet {
+    pub fn none(count: usize) -> Self {
+        assert!(
+            count <= MAX_CRITERIA,
+            "{MAX_CRITERIA} was not Enough For Everyone ({count} criteria)"
+        );
+        CriteriaSet(0)
+    }
+    pub fn set_criteria(&mut self, idx: usize) {
+        self.0 |= 1 << idx;
+    }
+    pub fn clear_criteria(&mut self, other: &CriteriaSet) {
+        self.0 &= !other.0;
+    }
+    pub fn has_criteria(&self, idx: usize) -> bool {
+        (self.0 & (1 << idx)) != 0
+    }
+    pub fn _intersected_with(&mut self, other: &CriteriaSet) {
+        self.0 &= other.0;
+    }
+    pub fn unioned_with(&mut self, other: &CriteriaSet) {
+        self.0 |= other.0;
+    }
+    pub fn contains(&self, other: &CriteriaSet) -> bool {
+        (self.0 & other.0) == other.0
+    }
+    pub fn is_empty(&self) -> bool {
+        self.0 == 0
+    }
+    pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
+        // Yield all the offsets that are set by repeatedly getting the lowest 1 and clearing it
+        let mut raw = self.0;
+        std::iter::from_fn(move || {
+            if raw == 0 {
+                None
+            } else {
+                let next = raw.trailing_zeros() as usize;
+                raw &= !(1 << next);
+                Some(next)
+            }
+        })
+    }
+}
+
+impl fmt::Debug for CriteriaSet {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{:08b}", self.0)
+    }
+}
diff --git a/src/errors.rs b/src/errors.rs
index f2f6803..f51dd90 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -393,9 +393,6 @@ pub enum StoreValidateError {
     #[diagnostic(transparent)]
     #[error(transparent)]
     BadWildcardEndDate(BadWildcardEndDateError),
-    #[diagnostic(transparent)]
-    #[error(transparent)]
-    BadBuiltInCriteriaMapping(BadBuiltInCriteriaMappingError),
     #[error("imports.lock is out-of-date with respect to configuration")]
     #[diagnostic(help("run `cargo vet` without --locked to update imports"))]
     ImportsLockOutdated,
@@ -432,17 +429,6 @@ pub struct BadWildcardEndDateError {
     pub max: chrono::NaiveDate,
 }
 
-#[derive(Debug, Error, Diagnostic)]
-#[error("Built-in criteria '{criteria_name}' cannot be re-mapped when importing")]
-#[diagnostic(help("built-in criteria are always imported unmodified (see issue #425)"))]
-pub struct BadBuiltInCriteriaMappingError {
-    #[source_code]
-    pub source_code: SourceFile,
-    #[label]
-    pub span: SourceSpan,
-    pub criteria_name: CriteriaName,
-}
-
 //////////////////////////////////////////////////////////
 // CacheErrors
 /////////////////////////////////////////////////////////
diff --git a/src/format.rs b/src/format.rs
index af42332..391d0d7 100644
--- a/src/format.rs
+++ b/src/format.rs
@@ -665,24 +665,30 @@ pub struct PolicyEntry {
     ///
     /// Any dependency edge that isn't explicitly specified defaults to `criteria`.
     #[serde(rename = "dependency-criteria")]
-    #[serde(skip_serializing_if = "DependencyCriteria::is_empty")]
-    #[serde(with = "serialization::dependency_criteria")]
+    #[serde(skip_serializing_if = "CriteriaMap::is_empty")]
+    #[serde(with = "serialization::criteria_map")]
     #[serde(default)]
-    pub dependency_criteria: DependencyCriteria,
+    pub dependency_criteria: CriteriaMap,
 
     /// Freeform notes
     pub notes: Option<String>,
 }
 
-/// A list of criteria that transitive dependencies must satisfy for this
-/// policy to be satisfied.
+/// Helper type for managing a mapping from a string to a set of criteria. This
+/// is used for dependency-criteria to specify the criteria that transitive
+/// dependencies must satisfy, as well as for criteria-maps when specifying the
+/// criteria implied by foreign criteria.
 ///
 /// Example:
 ///
 /// ```toml
 /// dependency_criteria = { hmac = ['secure', 'crypto_reviewed'] }
 /// ```
-pub type DependencyCriteria = SortedMap<PackageName, Vec<Spanned<CriteriaName>>>;
+///
+/// ```toml
+/// criteria-map = { fuzzed = 'safe-to-deploy' }
+/// ```
+pub type CriteriaMap = SortedMap<Spanned<String>, Vec<Spanned<CriteriaName>>>;
 
 pub static DEFAULT_POLICY_CRITERIA: CriteriaStr = SAFE_TO_DEPLOY;
 pub static DEFAULT_POLICY_DEV_CRITERIA: CriteriaStr = SAFE_TO_RUN;
@@ -698,9 +704,10 @@ pub struct RemoteImport {
     pub exclude: Vec<PackageName>,
     /// A list of criteria that are implied by foreign criteria
     #[serde(rename = "criteria-map")]
+    #[serde(skip_serializing_if = "CriteriaMap::is_empty")]
+    #[serde(with = "serialization::criteria_map")]
     #[serde(default)]
-    #[serde(skip_serializing_if = "Vec::is_empty")]
-    pub criteria_map: Vec<CriteriaMapping>,
+    pub criteria_map: CriteriaMap,
 }
 
 /// Translations of foreign criteria to local criteria.
diff --git a/src/main.rs b/src/main.rs
index 9219766..9a9dfc2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -32,6 +32,7 @@ use thiserror::Error;
 use tracing::{error, info, trace, warn};
 
 use crate::cli::*;
+use crate::criteria::CriteriaMapper;
 use crate::errors::{CommandError, DownloadError, FetchAndDiffError, FetchError, SourceFile};
 use crate::format::{
     AuditEntry, AuditKind, AuditsFile, ConfigFile, CratesUserId, CriteriaEntry, ExemptedDependency,
@@ -40,10 +41,11 @@ use crate::format::{
 };
 use crate::git_tool::Pager;
 use crate::out::{indeterminate_spinner, Out, StderrLogWriter, MULTIPROGRESS};
-use crate::resolver::{CriteriaMapper, CriteriaNamespace, DepGraph};
+use crate::resolver::DepGraph;
 use crate::storage::{Cache, Store};
 
 mod cli;
+mod criteria;
 pub mod errors;
 mod flock;
 pub mod format;
@@ -796,11 +798,7 @@ fn do_cmd_certify(
         )
     };
 
-    let criteria_mapper = CriteriaMapper::new(
-        &store.audits.criteria,
-        store.imported_audits(),
-        &store.config.imports,
-    );
+    let criteria_mapper = CriteriaMapper::new(&store.audits.criteria);
 
     let criteria_names = if sub_args.criteria.is_empty() {
         // If we don't have explicit cli criteria, guess the criteria
@@ -835,38 +833,23 @@ fn do_cmd_certify(
             );
             writeln!(out, "  0. <clear selections>");
             let implied_criteria = criteria_mapper.criteria_from_list(&chosen_criteria);
-            // Iterate over all the local criteria. Note that it's fine for us to do the enumerate
-            // first, because local criteria are added to the list before foreign criteria, so they
-            // should be contiguous from 0..N.
-            let local_criteria = criteria_mapper
-                .list
-                .iter()
-                .enumerate()
-                .filter(|(_, info)| matches!(info.namespace, CriteriaNamespace::Local));
-            for (criteria_idx, criteria_info) in local_criteria {
-                if chosen_criteria.contains(&criteria_info.namespaced_name) {
+            for (criteria_idx, criteria_name) in criteria_mapper.all_criteria_names().enumerate() {
+                if chosen_criteria.iter().any(|s| s == criteria_name) {
                     writeln!(
                         out,
                         "  {}. {}",
                         criteria_idx + 1,
-                        out.style().green().apply_to(&criteria_info.namespaced_name)
+                        out.style().green().apply_to(criteria_name)
                     );
                 } else if implied_criteria.has_criteria(criteria_idx) {
                     writeln!(
                         out,
                         "  {}. {}",
                         criteria_idx + 1,
-                        out.style()
-                            .yellow()
-                            .apply_to(&criteria_info.namespaced_name)
+                        out.style().yellow().apply_to(criteria_name)
                     );
                 } else {
-                    writeln!(
-                        out,
-                        "  {}. {}",
-                        criteria_idx + 1,
-                        &criteria_info.namespaced_name
-                    );
+                    writeln!(out, "  {}. {}", criteria_idx + 1, criteria_name);
                 }
             }
 
@@ -901,12 +884,12 @@ fn do_cmd_certify(
                 chosen_criteria.clear();
                 continue;
             }
-            if answer > criteria_mapper.list.len() {
+            if answer > criteria_mapper.len() {
                 // ERRORS: immediate error print to output for feedback, non-fatal
                 writeln!(out, "error: not a valid criteria");
                 continue;
             }
-            chosen_criteria.push(criteria_mapper.list[answer - 1].namespaced_name.clone());
+            chosen_criteria.push(criteria_mapper.criteria_name(answer - 1).to_owned());
         }
         chosen_criteria
     } else {
diff --git a/src/resolver.rs b/src/resolver.rs
index ac79dab..7168269 100644
--- a/src/resolver.rs
+++ b/src/resolver.rs
@@ -46,19 +46,18 @@ use miette::IntoDiagnostic;
 use serde::{Deserialize, Serialize};
 use std::cmp::Reverse;
 use std::collections::BinaryHeap;
-use std::fmt;
 use std::sync::Arc;
 use tracing::{error, trace, trace_span};
 
 use crate::cli::{DumpGraphArgs, GraphFilter, GraphFilterProperty, GraphFilterQuery};
+use crate::criteria::{CriteriaMapper, CriteriaSet};
 use crate::errors::SuggestError;
 use crate::format::{
-    self, AuditEntry, AuditKind, AuditsFile, CratesPublisher, CriteriaEntry, CriteriaName,
-    CriteriaStr, Delta, DiffStat, ExemptedDependency, FastMap, FastSet, ImportName, ImportsFile,
-    JsonPackage, JsonReport, JsonReportConclusion, JsonReportFailForVet,
-    JsonReportFailForViolationConflict, JsonReportSuccess, JsonSuggest, JsonSuggestItem,
-    JsonVetFailure, PackageName, PackageStr, Policy, RemoteImport, VetVersion, WildcardEntry,
-    SAFE_TO_DEPLOY, SAFE_TO_RUN,
+    self, AuditEntry, AuditKind, AuditsFile, CratesPublisher, CriteriaName, Delta, DiffStat,
+    ExemptedDependency, FastMap, FastSet, ImportName, ImportsFile, JsonPackage, JsonReport,
+    JsonReportConclusion, JsonReportFailForVet, JsonReportFailForViolationConflict,
+    JsonReportSuccess, JsonSuggest, JsonSuggestItem, JsonVetFailure, PackageName, PackageStr,
+    Policy, VetVersion, WildcardEntry,
 };
 use crate::format::{SortedMap, SortedSet};
 use crate::network::Network;
@@ -78,7 +77,7 @@ pub struct ResolveReport<'a> {
     /// Low-level results for each package's individual criteria resolving
     /// analysis, indexed by [`PackageIdx`][]. Will be `None` for first-party
     /// crates or crates with violation conflicts.
-    pub results: Vec<Option<ResolveResult<'a>>>,
+    pub results: Vec<Option<ResolveResult>>,
 
     /// The final conclusion of our analysis.
     pub conclusion: Conclusion,
@@ -113,18 +112,20 @@ pub struct FailForVet {
     pub suggest: Option<Suggest>,
 }
 
+// FIXME: This format is pretty janky and unstable, so we probably should come
+// up with an actually-useful format for this.
 #[allow(clippy::large_enum_variant)]
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub enum ViolationConflict {
     UnauditedConflict {
-        violation_source: CriteriaNamespace,
+        violation_source: Option<ImportName>,
         violation: AuditEntry,
         exemptions: ExemptedDependency,
     },
     AuditConflict {
-        violation_source: CriteriaNamespace,
+        violation_source: Option<ImportName>,
         violation: AuditEntry,
-        audit_source: CriteriaNamespace,
+        audit_source: Option<ImportName>,
         audit: AuditEntry,
     },
 }
@@ -151,73 +152,6 @@ pub struct DiffRecommendation {
     pub diffstat: DiffStat,
 }
 
-/// Set of booleans, 64 should be Enough For Anyone (but abstracting in case not).
-///
-/// Note that this intentionally doesn't implement Default to allow the implementation
-/// to require the CriteriaMapper to provide the count of items at construction time.
-/// Which will be useful if we ever decide to give it ~infinite capacity and wrap
-/// a BitSet.
-#[derive(Clone)]
-pub struct CriteriaSet(u64);
-const MAX_CRITERIA: usize = u64::BITS as usize; // funnier this way
-
-/// Set of criteria which failed for a given package. `confident` contains only
-/// criteria which we're confident about, whereas `all` contains all criteria.
-#[derive(Debug, Clone)]
-pub struct CriteriaFailureSet {
-    pub confident: CriteriaSet,
-    pub all: CriteriaSet,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
-pub enum CriteriaNamespace {
-    Local,
-    Foreign(ImportName),
-}
-
-/// Misc info about a Criteria that CriteriaMapper wants for some tasks.
-#[derive(Debug, Clone)]
-pub struct CriteriaInfo {
-    /// The namespace this criteria is natively part of.
-    ///
-    /// Note that builtins are in [`CriteriaNamespace::Local`] but still resolve
-    /// in foreign namespaces. This is effectively done by having a copy of those
-    /// criteria in every namespace that is auto-mapped into local. Because mapping
-    /// is bijective, the foreign copies are pointless and everyone just uses the
-    /// same local instance happily.
-    ///
-    /// The automapping is resolved by [`CriteriaMapper::index`].
-    pub namespace: CriteriaNamespace,
-    /// The name of the criteria with namespacing modifiers applied.
-    ///
-    /// e.g. a local criteria will show up as `some-criteria` but a foreign
-    /// one will appear as `foreign::some-criteria`. This string is intended
-    /// for user-facing messages to avoid ambiguities (and make it easier to
-    /// tell if we mess up and leak a foreign criteria where it shouldn't be).
-    pub namespaced_name: String,
-
-    /// The raw name of the criteria, as it would appear if it was local.
-    ///
-    /// FIXME: arguably we don't need to hold onto this, but it's annoying
-    /// to factor out and the overhead is completely trivial.
-    raw_name: CriteriaName,
-    /// FIXME: we don't actually need/want to store this but it's annoying
-    /// to factor out and honestly the overhead is completely trivial.
-    implies: Vec<CriteriaName>,
-}
-
-/// A processed version of config.toml's criteria definitions, for mapping
-/// lists of criteria names to CriteriaSets.
-#[derive(Debug, Clone)]
-pub struct CriteriaMapper {
-    /// All the criteria in their raw form
-    pub list: Vec<CriteriaInfo>,
-    /// name -> index in all lists
-    pub index: FastMap<CriteriaNamespace, FastMap<CriteriaName, usize>>,
-    /// The transitive closure of all criteria implied by each criteria (including self)
-    pub implied_criteria: Vec<CriteriaSet>,
-}
-
 /// An "interned" cargo PackageId which is used to uniquely identify packages throughout
 /// the code. This is simpler and faster than actually using PackageIds (strings) or name+version.
 /// In the current implementation it can be used to directly index into the `graph` or `results`.
@@ -271,9 +205,7 @@ pub struct DepGraph<'a> {
 
 /// Results and notes from running vet on a particular package.
 #[derive(Debug, Clone)]
-pub struct ResolveResult<'a> {
-    /// Graph used to compute audits for this package.
-    pub audit_graph: AuditGraph<'a>,
+pub struct ResolveResult {
     /// Cache of search results for each criteria.
     pub search_results: Vec<Result<Vec<DeltaEdgeOrigin>, SearchFailure>>,
 }
@@ -404,319 +336,6 @@ pub enum SearchMode {
     RegenerateExemptions,
 }
 
-const NUM_BUILTINS: usize = 2;
-fn builtin_criteria() -> [CriteriaInfo; NUM_BUILTINS] {
-    [
-        CriteriaInfo {
-            namespace: CriteriaNamespace::Local,
-            raw_name: SAFE_TO_RUN.to_string(),
-            namespaced_name: SAFE_TO_RUN.to_string(),
-            implies: vec![],
-        },
-        CriteriaInfo {
-            namespace: CriteriaNamespace::Local,
-            raw_name: SAFE_TO_DEPLOY.to_string(),
-            namespaced_name: SAFE_TO_DEPLOY.to_string(),
-            implies: vec!["safe-to-run".to_string()],
-        },
-    ]
-}
-
-impl CriteriaMapper {
-    pub fn new(
-        criteria: &SortedMap<CriteriaName, CriteriaEntry>,
-        imports: &SortedMap<ImportName, AuditsFile>,
-        mappings: &SortedMap<ImportName, RemoteImport>,
-    ) -> CriteriaMapper {
-        // First, build a list of all our criteria
-        let locals = criteria.iter().map(|(k, v)| CriteriaInfo {
-            namespace: CriteriaNamespace::Local,
-            raw_name: k.clone(),
-            namespaced_name: k.clone(),
-            implies: v.implies.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
-        });
-        let builtins = builtin_criteria();
-        let foreigns = imports.iter().flat_map(|(import, audit_file)| {
-            audit_file.criteria.iter().map(move |(k, v)| CriteriaInfo {
-                namespace: CriteriaNamespace::Foreign(import.clone()),
-                raw_name: k.clone(),
-                namespaced_name: format!("{import}::{k}"),
-                implies: v.implies.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
-            })
-        });
-        let list = builtins
-            .into_iter()
-            .chain(locals)
-            .chain(foreigns)
-            .collect::<Vec<_>>();
-        let num_criteria = list.len();
-
-        // Now construct an index over this list, that lets us map
-        // (CriteriaNamespace, CriteriaName) => CriteriaIdx
-        let mut index = FastMap::<CriteriaNamespace, FastMap<CriteriaName, usize>>::new();
-        let all_import_names = mappings.iter().map(|(import_name, _)| import_name);
-
-        // Add all the natural entries
-        for (idx, info) in list.iter().enumerate() {
-            let prev = index
-                .entry(info.namespace.clone())
-                .or_default()
-                .insert(info.raw_name.clone(), idx);
-            assert!(prev.is_none(), "criteria name was multiply defined???");
-        }
-
-        // Hack the builtins into every foreign namespace. Because they all use
-        // the same CriteriaIdx, we can now forget that builtins are special and
-        // just naturally look them up in any namespace without issue.
-        for import_name in all_import_names {
-            for (idx, info) in list[0..NUM_BUILTINS].iter().enumerate() {
-                index
-                    .entry(CriteriaNamespace::Foreign(import_name.clone()))
-                    .or_default()
-                    .insert(info.raw_name.clone(), idx);
-            }
-        }
-
-        // Compute the graph of "implies" relationships. We will then run DFS from each
-        // node to compute the transitive closure of each criteria's implications, which
-        // will becomes `implies[idx]`, the value used whenever the criteria is named.
-        let mut direct_implies = FastMap::<usize, CriteriaSet>::with_capacity(num_criteria);
-        // Add all the edges for implies entries (and ensure there's a node for every idx)
-        for (idx, info) in list.iter().enumerate() {
-            let mut edges = CriteriaSet::none(num_criteria);
-            for implied in &info.implies {
-                let their_idx = index[&info.namespace][&**implied];
-                edges.set_criteria(their_idx);
-            }
-            direct_implies.insert(idx, edges);
-        }
-
-        // Add all the edges for foreign mappings
-        //
-        // FIXME: in principle these foreign criteria can be completely eliminated
-        // because they're 100% redundant with the local criteria they're getting
-        // mapped to. However this results in us discarding some information that
-        // conceivably could be useful for some diagnostics or other analysis.
-        //
-        // For now let's leave them in with an eye towards eliminating them.
-        // We currently handle eliminating their redundant nature in a more "late-bound"
-        // way in `CriteriaMapper::minimal_indices` and the various APIs that are
-        // built on top of it (all the *_criteria_names APIs, which govern ~all output).
-        for (import_name, mappings) in mappings
-            .iter()
-            .map(|(name, entry)| (name, &entry.criteria_map))
-        {
-            let foreign = CriteriaNamespace::Foreign(import_name.clone());
-            let local = CriteriaNamespace::Local;
-            for mapping in mappings {
-                // Add a bidirectional edge between these two criteria (they are now completely equivalent)
-                let our_idx = index[&local][&mapping.ours];
-                let their_idx = index[&foreign][&*mapping.theirs];
-                direct_implies
-                    .get_mut(&our_idx)
-                    .unwrap()
-                    .set_criteria(their_idx);
-                direct_implies
-                    .get_mut(&their_idx)
-                    .unwrap()
-                    .set_criteria(our_idx);
-            }
-        }
-
-        // Now do DFS over the direct_implies graph to compute the true transitive implies closure
-        let mut implied_criteria = Vec::with_capacity(num_criteria);
-        for idx in 0..num_criteria {
-            let mut implied = CriteriaSet::none(num_criteria);
-            implied.set_criteria(idx);
-            recursive_implies(&mut implied, &direct_implies, idx);
-            implied_criteria.push(implied);
-
-            fn recursive_implies(
-                result: &mut CriteriaSet,
-                direct_implies: &FastMap<usize, CriteriaSet>,
-                cur_idx: usize,
-            ) {
-                for implied_idx in direct_implies[&cur_idx].indices() {
-                    if result.has_criteria(implied_idx) {
-                        // If we've already visited this criteria, don't do it again.
-                        // This resolves all cycles (such as foreign mappings).
-                        continue;
-                    }
-                    result.set_criteria(implied_idx);
-
-                    // FIXME: we should detect infinite implies loops?
-                    recursive_implies(result, direct_implies, implied_idx);
-                }
-            }
-        }
-
-        Self {
-            list,
-            index,
-            implied_criteria,
-        }
-    }
-    pub fn criteria_from_entry(&self, entry: &AuditEntry) -> CriteriaSet {
-        self.criteria_from_namespaced_entry(&CriteriaNamespace::Local, entry)
-    }
-    pub fn criteria_from_namespaced_entry(
-        &self,
-        namespace: &CriteriaNamespace,
-        entry: &AuditEntry,
-    ) -> CriteriaSet {
-        self.criteria_from_namespaced_list(namespace, &entry.criteria)
-    }
-    pub fn criteria_from_list<'b, S: AsRef<str> + 'b + ?Sized>(
-        &self,
-        list: impl IntoIterator<Item = &'b S>,
-    ) -> CriteriaSet {
-        self.criteria_from_namespaced_list(&CriteriaNamespace::Local, list)
-    }
-    pub fn criteria_from_namespaced_list<'b, S: AsRef<str> + 'b + ?Sized>(
-        &self,
-        namespace: &CriteriaNamespace,
-        list: impl IntoIterator<Item = &'b S>,
-    ) -> CriteriaSet {
-        let mut result = self.no_criteria();
-        for criteria in list {
-            let idx = self.index[namespace][criteria.as_ref()];
-            result.unioned_with(&self.implied_criteria[idx]);
-        }
-        result
-    }
-    pub fn set_criteria(&self, set: &mut CriteriaSet, criteria: CriteriaStr) {
-        self.set_namespaced_criteria(set, &CriteriaNamespace::Local, criteria);
-    }
-    pub fn set_namespaced_criteria(
-        &self,
-        set: &mut CriteriaSet,
-        namespace: &CriteriaNamespace,
-        criteria: CriteriaStr,
-    ) {
-        set.unioned_with(&self.implied_criteria[self.index[namespace][criteria]])
-    }
-    /// An iterator over every criteria in order, with 'implies' fully applied.
-    ///
-    /// This includes any foreign criteria that has been eliminated as redundant.
-    pub fn all_criteria_iter(&self) -> impl Iterator<Item = &CriteriaSet> {
-        self.implied_criteria.iter()
-    }
-    /// An iterator over every **local** criteria in order, with 'implies' fully applied.
-    pub fn all_local_criteria_iter(&self) -> impl Iterator<Item = &CriteriaSet> {
-        // Just filter out the non-local criteria
-        self.implied_criteria
-            .iter()
-            .enumerate()
-            .filter(|(idx, _)| matches!(self.list[*idx].namespace, CriteriaNamespace::Local))
-            .map(|(_, set)| set)
-    }
-    pub fn len(&self) -> usize {
-        self.list.len()
-    }
-    pub fn is_empty(&self) -> bool {
-        self.list.is_empty()
-    }
-    pub fn no_criteria(&self) -> CriteriaSet {
-        CriteriaSet::none(self.len())
-    }
-    pub fn all_criteria(&self) -> CriteriaSet {
-        CriteriaSet::_all(self.len())
-    }
-
-    /// Like [`CriteriaSet::indices`] but uses knowledge of things like
-    /// `implies` relationships to remove redundant information. For
-    /// instance, if safe-to-deploy is set, we don't also yield safe-to-run.
-    pub fn minimal_indices<'a>(
-        &'a self,
-        criteria: &'a CriteriaSet,
-    ) -> impl Iterator<Item = usize> + 'a {
-        criteria.indices().filter(|&cur_idx| {
-            criteria.indices().all(|other_idx| {
-                // Ignore our own index
-                let is_identity = cur_idx == other_idx;
-                // Discard this criteria if it's implied by another
-                let isnt_implied = !self.implied_criteria[other_idx].has_criteria(cur_idx);
-                // Unless we're local and they're foreign, then we win
-                let cur_is_local = self.list[cur_idx].namespace == CriteriaNamespace::Local;
-                let other_is_foreign = self.list[other_idx].namespace != CriteriaNamespace::Local;
-                let is_mapping = cur_is_local && other_is_foreign;
-                is_identity || isnt_implied || is_mapping
-            })
-        })
-    }
-
-    /// Yields all the names of the set criteria with implied members filtered out.
-    pub fn criteria_names<'a>(
-        &'a self,
-        criteria: &'a CriteriaSet,
-    ) -> impl Iterator<Item = CriteriaStr<'a>> + 'a {
-        self.minimal_indices(criteria)
-            .map(|idx| &*self.list[idx].namespaced_name)
-    }
-}
-
-impl CriteriaSet {
-    pub fn none(count: usize) -> Self {
-        assert!(
-            count <= MAX_CRITERIA,
-            "{MAX_CRITERIA} was not Enough For Everyone ({count} criteria)"
-        );
-        CriteriaSet(0)
-    }
-    pub fn _all(count: usize) -> Self {
-        assert!(
-            count <= MAX_CRITERIA,
-            "{MAX_CRITERIA} was not Enough For Everyone ({count} criteria)"
-        );
-        if count == MAX_CRITERIA {
-            CriteriaSet(!0)
-        } else {
-            // Bit Magic to get 'count' 1's
-            CriteriaSet((1 << count) - 1)
-        }
-    }
-    pub fn set_criteria(&mut self, idx: usize) {
-        self.0 |= 1 << idx;
-    }
-    pub fn clear_criteria(&mut self, other: &CriteriaSet) {
-        self.0 &= !other.0;
-    }
-    pub fn has_criteria(&self, idx: usize) -> bool {
-        (self.0 & (1 << idx)) != 0
-    }
-    pub fn _intersected_with(&mut self, other: &CriteriaSet) {
-        self.0 &= other.0;
-    }
-    pub fn unioned_with(&mut self, other: &CriteriaSet) {
-        self.0 |= other.0;
-    }
-    pub fn contains(&self, other: &CriteriaSet) -> bool {
-        (self.0 & other.0) == other.0
-    }
-    pub fn is_empty(&self) -> bool {
-        self.0 == 0
-    }
-    pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
-        // Yield all the offsets that are set by repeatedly getting the lowest 1 and clearing it
-        let mut raw = self.0;
-        std::iter::from_fn(move || {
-            if raw == 0 {
-                None
-            } else {
-                let next = raw.trailing_zeros() as usize;
-                raw &= !(1 << next);
-                Some(next)
-            }
-        })
-    }
-}
-
-impl fmt::Debug for CriteriaSet {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "{:08b}", self.0)
-    }
-}
-
 impl<'a> DepGraph<'a> {
     pub fn new(
         metadata: &'a Metadata,
@@ -1181,7 +800,7 @@ impl<'a> DepGraph<'a> {
 pub fn resolve<'a>(
     metadata: &'a Metadata,
     filter_graph: Option<&Vec<GraphFilter>>,
-    store: &'a Store,
+    store: &Store,
 ) -> ResolveReport<'a> {
     // A large part of our algorithm is unioning and intersecting criteria, so we map all
     // the criteria into indexed boolean sets (*whispers* an integer with lots of bits).
@@ -1189,11 +808,7 @@ pub fn resolve<'a>(
     // trace!("built DepGraph: {:#?}", graph);
     trace!("built DepGraph!");
 
-    let criteria_mapper = CriteriaMapper::new(
-        &store.audits.criteria,
-        store.imported_audits(),
-        &store.config.imports,
-    );
+    let criteria_mapper = CriteriaMapper::new(&store.audits.criteria);
     trace!("built CriteriaMapper!");
 
     let requirements = resolve_requirements(&graph, &store.config.policy, &criteria_mapper);
@@ -1274,12 +889,12 @@ fn resolve_requirements(
     requirements
 }
 
-fn resolve_audits<'a>(
+fn resolve_audits(
     graph: &DepGraph<'_>,
-    store: &'a Store,
+    store: &Store,
     criteria_mapper: &CriteriaMapper,
     requirements: &[CriteriaSet],
-) -> (Vec<Option<ResolveResult<'a>>>, Conclusion) {
+) -> (Vec<Option<ResolveResult>>, Conclusion) {
     let _resolve_audits = trace_span!("resolve_audits").entered();
     let mut violations = Vec::new();
     let mut failures = Vec::new();
@@ -1339,10 +954,7 @@ fn resolve_audits<'a>(
                 vetted_partially.push(pkgidx);
             }
 
-            Some(ResolveResult {
-                audit_graph,
-                search_results,
-            })
+            Some(ResolveResult { search_results })
         })
         .collect();
 
@@ -1375,10 +987,10 @@ impl<'a> AuditGraph<'a> {
     ) -> Result<Self, Vec<ViolationConflict>> {
         // Pre-build the namespaces for each audit so that we can take a reference
         // to each one as-needed rather than cloning the name each time.
-        let foreign_namespaces: Vec<CriteriaNamespace> = store
+        let foreign_namespaces: Vec<Option<ImportName>> = store
             .imported_audits()
             .keys()
-            .map(|import_name| CriteriaNamespace::Foreign(import_name.clone()))
+            .map(|import_name| Some(import_name.clone()))
             .collect();
 
         // Iterator over every audits file, including imported audits.
@@ -1393,7 +1005,7 @@ impl<'a> AuditGraph<'a> {
                     audits_file,
                 )
             })
-            .chain([(None, &CriteriaNamespace::Local, &store.audits)]);
+            .chain([(None, &None, &store.audits)]);
 
         // Iterator over every normal audit.
         let all_audits =
@@ -1463,7 +1075,7 @@ impl<'a> AuditGraph<'a> {
                 }
             };
 
-            let criteria = criteria_mapper.criteria_from_namespaced_entry(namespace, entry);
+            let criteria = criteria_mapper.criteria_from_list(&entry.criteria);
 
             forward_audits.entry(from_ver).or_default().push(DeltaEdge {
                 version: Some(to_ver),
@@ -1486,15 +1098,14 @@ impl<'a> AuditGraph<'a> {
         // wildcard audits apply and add full-audits to those versions if they
         // do.
         for (publisher_index, publisher) in publishers.iter().enumerate() {
-            for (namespace, import_index, audit_index, entry) in all_wildcard_audits.clone() {
+            for (_, import_index, audit_index, entry) in all_wildcard_audits.clone() {
                 if entry.user_id == publisher.user_id
                     && *entry.start <= publisher.when
                     && publisher.when < *entry.end
                 {
                     let from_ver = None;
                     let to_ver = Some(&publisher.version);
-                    let criteria =
-                        criteria_mapper.criteria_from_namespaced_list(namespace, &entry.criteria);
+                    let criteria = criteria_mapper.criteria_from_list(&entry.criteria);
                     let origin = DeltaEdgeOrigin::WildcardAudit {
                         import_index,
                         audit_index,
@@ -1567,7 +1178,7 @@ impl<'a> AuditGraph<'a> {
             let violation_criterias = violation_entry
                 .criteria
                 .iter()
-                .map(|c| criteria_mapper.criteria_from_namespaced_list(violation_source, [&c]))
+                .map(|c| criteria_mapper.criteria_from_list([&c]))
                 .collect::<Vec<_>>();
             let violation_range = if let AuditKind::Violation { violation } = &violation_entry.kind
             {
@@ -1598,8 +1209,7 @@ impl<'a> AuditGraph<'a> {
 
             // Note if this entry conflicts with any audits
             for (namespace, _origin, audit) in all_audits.clone() {
-                let audit_criteria =
-                    criteria_mapper.criteria_from_namespaced_entry(namespace, audit);
+                let audit_criteria = criteria_mapper.criteria_from_list(&audit.criteria);
                 let has_violation = violation_criterias
                     .iter()
                     .any(|v| audit_criteria.contains(v));
@@ -2512,12 +2122,12 @@ impl FailForViolationConflict {
 
         fn print_entry(
             out: &Arc<dyn Out>,
-            source: &CriteriaNamespace,
+            source: &Option<ImportName>,
             entry: &AuditEntry,
         ) -> Result<(), std::io::Error> {
             match source {
-                CriteriaNamespace::Local => write!(out, "own "),
-                CriteriaNamespace::Foreign(name) => write!(out, "foreign ({name}) "),
+                None => write!(out, "own "),
+                Some(name) => write!(out, "foreign ({name}) "),
             }
             match &entry.kind {
                 AuditKind::Full { version, .. } => {
@@ -2682,11 +2292,7 @@ pub(crate) fn get_store_updates(
         cfg.cli.filter_graph.as_ref(),
         Some(&store.config.policy),
     );
-    let criteria_mapper = CriteriaMapper::new(
-        &store.audits.criteria,
-        store.imported_audits(),
-        &store.config.imports,
-    );
+    let criteria_mapper = CriteriaMapper::new(&store.audits.criteria);
     let requirements = resolve_requirements(&graph, &store.config.policy, &criteria_mapper);
 
     let mut required_entries = SortedMap::new();
diff --git a/src/serialization.rs b/src/serialization.rs
index a75fd32..e0bb75a 100644
--- a/src/serialization.rs
+++ b/src/serialization.rs
@@ -1,6 +1,6 @@
 //! Serialization helpers
 
-use crate::format::{DependencyCriteria, SortedMap};
+use crate::format::{CriteriaMap, SortedMap};
 use core::fmt;
 use serde::{
     de::{self, value, SeqAccess, Visitor},
@@ -152,31 +152,31 @@ pub mod string_or_vec_or_none {
     }
 }
 
-/// Allows the `Vec<String>` map value in dependency-criteria to support
-/// `string_or_vec` semantics.
-pub mod dependency_criteria {
-    use crate::format::{CriteriaName, PackageName};
+/// Allows the `Vec<String>` map value in dependency-criteria or criteria-map to
+/// support `string_or_vec` semantics.
+pub mod criteria_map {
+    use crate::format::CriteriaName;
 
     use super::*;
     #[derive(Serialize, Deserialize)]
     struct Wrapper(#[serde(with = "string_or_vec")] Vec<Spanned<CriteriaName>>);
 
-    pub fn serialize<S>(c: &DependencyCriteria, s: S) -> Result<S::Ok, S::Error>
+    pub fn serialize<S>(c: &CriteriaMap, s: S) -> Result<S::Ok, S::Error>
     where
         S: Serializer,
     {
-        let m: SortedMap<PackageName, Wrapper> = c
+        let m: SortedMap<Spanned<String>, Wrapper> = c
             .iter()
             .map(|(k, v)| (k.clone(), Wrapper(v.clone())))
             .collect();
         m.serialize(s)
     }
 
-    pub fn deserialize<'de, D>(deserializer: D) -> Result<DependencyCriteria, D::Error>
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<CriteriaMap, D::Error>
     where
         D: Deserializer<'de>,
     {
-        let m = SortedMap::<PackageName, Wrapper>::deserialize(deserializer)?;
+        let m = SortedMap::<Spanned<String>, Wrapper>::deserialize(deserializer)?;
         Ok(m.into_iter().map(|(k, v)| (k, v.0)).collect())
     }
 }
@@ -597,6 +597,12 @@ pub mod spanned {
         }
     }
 
+    impl<T> Borrow<T> for Spanned<T> {
+        fn borrow(&self) -> &T {
+            self
+        }
+    }
+
     impl<T, U: ?Sized> AsRef<U> for Spanned<T>
     where
         T: AsRef<U>,
@@ -695,14 +701,14 @@ mod test {
     fn toml_formatter_wrapping() {
         let mut dc_long = SortedMap::new();
         dc_long.insert(
-            "example-crate-1".to_owned(),
+            "example-crate-1".to_owned().into(),
             vec![
                 "criteria-one-very-long".to_owned().into(),
                 "criteria-two-very-long".to_owned().into(),
             ],
         );
         dc_long.insert(
-            "example-crate-2".to_owned(),
+            "example-crate-2".to_owned().into(),
             vec![
                 // This array would wrap over multiple lines if byte length was
                 // used rather than utf-8 character length.
@@ -711,7 +717,7 @@ mod test {
             ],
         );
         dc_long.insert(
-            "example-crate-3".to_owned(),
+            "example-crate-3".to_owned().into(),
             vec![
                 "criteria-one-very-long".to_owned().into(),
                 "criteria-two-very-long".to_owned().into(),
@@ -723,7 +729,7 @@ mod test {
 
         let mut dc_short = SortedMap::new();
         dc_short.insert(
-            "example-crate-1".to_owned(),
+            "example-crate-1".to_owned().into(),
             vec!["criteria-one".to_owned().into()],
         );
 
diff --git a/src/storage.rs b/src/storage.rs
index 5cddaa1..b2ec77d 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -19,13 +19,14 @@ use tar::Archive;
 use tracing::{error, info, log::warn, trace};
 
 use crate::{
+    criteria::CriteriaMapper,
     errors::{
-        BadBuiltInCriteriaMappingError, BadFormatError, BadWildcardEndDateError, CacheAcquireError,
-        CacheCommitError, CertifyError, CommandError, CriteriaChangeError, CriteriaChangeErrors,
-        DiffError, DownloadError, FetchAndDiffError, FetchAuditError, FetchError, FlockError,
-        InvalidCriteriaError, JsonParseError, LoadJsonError, LoadTomlError, SourceFile,
-        StoreAcquireError, StoreCommitError, StoreCreateError, StoreJsonError, StoreTomlError,
-        StoreValidateError, StoreValidateErrors, TomlParseError, UnpackCheckoutError, UnpackError,
+        BadFormatError, BadWildcardEndDateError, CacheAcquireError, CacheCommitError, CertifyError,
+        CommandError, CriteriaChangeError, CriteriaChangeErrors, DiffError, DownloadError,
+        FetchAndDiffError, FetchAuditError, FetchError, FlockError, InvalidCriteriaError,
+        JsonParseError, LoadJsonError, LoadTomlError, SourceFile, StoreAcquireError,
+        StoreCommitError, StoreCreateError, StoreJsonError, StoreTomlError, StoreValidateError,
+        StoreValidateErrors, TomlParseError, UnpackCheckoutError, UnpackError,
     },
     flock::{FileLock, Filesystem},
     format::{
@@ -222,8 +223,13 @@ impl Store {
         let live_imports = if let (false, Some(network)) = (cfg.cli.locked, network) {
             let fetched_audits = tokio::runtime::Handle::current()
                 .block_on(fetch_imported_audits(network, &config))?;
-            let mut live_imports =
-                process_imported_audits(fetched_audits, &config, &imports, allow_criteria_changes)?;
+            let mut live_imports = process_imported_audits(
+                fetched_audits,
+                &audits,
+                &config,
+                &imports,
+                allow_criteria_changes,
+            )?;
             // XXX: Allow callers to hold on to this cache so we don't need to
             // re-acquire it after `Store::acquire`.
             let cache = Cache::acquire(cfg).map_err(Box::new)?;
@@ -294,8 +300,13 @@ impl Store {
     ) -> Result<Self, StoreAcquireError> {
         let fetched_audits =
             tokio::runtime::Handle::current().block_on(fetch_imported_audits(network, &config))?;
-        let mut live_imports =
-            process_imported_audits(fetched_audits, &config, &imports, allow_criteria_changes)?;
+        let mut live_imports = process_imported_audits(
+            fetched_audits,
+            &audits,
+            &config,
+            &imports,
+            allow_criteria_changes,
+        )?;
         let cache = Cache::acquire(cfg).map_err(Box::new)?;
         import_publisher_versions(
             &cfg.metadata,
@@ -571,20 +582,6 @@ impl Store {
             }
         }
 
-        for (_name, import) in &self.config.imports {
-            for mapping in &import.criteria_map {
-                if &mapping.theirs[..] == SAFE_TO_DEPLOY || &mapping.theirs[..] == SAFE_TO_RUN {
-                    errors.push(StoreValidateError::BadBuiltInCriteriaMapping(
-                        BadBuiltInCriteriaMappingError {
-                            source_code: self.config_src.clone(),
-                            span: Spanned::span(&mapping.theirs),
-                            criteria_name: mapping.theirs[..].to_owned(),
-                        },
-                    ));
-                }
-            }
-        }
-
         // If requested, verify that files in the store are correctly formatted
         // and have no unrecognized fields. We don't want to be reformatting
         // them or dropping unused fields while in CI, as those changes will be
@@ -701,6 +698,7 @@ impl Store {
 /// description of the live state of imported audits.
 fn process_imported_audits(
     fetched_audits: Vec<(ImportName, AuditsFile)>,
+    local_audits_file: &AuditsFile,
     config_file: &ConfigFile,
     imports_lock: &ImportsFile,
     allow_criteria_changes: bool,
@@ -710,6 +708,8 @@ fn process_imported_audits(
         audits: SortedMap::new(),
     };
     let mut changed_criteria = Vec::new();
+
+    let local_criteria_mapper = CriteriaMapper::new(&local_audits_file.criteria);
     for (import_name, mut audits_file) in fetched_audits {
         let config = config_file
             .imports
@@ -722,9 +722,48 @@ fn process_imported_audits(
             audits_file.audits.remove(excluded);
         }
 
+        // Construct a mapping from the foreign criteria namespace into the
+        // local criteria namespace based on the criteria map from the config.
+        let foreign_criteria_mapper = CriteriaMapper::new(&audits_file.criteria);
+        let foreign_to_local_mapping: Vec<_> = foreign_criteria_mapper
+            .all_criteria_names()
+            .map(|foreign_name| {
+                // NOTE: We try the map before we check for built-in criteria to
+                // allow overriding the default behaviour.
+                if let Some(mapped) = config.criteria_map.get(foreign_name) {
+                    local_criteria_mapper.criteria_from_list(mapped)
+                } else if foreign_name == SAFE_TO_DEPLOY {
+                    local_criteria_mapper.criteria_from_list([SAFE_TO_DEPLOY])
+                } else if foreign_name == SAFE_TO_RUN {
+                    local_criteria_mapper.criteria_from_list([SAFE_TO_RUN])
+                } else {
+                    local_criteria_mapper.no_criteria()
+                }
+            })
+            .collect();
+
+        // Helper to re-write foreign criteria into the local criteria
+        // namespace.
+        let make_criteria_local = |criteria: &mut Vec<Spanned<CriteriaName>>| {
+            let foreign_set = foreign_criteria_mapper.criteria_from_list(&*criteria);
+            let mut local_set = local_criteria_mapper.no_criteria();
+            for foreign_criteria_idx in foreign_set.indices() {
+                local_set.unioned_with(&foreign_to_local_mapping[foreign_criteria_idx]);
+            }
+            *criteria = local_criteria_mapper
+                .criteria_names(&local_set)
+                .map(|name| name.to_owned().into())
+                .collect();
+        };
+
         // By default all audits read from the network are fresh.
+        //
+        // Note: This may leave behind useless audits which imply no criteria,
+        // but that's OK - we'll never choose to import them. In the future we
+        // might want to trim them.
         for audit_entry in audits_file.audits.values_mut().flat_map(|v| v.iter_mut()) {
             audit_entry.is_fresh_import = true;
+            make_criteria_local(&mut audit_entry.criteria);
         }
         for audit_entry in audits_file
             .wildcard_audits
@@ -732,8 +771,21 @@ fn process_imported_audits(
             .flat_map(|v| v.iter_mut())
         {
             audit_entry.is_fresh_import = true;
+            make_criteria_local(&mut audit_entry.criteria);
         }
 
+        // Now that we're done with foreign criteria, trim the set to only
+        // contain mapped criteria, as we don't care about other criteria, so
+        // shouldn't bother importing them.
+        //
+        // We also clear out description_url and implies, as those fields will
+        // not be used locally.
+        audits_file.criteria.retain(|name, entry| {
+            entry.description_url = None;
+            entry.implies = Vec::new();
+            config.criteria_map.contains_key(name)
+        });
+
         // If we have an existing audits file for these imports, compare against it.
         if let Some(existing_audits_file) = imports_lock.audits.get(&import_name) {
             if !allow_criteria_changes {


Test patch:
diff --git a/src/tests/certify.rs b/src/tests/certify.rs
index 2481cb9..334a28c 100644
--- a/src/tests/certify.rs
+++ b/src/tests/certify.rs
@@ -1,5 +1,3 @@
-use crate::network::Network;
-
 use super::*;
 use std::fmt::Write;
 
diff --git a/src/tests/crate_policies.rs b/src/tests/crate_policies.rs
index 7d4fe2b..88dc5a2 100644
--- a/src/tests/crate_policies.rs
+++ b/src/tests/crate_policies.rs
@@ -53,7 +53,7 @@ fn simple_crate_policies_third_party_crates_dont_need_versions() {
 
 fn dep_criteria_policy_entry() -> PolicyEntry {
     PolicyEntry {
-        dependency_criteria: [("foo".to_owned(), vec!["bar".to_owned().into()])].into(),
+        dependency_criteria: [("foo".to_owned().into(), vec!["bar".to_owned().into()])].into(),
         ..Default::default()
     }
 }
diff --git a/src/tests/import.rs b/src/tests/import.rs
index 21b2740..1fdb8df 100644
--- a/src/tests/import.rs
+++ b/src/tests/import.rs
@@ -1,5 +1,3 @@
-use crate::network::Network;
-
 use super::*;
 
 // Helper function for imports tests. Performs a vet and updates imports based
@@ -375,9 +373,9 @@ fn existing_peer_import_delta_audit() {
 
 #[test]
 fn existing_peer_import_custom_criteria() {
-    // (Pass) We'll immediately import criteria changes when unlocked, even if
-    // our peer hasn't changed or we aren't mapping them locally. Only the
-    // criteria will be updated.
+    // (Pass) We'll immediately import criteria changes for mapped criteria when
+    // unlocked, even if our peer hasn't changed or we aren't mapping them
+    // locally. Only the criteria will be updated.
 
     let _enter = TEST_RUNTIME.enter();
     let mock = MockMetadata::simple();
@@ -399,9 +397,15 @@ fn existing_peer_import_custom_criteria() {
     };
 
     let new_foreign_audits = AuditsFile {
-        criteria: [("fuzzed".to_string(), criteria("fuzzed"))]
-            .into_iter()
-            .collect(),
+        criteria: [
+            ("fuzzed".to_string(), criteria("fuzzed")),
+            (
+                "super-fuzzed".to_string(),
+                criteria_implies("super-fuzzed", ["fuzzed"]),
+            ),
+        ]
+        .into_iter()
+        .collect(),
         wildcard_audits: SortedMap::new(),
         audits: [(
             "third-party2".to_owned(),
@@ -422,6 +426,12 @@ fn existing_peer_import_custom_criteria() {
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
+            criteria_map: [(
+                "fuzzed".to_string().into(),
+                vec![SAFE_TO_RUN.to_string().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -441,7 +451,7 @@ fn existing_peer_import_custom_criteria() {
 #[test]
 fn new_audit_for_unused_criteria_basic() {
     // (Pass) If a peer adds an audit for an unused criteria, we shouldn't
-    // vendor in the changes unnecessarily.
+    // vendor in the changes unnecessarily, even if the criteria is mapped.
 
     let _enter = TEST_RUNTIME.enter();
     let mock = MockMetadata::simple();
@@ -479,6 +489,12 @@ fn new_audit_for_unused_criteria_basic() {
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
+            criteria_map: [(
+                "fuzzed".to_string().into(),
+                vec![SAFE_TO_RUN.to_string().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -540,6 +556,12 @@ fn new_audit_for_unused_criteria_transitive() {
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
+            criteria_map: [(
+                "fuzzed".to_string().into(),
+                vec![SAFE_TO_RUN.to_string().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -983,6 +1005,12 @@ fn existing_peer_updated_description() {
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
+            criteria_map: [(
+                "example".to_string().into(),
+                vec![SAFE_TO_DEPLOY.to_string().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -1529,3 +1557,128 @@ fn import_wildcard_audit_publisher() {
     let output = get_imports_file_changes_prune(&metadata, &store);
     insta::assert_snapshot!(output);
 }
+
+#[test]
+fn import_criteria_map() {
+    // (Pass)
+
+    let _enter = TEST_RUNTIME.enter();
+    let mock = MockMetadata::simple();
+
+    let metadata = mock.metadata();
+    let (mut config, audits, imports) = files_no_exemptions(&metadata);
+
+    config.imports.insert(
+        FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: FOREIGN_URL.to_owned(),
+            criteria_map: [
+                (
+                    "foreign-reviewed".to_owned().into(),
+                    vec!["reviewed".to_owned().into()],
+                ),
+                (
+                    "safe-to-deploy".to_owned().into(),
+                    vec!["strong-reviewed".to_owned().into()],
+                ),
+                (
+                    "safe-to-run".to_owned().into(),
+                    vec!["weak-reviewed".to_owned().into()],
+                ),
+            ]
+            .into_iter()
+            .collect(),
+            ..Default::default()
+        },
+    );
+
+    config.policy.package.insert(
+        "first-party".to_owned(),
+        PackagePolicyEntry::Unversioned(PolicyEntry {
+            dependency_criteria: [
+                (
+                    "third-party1".to_owned().into(),
+                    vec!["reviewed".to_owned().into()],
+                ),
+                (
+                    "third-party2".to_owned().into(),
+                    vec!["weak-reviewed".to_owned().into()],
+                ),
+            ]
+            .into_iter()
+            .collect(),
+            ..Default::default()
+        }),
+    );
+    config.policy.package.insert(
+        "third-party1".to_owned(),
+        PackagePolicyEntry::Versioned {
+            version: [(
+                ver(DEFAULT_VER),
+                PolicyEntry {
+                    dependency_criteria: [
+                        (
+                            "third-party1".to_owned().into(),
+                            vec!["strong-reviewed".to_owned().into()],
+                        ),
+                        (
+                            "third-party2".to_owned().into(),
+                            vec!["weak-reviewed".to_owned().into()],
+                        ),
+                    ]
+                    .into_iter()
+                    .collect(),
+                    ..Default::default()
+                },
+            )]
+            .into_iter()
+            .collect(),
+        },
+    );
+
+    let new_foreign_audits = AuditsFile {
+        criteria: [
+            (
+                "foreign-strong-reviewed".to_string(),
+                criteria_implies("foreign strongly reviewed", ["foreign-reviewed"]),
+            ),
+            (
+                "foreign-reviewed".to_string(),
+                criteria_implies("foreign reviewed", ["foreign-weak-reviewed"]),
+            ),
+            (
+                "foreign-weak-reviewed".to_string(),
+                criteria("foreign weakly reviewed"),
+            ),
+        ]
+        .into_iter()
+        .collect(),
+        audits: [
+            (
+                "third-party1".to_owned(),
+                vec![full_audit(ver(DEFAULT_VER), "foreign-strong-reviewed")],
+            ),
+            (
+                "transitive-third-party1".to_owned(),
+                vec![full_audit(ver(DEFAULT_VER), "safe-to-deploy")],
+            ),
+            (
+                "third-party2".to_owned(),
+                vec![full_audit(ver(DEFAULT_VER), "safe-to-run")],
+            ),
+        ]
+        .into_iter()
+        .collect(),
+        wildcard_audits: SortedMap::new(),
+    };
+
+    let cfg = mock_cfg(&metadata);
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(&cfg, config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes_prune(&metadata, &store);
+    insta::assert_snapshot!(output);
+}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 80594e4..54b47a2 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -12,12 +12,13 @@ use serde_json::{json, Value};
 
 use crate::{
     format::{
-        AuditKind, CratesPublisher, CriteriaName, CriteriaStr, DependencyCriteria, FastMap,
-        MetaConfig, PackageName, PackagePolicyEntry, PackageStr, PolicyEntry, SortedSet,
-        VersionReq, VetVersion, WildcardEntry, SAFE_TO_DEPLOY, SAFE_TO_RUN,
+        AuditKind, CratesPublisher, CriteriaMap, CriteriaName, CriteriaStr, FastMap, MetaConfig,
+        PackageName, PackagePolicyEntry, PackageStr, PolicyEntry, SortedSet, VersionReq,
+        VetVersion, WildcardEntry, SAFE_TO_DEPLOY, SAFE_TO_RUN,
     },
     git_tool::Editor,
     init_files,
+    network::Network,
     out::Out,
     resolver::ResolveReport,
     AuditEntry, AuditsFile, Config, ConfigFile, CriteriaEntry, ExemptedDependency, ImportsFile,
@@ -32,8 +33,9 @@ use crate::{
 /// Unlike a normal `assert_snapshot!` the snapshot name isn't inferred by this
 /// macro, as multiple snapshots with different names need to be generated.
 macro_rules! assert_report_snapshot {
-    ($name:expr, $metadata:expr, $report:expr) => {{
-        let (human, json) = $crate::tests::get_reports(&$metadata, $report);
+    ($name:expr, $metadata:expr, $store:expr) => {{
+        let report = $crate::resolver::resolve(&$metadata, None, &$store);
+        let (human, json) = $crate::tests::get_reports(&$metadata, report);
         insta::assert_snapshot!($name, human);
         insta::assert_snapshot!(concat!($name, ".json"), json);
     }};
@@ -464,7 +466,7 @@ fn dep_policy(
             .into_iter()
             .map(|(k, v)| {
                 (
-                    k.into(),
+                    k.into().into(),
                     v.into_iter().map(|s| s.into().into()).collect::<Vec<_>>(),
                 )
             })
@@ -972,7 +974,7 @@ fn files_inited(metadata: &Metadata) -> (ConfigFile, AuditsFile, ImportsFile) {
                         audit_as_crates_io: None,
                         criteria: Some(vec![DEFAULT_CRIT.to_string().into()]),
                         dev_criteria: Some(vec![DEFAULT_CRIT.to_string().into()]),
-                        dependency_criteria: DependencyCriteria::new(),
+                        dependency_criteria: CriteriaMap::new(),
                         notes: None,
                     }),
                 );
diff --git a/src/tests/snapshots/cargo_vet__tests__import__import_criteria_map.snap b/src/tests/snapshots/cargo_vet__tests__import__import_criteria_map.snap
new file mode 100644
index 0000000..5004301
--- /dev/null
+++ b/src/tests/snapshots/cargo_vet__tests__import__import_criteria_map.snap
@@ -0,0 +1,20 @@
+---
+source: src/tests/import.rs
+expression: output
+---
++
++[audits.peer-company.criteria.foreign-reviewed]
++description = "foreign reviewed"
++
++[[audits.peer-company.audits.third-party1]]
++criteria = "reviewed"
++version = "10.0.0"
++
++[[audits.peer-company.audits.third-party2]]
++criteria = "weak-reviewed"
++version = "10.0.0"
++
++[[audits.peer-company.audits.transitive-third-party1]]
++criteria = "strong-reviewed"
++version = "10.0.0"
+
diff --git a/src/tests/snapshots/cargo_vet__tests__store_parsing__invalid_criteria_map.snap b/src/tests/snapshots/cargo_vet__tests__store_parsing__invalid_criteria_map.snap
deleted file mode 100644
index ff1a8f4..0000000
--- a/src/tests/snapshots/cargo_vet__tests__store_parsing__invalid_criteria_map.snap
+++ /dev/null
@@ -1,16 +0,0 @@
----
-source: src/tests/store_parsing.rs
-expression: acquire_errors
----
-
-  × Your cargo-vet store (supply-chain) has consistency errors
-
-Error: 
-  × Built-in criteria 'safe-to-deploy' cannot be re-mapped when importing
-    ╭─[config.toml:12:1]
- 12 │ ours = "safe-to-run"
- 13 │ theirs = "safe-to-deploy"
-    ·          ────────────────
-    ╰────
-  help: built-in criteria are always imported unmodified (see issue #425)
-
diff --git a/src/tests/snapshots/cargo_vet__tests__store_parsing__parse_criteria_map.snap b/src/tests/snapshots/cargo_vet__tests__store_parsing__parse_criteria_map.snap
new file mode 100644
index 0000000..802d949
--- /dev/null
+++ b/src/tests/snapshots/cargo_vet__tests__store_parsing__parse_criteria_map.snap
@@ -0,0 +1,5 @@
+---
+source: src/tests/store_parsing.rs
+expression: acquire_errors
+---
+
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-high-hit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-high-hit.json.snap
index 2382a72..4d99401 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-high-hit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-high-hit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "dev:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-deploy",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-imply-hit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-imply-hit.json.snap
index be4d8a3..cf618e8 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-imply-hit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-imply-hit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "dev:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-low-hit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-low-hit.json.snap
index 0c0623e..cc67224 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-low-hit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-low-hit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "dev:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-run",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-redundant-low-hit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-redundant-low-hit.json.snap
index ad314df..9e64225 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-redundant-low-hit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__builtin-simple-deps-violation-redundant-low-hit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "dev:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": [
               "safe-to-run",
@@ -19,7 +19,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-run",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-full-audit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-full-audit.json.snap
index c69c8f1..230f1f0 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-full-audit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-full-audit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "=10",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-unaudited.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-unaudited.json.snap
index cf18b4a..34e2aab 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-unaudited.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-cur-unaudited.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "UnauditedConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "weak-reviewed",
             "version": null,
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-delta.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-delta.json.snap
index 83a55e0..0128f75 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-delta.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-delta.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "=5.0.0",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": null,
@@ -28,7 +28,7 @@ expression: json
       },
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -36,7 +36,7 @@ expression: json
             "violation": "=5.0.0",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": null,
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-full-audit.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-full-audit.json.snap
index 2e1eb3b..0b9c9e3 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-full-audit.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-full-audit.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "=3.0.0",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": "3.0.0",
@@ -28,7 +28,7 @@ expression: json
       },
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -36,7 +36,7 @@ expression: json
             "violation": "=3.0.0",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": null,
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-hit-with-extra-junk.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-hit-with-extra-junk.json.snap
index 2ba66c3..51bf8ca 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-hit-with-extra-junk.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-hit-with-extra-junk.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": [
               "safe-to-run",
@@ -19,7 +19,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-run",
             "version": "10.0.0",
diff --git a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-wildcard.json.snap b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-wildcard.json.snap
index ffbdec4..579eab5 100644
--- a/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-wildcard.json.snap
+++ b/src/tests/snapshots/cargo_vet__tests__violations__mock-simple-violation-wildcard.json.snap
@@ -8,7 +8,7 @@ expression: json
     "third-party1:10.0.0": [
       {
         "AuditConflict": {
-          "violation_source": "Local",
+          "violation_source": null,
           "violation": {
             "criteria": "safe-to-run",
             "version": null,
@@ -16,7 +16,7 @@ expression: json
             "violation": "*",
             "notes": null
           },
-          "audit_source": "Local",
+          "audit_source": null,
           "audit": {
             "criteria": "safe-to-deploy",
             "version": "10.0.0",
diff --git a/src/tests/store_parsing.rs b/src/tests/store_parsing.rs
index 0bac050..24e00d7 100644
--- a/src/tests/store_parsing.rs
+++ b/src/tests/store_parsing.rs
@@ -388,20 +388,16 @@ version = "10.0.0"
 }
 
 #[test]
-fn test_invalid_criteria_map() {
+fn parse_criteria_map() {
     let config = r##"
 # cargo-vet config file
 
 [imports.peer1]
 url = "https://peer1.com"
 
-[[imports.peer1.criteria-map]]
-ours = "safe-to-run"
-theirs = "fuzzed"
-
-[[imports.peer1.criteria-map]]
-ours = "safe-to-run"
-theirs = "safe-to-deploy"
+[imports.peer1.criteria-map]
+fuzzed = "safe-to-run"
+safe-to-deploy = "safe-to-run"
 "##;
 
     let imports = r##"
diff --git a/src/tests/vet.rs b/src/tests/vet.rs
index 95657eb..ff030c4 100644
--- a/src/tests/vet.rs
+++ b/src/tests/vet.rs
@@ -1,5 +1,3 @@
-use crate::format::CriteriaMapping;
-
 use super::*;
 
 #[test]
@@ -13,8 +11,7 @@ fn mock_simple_init() {
     let (config, audits, imports) = files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-simple-init", &metadata, report);
+    assert_report_snapshot!("mock-simple-init", metadata, store);
 }
 
 #[test]
@@ -28,9 +25,8 @@ fn mock_simple_no_exemptions() {
     let (config, audits, imports) = files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-no-unaudited", &metadata, report);
+    assert_report_snapshot!("mock-simple-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -44,9 +40,8 @@ fn mock_simple_full_audited() {
     let (config, audits, imports) = files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-full-audited", &metadata, report);
+    assert_report_snapshot!("mock-simple-full-audited", metadata, store);
 }
 
 #[test]
@@ -60,8 +55,7 @@ fn builtin_simple_init() {
     let (config, audits, imports) = builtin_files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-simple-init", &metadata, report);
+    assert_report_snapshot!("builtin-simple-init", metadata, store);
 }
 
 #[test]
@@ -75,9 +69,8 @@ fn builtin_simple_no_exemptions() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-no-unaudited", &metadata, report);
+    assert_report_snapshot!("builtin-simple-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -91,9 +84,8 @@ fn builtin_simple_full_audited() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-full-audited", &metadata, report);
+    assert_report_snapshot!("builtin-simple-full-audited", metadata, store);
 }
 
 #[test]
@@ -113,9 +105,8 @@ fn mock_simple_missing_transitive() {
         .clear();
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-missing-transitive", &metadata, report);
+    assert_report_snapshot!("mock-simple-missing-transitive", metadata, store);
 }
 
 #[test]
@@ -131,9 +122,8 @@ fn mock_simple_missing_direct_internal() {
     audits.audits.get_mut("third-party1").unwrap().clear();
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-missing-direct-internal", &metadata, report);
+    assert_report_snapshot!("mock-simple-missing-direct-internal", metadata, store);
 }
 
 #[test]
@@ -149,9 +139,8 @@ fn mock_simple_missing_direct_leaf() {
     audits.audits.get_mut("third-party2").unwrap().clear();
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-missing-direct-leaf", &metadata, report);
+    assert_report_snapshot!("mock-simple-missing-direct-leaf", metadata, store);
 }
 
 #[test]
@@ -172,9 +161,8 @@ fn mock_simple_missing_leaves() {
         .clear();
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-missing-leaves", &metadata, report);
+    assert_report_snapshot!("mock-simple-missing-leaves", metadata, store);
 }
 
 #[test]
@@ -201,9 +189,8 @@ fn mock_simple_weaker_transitive_req() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-weaker-transitive-req", &metadata, report);
+    assert_report_snapshot!("mock-simple-weaker-transitive-req", metadata, store);
 }
 
 #[test]
@@ -231,12 +218,11 @@ fn mock_simple_weaker_transitive_req_using_implies() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-weaker-transitive-req-using-implies",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -255,9 +241,8 @@ fn mock_simple_lower_version_review() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 1), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-lower-version-review", &metadata, report);
+    assert_report_snapshot!("mock-simple-lower-version-review", metadata, store);
 }
 
 #[test]
@@ -275,9 +260,8 @@ fn mock_simple_higher_version_review() {
     direct_audits.push(full_audit(ver(DEFAULT_VER + 1), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-higher-version-review", &metadata, report);
+    assert_report_snapshot!("mock-simple-higher-version-review", metadata, store);
 }
 
 #[test]
@@ -298,12 +282,11 @@ fn mock_simple_higher_and_lower_version_review() {
     direct_audits.push(full_audit(ver(DEFAULT_VER + 1), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-higher-and-lower-version-review",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -322,9 +305,8 @@ fn mock_simple_reviewed_too_weakly() {
     trans_audits.push(full_audit(ver(DEFAULT_VER), "weak-reviewed"));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-reviewed-too-weakly", &metadata, report);
+    assert_report_snapshot!("mock-simple-reviewed-too-weakly", metadata, store);
 }
 
 #[test]
@@ -352,9 +334,8 @@ fn mock_simple_delta_to_exemptions() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-delta-to-unaudited", &metadata, report);
+    assert_report_snapshot!("mock-simple-delta-to-unaudited", metadata, store);
 }
 
 #[test]
@@ -382,13 +363,8 @@ fn mock_simple_delta_to_exemptions_overshoot() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-delta-to-unaudited-overshoot",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-delta-to-unaudited-overshoot", metadata, store);
 }
 
 #[test]
@@ -416,13 +392,8 @@ fn mock_simple_delta_to_exemptions_undershoot() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-delta-to-unaudited-undershoot",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-delta-to-unaudited-undershoot", metadata, store);
 }
 
 #[test]
@@ -445,9 +416,8 @@ fn mock_simple_delta_to_full_audit() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-delta-to-full-audit", &metadata, report);
+    assert_report_snapshot!("mock-simple-delta-to-full-audit", metadata, store);
 }
 
 #[test]
@@ -470,13 +440,8 @@ fn mock_simple_delta_to_full_audit_overshoot() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-delta-to-full-audit-overshoot",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-delta-to-full-audit-overshoot", metadata, store);
 }
 
 #[test]
@@ -499,12 +464,11 @@ fn mock_simple_delta_to_full_audit_undershoot() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-delta-to-full-audit-undershoot",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -528,9 +492,8 @@ fn mock_simple_reverse_delta_to_full_audit() {
     direct_audits.push(full_audit(ver(DEFAULT_VER + 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-reverse-delta-to-full-audit", &metadata, report);
+    assert_report_snapshot!("mock-simple-reverse-delta-to-full-audit", metadata, store);
 }
 
 #[test]
@@ -558,9 +521,8 @@ fn mock_simple_reverse_delta_to_exemptions() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-reverse-delta-to-unaudited", &metadata, report);
+    assert_report_snapshot!("mock-simple-reverse-delta-to-unaudited", metadata, store);
 }
 
 #[test]
@@ -588,12 +550,11 @@ fn mock_simple_wrongly_reversed_delta_to_exemptions() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-wrongly-reversed-delta-to-unaudited",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -617,12 +578,11 @@ fn mock_simple_wrongly_reversed_delta_to_full_audit() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-wrongly-reversed-delta-to-full-audit",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -651,12 +611,11 @@ fn mock_simple_needed_reversed_delta_to_exemptions() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "mock-simple-needed-reversed-delta-to-unaudited",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -685,9 +644,8 @@ fn mock_simple_delta_to_exemptions_too_weak() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-delta-to-unaudited-too-weak", &metadata, report);
+    assert_report_snapshot!("mock-simple-delta-to-unaudited-too-weak", metadata, store);
 }
 
 #[test]
@@ -710,13 +668,8 @@ fn mock_simple_delta_to_full_audit_too_weak() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), DEFAULT_CRIT));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-delta-to-full-audit-too-weak",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-delta-to-full-audit-too-weak", metadata, store);
 }
 
 #[test]
@@ -739,13 +692,8 @@ fn mock_simple_delta_to_too_weak_full_audit() {
     direct_audits.push(full_audit(ver(DEFAULT_VER - 5), "weak-reviewed"));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-delta-to-too-weak-full-audit",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-delta-to-too-weak-full-audit", metadata, store);
 }
 
 #[test]
@@ -758,8 +706,7 @@ fn mock_complex_inited() {
     let (config, audits, imports) = files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-inited", &metadata, report);
+    assert_report_snapshot!("mock-complex-inited", metadata, store);
 }
 
 #[test]
@@ -772,8 +719,7 @@ fn mock_complex_no_exemptions() {
     let (config, audits, imports) = files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-no-unaudited", &metadata, report);
+    assert_report_snapshot!("mock-complex-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -786,8 +732,7 @@ fn mock_complex_full_audited() {
     let (config, audits, imports) = files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-full-audited", &metadata, report);
+    assert_report_snapshot!("mock-complex-full-audited", metadata, store);
 }
 
 #[test]
@@ -800,8 +745,7 @@ fn builtin_complex_inited() {
     let (config, audits, imports) = builtin_files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-complex-inited", &metadata, report);
+    assert_report_snapshot!("builtin-complex-inited", metadata, store);
 }
 
 #[test]
@@ -814,8 +758,7 @@ fn builtin_complex_no_exemptions() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-complex-no-unaudited", &metadata, report);
+    assert_report_snapshot!("builtin-complex-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -828,8 +771,7 @@ fn builtin_complex_full_audited() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-complex-full-audited", &metadata, report);
+    assert_report_snapshot!("builtin-complex-full-audited", metadata, store);
 }
 
 #[test]
@@ -842,8 +784,7 @@ fn builtin_complex_minimal_audited() {
     let (config, audits, imports) = builtin_files_minimal_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-complex-minimal-audited", &metadata, report);
+    assert_report_snapshot!("builtin-complex-minimal-audited", metadata, store);
 }
 
 #[test]
@@ -861,8 +802,7 @@ fn mock_complex_missing_core5() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-missing-core5", &metadata, report);
+    assert_report_snapshot!("mock-complex-missing-core5", metadata, store);
 }
 
 #[test]
@@ -880,8 +820,7 @@ fn mock_complex_missing_core10() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-missing-core10", &metadata, report);
+    assert_report_snapshot!("mock-complex-missing-core10", metadata, store);
 }
 
 #[test]
@@ -902,8 +841,7 @@ fn mock_complex_core10_too_weak() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-core10-too-weak", &metadata, report);
+    assert_report_snapshot!("mock-complex-core10-too-weak", metadata, store);
 }
 
 #[test]
@@ -941,8 +879,7 @@ fn mock_complex_core10_partially_too_weak() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("mock-complex-core10-partially-too-weak", &metadata, report);
+    assert_report_snapshot!("mock-complex-core10-partially-too-weak", metadata, store);
 }
 
 #[test]
@@ -980,11 +917,10 @@ fn mock_complex_core10_partially_too_weak_via_weak_delta() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
     assert_report_snapshot!(
         "mock-complex-core10-partially-too-weak-via-weak-delta",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1028,11 +964,10 @@ fn mock_complex_core10_partially_too_weak_via_strong_delta() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
     assert_report_snapshot!(
         "mock-complex-core10-partially-too-weak-via-strong-delta",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1050,8 +985,7 @@ fn mock_simple_policy_root_too_strong() {
         .insert("root-package".to_string(), self_policy(["strong-reviewed"]));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-root-too-strong", &metadata, report);
+    assert_report_snapshot!("simple-policy-root-too-strong", metadata, store);
 }
 
 #[test]
@@ -1068,8 +1002,7 @@ fn mock_simple_policy_root_weaker() {
         .insert("root-package".to_string(), self_policy(["weak-reviewed"]));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-root-weaker", &metadata, report);
+    assert_report_snapshot!("simple-policy-root-weaker", metadata, store);
 }
 
 #[test]
@@ -1086,8 +1019,7 @@ fn mock_simple_policy_first_too_strong() {
         .insert("first-party".to_string(), self_policy(["strong-reviewed"]));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-too-strong", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-too-strong", metadata, store);
 }
 
 #[test]
@@ -1104,8 +1036,7 @@ fn mock_simple_policy_first_weaker() {
         .insert("first-party".to_string(), self_policy(["weak-reviewed"]));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-weaker", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-weaker", metadata, store);
 }
 
 #[test]
@@ -1123,8 +1054,7 @@ fn mock_simple_policy_root_dep_weaker() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-root-dep-weaker", &metadata, report);
+    assert_report_snapshot!("simple-policy-root-dep-weaker", metadata, store);
 }
 
 #[test]
@@ -1142,8 +1072,7 @@ fn mock_simple_policy_root_dep_too_strong() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-root-dep-too-strong", &metadata, report);
+    assert_report_snapshot!("simple-policy-root-dep-too-strong", metadata, store);
 }
 
 #[test]
@@ -1161,8 +1090,7 @@ fn mock_simple_policy_first_dep_weaker() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-weaker", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-weaker", metadata, store);
 }
 
 #[test]
@@ -1180,8 +1108,7 @@ fn mock_simple_policy_first_dep_too_strong() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-too-strong", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-too-strong", metadata, store);
 }
 
 #[test]
@@ -1204,8 +1131,7 @@ fn mock_simple_policy_first_dep_stronger() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-stronger", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-stronger", metadata, store);
 }
 
 #[test]
@@ -1228,8 +1154,7 @@ fn mock_simple_policy_first_dep_weaker_needed() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-weaker-needed", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-weaker-needed", metadata, store);
 }
 
 #[test]
@@ -1255,8 +1180,7 @@ fn mock_simple_policy_first_dep_extra() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-extra", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-extra", metadata, store);
 }
 
 #[test]
@@ -1279,8 +1203,7 @@ fn mock_simple_policy_first_dep_extra_missing() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-dep-extra-missing", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-dep-extra-missing", metadata, store);
 }
 
 #[test]
@@ -1306,11 +1229,10 @@ fn mock_simple_policy_first_extra_partially_missing() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
     assert_report_snapshot!(
         "simple-policy-first-extra-partially-missing",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1329,8 +1251,7 @@ fn mock_simple_first_policy_redundant() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("simple-policy-first-policy-redundant", &metadata, report);
+    assert_report_snapshot!("simple-policy-first-policy-redundant", metadata, store);
 }
 
 #[test]
@@ -1343,8 +1264,7 @@ fn builtin_simple_deps_inited() {
     let (config, audits, imports) = builtin_files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-simple-deps-init", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-init", metadata, store);
 }
 
 #[test]
@@ -1358,9 +1278,8 @@ fn builtin_simple_deps_no_exemptions() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-no-unaudited", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -1374,9 +1293,8 @@ fn builtin_simple_deps_full_audited() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-full-audited", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-full-audited", metadata, store);
 }
 
 #[test]
@@ -1390,9 +1308,8 @@ fn builtin_simple_deps_minimal_audited() {
     let (config, audits, imports) = builtin_files_minimal_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-minimal-audited", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-minimal-audited", metadata, store);
 }
 
 #[test]
@@ -1411,9 +1328,8 @@ fn builtin_no_deps() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-no-deps", &metadata, report);
+    assert_report_snapshot!("builtin-no-deps", metadata, store);
 }
 
 #[test]
@@ -1440,9 +1356,8 @@ fn builtin_only_first_deps() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-only-first-deps", &metadata, report);
+    assert_report_snapshot!("builtin-only-first-deps", metadata, store);
 }
 
 #[test]
@@ -1455,8 +1370,7 @@ fn builtin_cycle_inited() {
     let (config, audits, imports) = builtin_files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-cycle-inited", &metadata, report);
+    assert_report_snapshot!("builtin-cycle-inited", metadata, store);
 }
 
 #[test]
@@ -1470,9 +1384,8 @@ fn builtin_cycle_exemptions() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-cycle-unaudited", &metadata, report);
+    assert_report_snapshot!("builtin-cycle-unaudited", metadata, store);
 }
 
 #[test]
@@ -1486,9 +1399,8 @@ fn builtin_cycle_full_audited() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-cycle-full-audited", &metadata, report);
+    assert_report_snapshot!("builtin-cycle-full-audited", metadata, store);
 }
 
 #[test]
@@ -1502,9 +1414,8 @@ fn builtin_cycle_minimal_audited() {
     let (config, audits, imports) = builtin_files_minimal_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-cycle-minimal-audited", &metadata, report);
+    assert_report_snapshot!("builtin-cycle-minimal-audited", metadata, store);
 }
 
 #[test]
@@ -1543,9 +1454,8 @@ fn builtin_dev_detection() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-dev-detection", &metadata, report);
+    assert_report_snapshot!("builtin-dev-detection", metadata, store);
 }
 
 #[test]
@@ -1559,9 +1469,8 @@ fn builtin_dev_detection_empty() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-dev-detection-empty", &metadata, report);
+    assert_report_snapshot!("builtin-dev-detection-empty", metadata, store);
 }
 
 #[test]
@@ -1575,9 +1484,8 @@ fn builtin_dev_detection_empty_deeper() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-dev-detection-empty-deeper", &metadata, report);
+    assert_report_snapshot!("builtin-dev-detection-empty-deeper", metadata, store);
 }
 
 #[test]
@@ -1602,9 +1510,8 @@ fn builtin_simple_exemptions_extra() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-extra", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-extra", metadata, store);
 }
 
 #[test]
@@ -1624,9 +1531,8 @@ fn builtin_simple_exemptions_not_a_real_dep() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-not-a-real-dep", &metadata, report);
+    assert_report_snapshot!("builtin-simple-not-a-real-dep", metadata, store);
 }
 
 #[test]
@@ -1648,9 +1554,8 @@ fn builtin_simple_deps_exemptions_overbroad() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-overbroad", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-overbroad", metadata, store);
 }
 
 #[test]
@@ -1674,9 +1579,8 @@ fn builtin_complex_exemptions_twins() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-twins", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-twins", metadata, store);
 }
 
 #[test]
@@ -1700,9 +1604,8 @@ fn builtin_complex_exemptions_partial_twins() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-partial-twins", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-partial-twins", metadata, store);
 }
 
 #[test]
@@ -1731,9 +1634,8 @@ fn builtin_simple_exemptions_in_delta() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-in-delta", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-in-delta", metadata, store);
 }
 
 #[test]
@@ -1762,9 +1664,8 @@ fn builtin_simple_exemptions_in_full() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-in-full", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-in-full", metadata, store);
 }
 
 #[test]
@@ -1789,9 +1690,8 @@ fn builtin_simple_exemptions_in_direct_full() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-unaudited-in-direct-full", &metadata, report);
+    assert_report_snapshot!("builtin-simple-unaudited-in-direct-full", metadata, store);
 }
 
 #[test]
@@ -1835,12 +1735,11 @@ fn builtin_simple_exemptions_nested_weaker_req() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-unaudited-nested-weaker-req",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1889,12 +1788,11 @@ fn builtin_simple_exemptions_nested_stronger_req() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-unaudited-nested-stronger-req",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1922,12 +1820,11 @@ fn builtin_simple_deps_exemptions_adds_uneeded_criteria() {
         .insert("dev".to_string(), vec![exemptions(ver(5), SAFE_TO_DEPLOY)]);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-deps-unaudited-adds-uneeded-criteria",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1956,12 +1853,11 @@ fn builtin_dev_detection_exemptions_adds_uneeded_criteria_indirect() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-dev-detection-unaudited-adds-uneeded-criteria-indirect",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -1990,9 +1886,8 @@ fn builtin_dev_detection_cursed_full() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-dev-detection-cursed-full", &metadata, report);
+    assert_report_snapshot!("builtin-dev-detection-cursed-full", metadata, store);
 }
 
 #[test]
@@ -2015,9 +1910,8 @@ fn builtin_dev_detection_cursed_minimal() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-dev-detection-cursed-minimal", &metadata, report);
+    assert_report_snapshot!("builtin-dev-detection-cursed-minimal", metadata, store);
 }
 
 #[test]
@@ -2042,9 +1936,8 @@ fn builtin_simple_delta_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-delta-cycle", &metadata, report);
+    assert_report_snapshot!("builtin-simple-delta-cycle", metadata, store);
 }
 
 #[test]
@@ -2070,9 +1963,8 @@ fn builtin_simple_noop_delta() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-noop-delta", &metadata, report);
+    assert_report_snapshot!("builtin-simple-noop-delta", metadata, store);
 }
 
 #[test]
@@ -2101,9 +1993,8 @@ fn builtin_simple_delta_double_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-delta-double-cycle", &metadata, report);
+    assert_report_snapshot!("builtin-simple-delta-double-cycle", metadata, store);
 }
 
 #[test]
@@ -2132,13 +2023,8 @@ fn builtin_simple_delta_broken_double_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "builtin-simple-delta-broken-double-cycle",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("builtin-simple-delta-broken-double-cycle", metadata, store);
 }
 
 #[test]
@@ -2164,9 +2050,8 @@ fn builtin_simple_delta_broken_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-delta-broken-cycle", &metadata, report);
+    assert_report_snapshot!("builtin-simple-delta-broken-cycle", metadata, store);
 }
 
 #[test]
@@ -2192,9 +2077,8 @@ fn builtin_simple_long_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-long-cycle", &metadata, report);
+    assert_report_snapshot!("builtin-simple-long-cycle", metadata, store);
 }
 
 #[test]
@@ -2220,9 +2104,8 @@ fn builtin_simple_useless_long_cycle() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-useless-long-cycle", &metadata, report);
+    assert_report_snapshot!("builtin-simple-useless-long-cycle", metadata, store);
 }
 
 #[test]
@@ -2236,8 +2119,7 @@ fn builtin_haunted_init() {
     let (config, audits, imports) = builtin_files_inited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
-    assert_report_snapshot!("builtin-haunted-init", &metadata, report);
+    assert_report_snapshot!("builtin-haunted-init", metadata, store);
 }
 
 #[test]
@@ -2251,9 +2133,8 @@ fn builtin_haunted_no_exemptions() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-haunted-no-unaudited", &metadata, report);
+    assert_report_snapshot!("builtin-haunted-no-unaudited", metadata, store);
 }
 
 #[test]
@@ -2267,9 +2148,8 @@ fn builtin_haunted_no_exemptions_deeper() {
     let (config, audits, imports) = builtin_files_no_exemptions(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-haunted-no-unaudited-deeper", &metadata, report);
+    assert_report_snapshot!("builtin-haunted-no-unaudited-deeper", metadata, store);
 }
 
 #[test]
@@ -2283,9 +2163,8 @@ fn builtin_haunted_full_audited() {
     let (config, audits, imports) = builtin_files_full_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-haunted-full-audited", &metadata, report);
+    assert_report_snapshot!("builtin-haunted-full-audited", metadata, store);
 }
 
 #[test]
@@ -2299,9 +2178,8 @@ fn builtin_haunted_minimal_audited() {
     let (config, audits, imports) = builtin_files_minimal_audited(&metadata);
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-haunted-minimal-audited", &metadata, report);
+    assert_report_snapshot!("builtin-haunted-minimal-audited", metadata, store);
 }
 
 #[test]
@@ -2319,12 +2197,11 @@ fn builtin_simple_audit_as_default_root_no_audit() {
         .insert("root-package".to_string(), audit_as_policy(Some(true)));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-audit-as-default-root-no-audit",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -2347,9 +2224,8 @@ fn builtin_simple_audit_as_default_root() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-audit-as-default-root", &metadata, report);
+    assert_report_snapshot!("builtin-simple-audit-as-default-root", metadata, store);
 }
 
 #[test]
@@ -2371,12 +2247,11 @@ fn builtin_simple_audit_as_default_root_too_weak() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-audit-as-default-root-too-weak",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -2402,9 +2277,8 @@ fn builtin_simple_audit_as_weaker_root() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-audit-as-weaker-root", &metadata, report);
+    assert_report_snapshot!("builtin-simple-audit-as-weaker-root", metadata, store);
 }
 
 #[test]
@@ -2427,9 +2301,8 @@ fn builtin_simple_foreign_audited() {
     audits.audits.clear();
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin_simple_foreign_audited", &metadata, report);
+    assert_report_snapshot!("builtin_simple_foreign_audited", metadata, store);
 }
 
 #[test]
@@ -2441,8 +2314,11 @@ fn mock_simple_foreign_audited_pun_no_mapping() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, mut audits, mut imports) = files_full_audited(&metadata);
-    imports.audits.insert(FOREIGN.to_owned(), audits.clone());
+    let (mut config, mut audits, imports) = files_full_audited(&metadata);
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &audits);
+
     config.imports.insert(
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
@@ -2452,13 +2328,20 @@ fn mock_simple_foreign_audited_pun_no_mapping() {
     );
     audits.audits.clear();
 
-    let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
+    let store = Store::mock_online(
+        &mock_cfg(&metadata),
+        config,
+        audits,
+        imports,
+        &network,
+        true,
+    )
+    .unwrap();
 
     assert_report_snapshot!(
         "mock_simple_foreign_audited_pun_no_mapping",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -2470,25 +2353,37 @@ fn mock_simple_foreign_audited_pun_mapped() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, mut audits, mut imports) = files_full_audited(&metadata);
-    imports.audits.insert(FOREIGN.to_owned(), audits.clone());
+    let (mut config, mut audits, imports) = files_full_audited(&metadata);
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &audits);
+
     config.imports.insert(
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
-            criteria_map: vec![CriteriaMapping {
-                ours: DEFAULT_CRIT.to_owned(),
-                theirs: DEFAULT_CRIT.to_owned().into(),
-            }],
+            criteria_map: [(
+                DEFAULT_CRIT.to_owned().into(),
+                vec![DEFAULT_CRIT.to_owned().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
     audits.audits.clear();
 
-    let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
+    let store = Store::mock_online(
+        &mock_cfg(&metadata),
+        config,
+        audits,
+        imports,
+        &network,
+        true,
+    )
+    .unwrap();
 
-    assert_report_snapshot!("mock_simple_foreign_audited_pun_mapped", &metadata, report);
+    assert_report_snapshot!("mock_simple_foreign_audited_pun_mapped", metadata, store);
 }
 
 #[test]
@@ -2499,21 +2394,24 @@ fn mock_simple_foreign_audited_pun_wrong_mapped() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, mut audits, mut imports) = files_full_audited(&metadata);
+    let (mut config, mut audits, imports) = files_full_audited(&metadata);
+
     // FOREIGN has the audits, but OTHER_FOREIGN has the mapping
-    imports.audits.insert(FOREIGN.to_owned(), audits.clone());
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &audits);
     audits.audits.clear();
-    imports
-        .audits
-        .insert(OTHER_FOREIGN.to_owned(), audits.clone());
+    network.mock_serve_toml(OTHER_FOREIGN_URL, &audits);
+
     config.imports.insert(
         OTHER_FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: OTHER_FOREIGN_URL.to_owned(),
-            criteria_map: vec![CriteriaMapping {
-                ours: DEFAULT_CRIT.to_owned(),
-                theirs: DEFAULT_CRIT.to_owned().into(),
-            }],
+            criteria_map: [(
+                DEFAULT_CRIT.to_owned().into(),
+                vec![DEFAULT_CRIT.to_owned().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -2525,13 +2423,20 @@ fn mock_simple_foreign_audited_pun_wrong_mapped() {
         },
     );
 
-    let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
+    let store = Store::mock_online(
+        &mock_cfg(&metadata),
+        config,
+        audits,
+        imports,
+        &network,
+        true,
+    )
+    .unwrap();
 
     assert_report_snapshot!(
         "mock_simple_foreign_audited_pun_wrong_mapped",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -2570,9 +2475,8 @@ fn builtin_simple_foreign_tag_team() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin_simple_foreign_tag_team", &metadata, report);
+    assert_report_snapshot!("builtin_simple_foreign_tag_team", metadata, store);
 }
 
 #[test]
@@ -2630,7 +2534,6 @@ fn builtin_simple_mega_foreign_tag_team() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin_simple_mega_foreign_tag_team", &metadata, report);
+    assert_report_snapshot!("builtin_simple_mega_foreign_tag_team", metadata, store);
 }
diff --git a/src/tests/violations.rs b/src/tests/violations.rs
index 31c8dd1..ce95f9d 100644
--- a/src/tests/violations.rs
+++ b/src/tests/violations.rs
@@ -18,9 +18,8 @@ fn mock_simple_violation_cur_exemptions() {
         .push(violation(violation_ver, "weak-reviewed"));
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-violation-cur-unaudited", &metadata, report);
+    assert_report_snapshot!("mock-simple-violation-cur-unaudited", metadata, store);
 }
 
 #[test]
@@ -43,9 +42,8 @@ fn mock_simple_violation_cur_full_audit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-violation-cur-full-audit", &metadata, report);
+    assert_report_snapshot!("mock-simple-violation-cur-full-audit", metadata, store);
 }
 
 #[test]
@@ -70,9 +68,8 @@ fn mock_simple_violation_delta() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-violation-delta", &metadata, report);
+    assert_report_snapshot!("mock-simple-violation-delta", metadata, store);
 }
 
 #[test]
@@ -97,9 +94,8 @@ fn mock_simple_violation_full_audit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-violation-full-audit", &metadata, report);
+    assert_report_snapshot!("mock-simple-violation-full-audit", metadata, store);
 }
 
 #[test]
@@ -122,9 +118,8 @@ fn mock_simple_violation_wildcard() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("mock-simple-violation-wildcard", &metadata, report);
+    assert_report_snapshot!("mock-simple-violation-wildcard", metadata, store);
 }
 
 #[test]
@@ -146,9 +141,8 @@ fn builtin_simple_deps_violation_dodged() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-violation-dodged", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-violation-dodged", metadata, store);
 }
 
 #[test]
@@ -170,9 +164,8 @@ fn builtin_simple_deps_violation_low_hit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-violation-low-hit", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-violation-low-hit", metadata, store);
 }
 
 #[test]
@@ -194,9 +187,8 @@ fn builtin_simple_deps_violation_high_hit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-violation-high-hit", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-violation-high-hit", metadata, store);
 }
 
 #[test]
@@ -218,9 +210,8 @@ fn builtin_simple_deps_violation_imply_hit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("builtin-simple-deps-violation-imply-hit", &metadata, report);
+    assert_report_snapshot!("builtin-simple-deps-violation-imply-hit", metadata, store);
 }
 
 #[test]
@@ -242,12 +233,11 @@ fn builtin_simple_deps_violation_redundant_low_hit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
     assert_report_snapshot!(
         "builtin-simple-deps-violation-redundant-low-hit",
-        &metadata,
-        report
+        metadata,
+        store
     );
 }
 
@@ -270,11 +260,6 @@ fn mock_simple_violation_hit_with_extra_junk() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "mock-simple-violation-hit-with-extra-junk",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("mock-simple-violation-hit-with-extra-junk", metadata, store);
 }
diff --git a/src/tests/wildcard.rs b/src/tests/wildcard.rs
index d3681ed..f6742ef 100644
--- a/src/tests/wildcard.rs
+++ b/src/tests/wildcard.rs
@@ -21,9 +21,8 @@ fn wildcard_full_audit_locked() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("wildcard_full_audit_locked", &metadata, report);
+    assert_report_snapshot!("wildcard_full_audit_locked", metadata, store);
 }
 
 #[test]
@@ -47,13 +46,8 @@ fn wildcard_full_audit_wrong_user_id_locked() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "wildcard_full_audit_wrong_user_id_locked",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("wildcard_full_audit_wrong_user_id_locked", metadata, store);
 }
 
 #[test]
@@ -80,9 +74,8 @@ fn wildcard_delta_audit_locked() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("wildcard_delta_audit_locked", &metadata, report);
+    assert_report_snapshot!("wildcard_delta_audit_locked", metadata, store);
 }
 
 #[test]
@@ -109,13 +102,8 @@ fn wildcard_delta_audit_wrong_user_id_locked() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!(
-        "wildcard_delta_audit_wrong_user_id_locked",
-        &metadata,
-        report
-    );
+    assert_report_snapshot!("wildcard_delta_audit_wrong_user_id_locked", metadata, store);
 }
 
 #[test]
@@ -127,6 +115,9 @@ fn imported_wildcard_audit() {
 
     let metadata = mock.metadata();
     let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+    audits
+        .criteria
+        .insert("example".to_string(), criteria("example criteria"));
     audits.audits.remove("third-party1");
     audits.audits.insert(
         "transitive-third-party1".to_owned(),
@@ -161,6 +152,12 @@ fn imported_wildcard_audit() {
         FOREIGN.to_owned(),
         crate::format::RemoteImport {
             url: FOREIGN_URL.to_owned(),
+            criteria_map: [(
+                "example".to_owned().into(),
+                vec!["example".to_owned().into()],
+            )]
+            .into_iter()
+            .collect(),
             ..Default::default()
         },
     );
@@ -181,7 +178,6 @@ fn imported_wildcard_audit() {
     );
 
     let store = Store::mock(config, audits, imports);
-    let report = crate::resolver::resolve(&metadata, None, &store);
 
-    assert_report_snapshot!("imported_wildcard_audit", &metadata, report);
+    assert_report_snapshot!("imported_wildcard_audit", metadata, store);
 }