Repository: mozilla/cargo-vet
Pull request #399 (2023-02-07 20:57:30)

Import only the minimal number of audits required for vet to pass
This is the first part of #398, without any changes specifically pertaining to wildcard audits.

Doing minimal imports is a large change to how the import backend works. It takes advantage of the lack of per-audit `dependency-criteria` since #382 to dramatically simplify the set of audits which we import such that we always only import the
smallest number of audits possible.

While this impacts all imports, this is part of the work towards wildcard audits, as reifying all wildcard audits would be unnecessarily noisy, due to the number of releases of some crates.

In order to do this in a reliable fashion, we now sort audit edges more aggressively when traversing the tree, so as to give a more deterministic total order and ensure we're reliably selecting the same edges.

In addition, we now mock out the network layer completely when running unit tests in order to allow more flexibility when writing unit tests for wildcard audits in the future.

mystor: cc @afranchuk

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4ac59c2..3382e05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -125,6 +125,7 @@ name = "cargo-vet"
 version = "0.3.0"
 dependencies = [
  "base64-stream",
+ "bytes",
  "cargo_metadata",
  "clap",
  "clap-cargo",
diff --git a/Cargo.toml b/Cargo.toml
index 093eeaf..627972c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ exclude = [
 
 [dependencies]
 base64-stream = "1.2.7"
+bytes = "1.1.0"
 cargo_metadata = "0.15.2"
 clap = { version = "3.2.6", features = ["derive"] }
 clap-cargo = "0.9.1"
diff --git a/src/main.rs b/src/main.rs
index 626eedf..318b9f9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -661,7 +661,7 @@ fn cmd_certify(
     // potentially update imports, and remove now-unnecessary exemptions.
     // Explicitly disallow new exemptions so that exemptions are only updated
     // once we start passing vet.
-    match resolver::regenerate_exemptions(cfg, &mut store, false, false) {
+    match resolver::regenerate_exemptions(cfg, &mut store, false) {
         Ok(()) | Err(RegenerateExemptionsError::ViolationConflict) => {}
     }
 
@@ -1284,7 +1284,7 @@ fn cmd_regenerate_imports(
 
     // NOTE: Explicitly ignore the `ViolationConflict` error, as we still want
     // to update imports in that case.
-    match resolver::regenerate_exemptions(cfg, &mut store, false, true) {
+    match resolver::regenerate_exemptions(cfg, &mut store, false) {
         Ok(()) | Err(RegenerateExemptionsError::ViolationConflict) => {}
     }
 
@@ -1365,7 +1365,7 @@ fn cmd_regenerate_exemptions(
     let network = Network::acquire(cfg);
     let mut store = Store::acquire(cfg, network.as_ref(), false)?;
 
-    resolver::regenerate_exemptions(cfg, &mut store, true, false)?;
+    resolver::regenerate_exemptions(cfg, &mut store, true)?;
 
     // We were successful, commit the store
     store.commit()?;
@@ -1531,7 +1531,7 @@ fn cmd_check(
     } else {
         if !cfg.cli.locked {
             #[allow(clippy::single_match)]
-            match resolver::regenerate_exemptions(cfg, &mut store, false, false) {
+            match resolver::regenerate_exemptions(cfg, &mut store, false) {
                 Err(RegenerateExemptionsError::ViolationConflict) => {
                     unreachable!("unexpeced violation conflict regenerating exemptions?")
                 }
@@ -1568,7 +1568,7 @@ fn cmd_fetch_imports(
 
     // NOTE: Explicitly ignore the `ViolationConflict` error, as we still want
     // to update imports in that case.
-    match resolver::regenerate_exemptions(cfg, &mut store, false, true) {
+    match resolver::regenerate_exemptions(cfg, &mut store, false) {
         Ok(()) | Err(RegenerateExemptionsError::ViolationConflict) => {}
     }
 
diff --git a/src/network.rs b/src/network.rs
index e07910a..31db024 100644
--- a/src/network.rs
+++ b/src/network.rs
@@ -15,16 +15,55 @@ use std::{
 };
 
 use base64_stream::FromBase64Writer;
-use reqwest::{Client, Response, Url};
+use bytes::Bytes;
+use reqwest::{Client, Url};
 use tokio::io::AsyncWriteExt;
 
 use crate::{errors::DownloadError, PartialConfig};
 
+/// Wrapper for the pair of a `reqwest::Response` and the `SemaphorePermit` used
+/// to limit concurrent connections, with a test-only variant for mocking.
+enum Response<'a> {
+    Real(reqwest::Response, tokio::sync::SemaphorePermit<'a>),
+    #[cfg(test)]
+    Mock(Option<Bytes>),
+}
+
+impl Response<'_> {
+    fn has_header(&self, name: &str) -> bool {
+        match self {
+            Response::Real(response, _) => response.headers().contains_key(name),
+            #[cfg(test)]
+            Response::Mock(_) => false,
+        }
+    }
+
+    /// Get the next chunk in the response stream, or `None` if at the end of
+    /// the stream.
+    async fn chunk(&mut self) -> Result<Option<Bytes>, DownloadError> {
+        match self {
+            Response::Real(res, _) => {
+                res.chunk()
+                    .await
+                    .map_err(|error| DownloadError::FailedToReadDownload {
+                        url: Box::new(res.url().clone()),
+                        error,
+                    })
+            }
+            #[cfg(test)]
+            Response::Mock(data) => Ok(data.take()),
+        }
+    }
+}
+
 pub struct Network {
     /// The HTTP client all requests go through
     client: Client,
     /// Semaphore preventing exceeding the maximum number of connections.
     connection_semaphore: tokio::sync::Semaphore,
+    /// Test-only override for download requests.
+    #[cfg(test)]
+    mock_network: Option<std::collections::HashMap<Url, Bytes>>,
 }
 
 static DEFAULT_TIMEOUT_SECS: u64 = 60;
@@ -44,9 +83,9 @@ pub enum PayloadEncoding {
 }
 
 impl PayloadEncoding {
-    pub fn for_response(response: &Response) -> Self {
+    fn for_response(response: &Response) -> Self {
         // gitiles always encodes content in base64
-        if response.headers().contains_key("x-gitiles-object-type") {
+        if response.has_header("x-gitiles-object-type") {
             Self::Base64
         } else {
             Self::Plaintext
@@ -89,6 +128,8 @@ impl Network {
             Some(Self {
                 client,
                 connection_semaphore: tokio::sync::Semaphore::new(MAX_CONCURRENT_CONNECTIONS),
+                #[cfg(test)]
+                mock_network: None,
             })
         }
     }
@@ -104,22 +145,7 @@ impl Network {
             OsStr::new(".part"),
         ]));
         {
-            let _permit = self
-                .connection_semaphore
-                .acquire()
-                .await
-                .expect("Semaphore dropped?!");
-
-            let mut res = self
-                .client
-                .get(url.clone())
-                .send()
-                .await
-                .and_then(|res| res.error_for_status())
-                .map_err(|error| DownloadError::FailedToStartDownload {
-                    url: Box::new(url.clone()),
-                    error,
-                })?;
+            let mut res = self.fetch_core(url).await?;
 
             let mut download_tmp =
                 tokio::fs::File::create(&download_tmp_path)
@@ -129,22 +155,13 @@ impl Network {
                         error,
                     })?;
 
-            while let Some(chunk) =
-                res.chunk()
-                    .await
-                    .map_err(|error| DownloadError::FailedToReadDownload {
-                        url: Box::new(url.clone()),
-                        error,
-                    })?
-            {
-                let network_bytes = &chunk[..];
-                download_tmp
-                    .write_all(network_bytes)
-                    .await
-                    .map_err(|error| DownloadError::FailedToWriteDownload {
+            while let Some(chunk) = res.chunk().await? {
+                download_tmp.write_all(&chunk[..]).await.map_err(|error| {
+                    DownloadError::FailedToWriteDownload {
                         target: download_tmp_path.clone(),
                         error,
-                    })?;
+                    }
+                })?;
             }
         }
 
@@ -165,13 +182,51 @@ impl Network {
 
     /// Download a file into memory
     pub async fn download(&self, url: Url) -> Result<Vec<u8>, DownloadError> {
-        let _permit = self
+        let mut res = self.fetch_core(url).await?;
+
+        let encoding = PayloadEncoding::for_response(&res);
+
+        let mut output = vec![];
+        {
+            let mut writer = encoding.to_plaintext(&mut output);
+            while let Some(chunk) = res.chunk().await? {
+                writer
+                    .write_all(&chunk[..])
+                    .map_err(|error| DownloadError::InvalidEncoding { encoding, error })?;
+            }
+            writer
+                .flush()
+                .map_err(|error| DownloadError::InvalidEncoding { encoding, error })?;
+        }
+        Ok(output)
+    }
+
+    /// Internal core implementation of network fetching which is shared between
+    /// `download` and `download_and_persist`.
+    async fn fetch_core(&self, url: Url) -> Result<Response, DownloadError> {
+        #[cfg(test)]
+        if let Some(mock_network) = &self.mock_network {
+            let chunk = mock_network
+                .get(&url)
+                .cloned()
+                // The error is complete nonsense, but this is test-only.
+                .ok_or(DownloadError::FailedToWriteDownload {
+                    target: url.to_string().into(),
+                    error: std::io::Error::new(
+                        std::io::ErrorKind::Other,
+                        format!("mock network does not support URL: {url}"),
+                    ),
+                })?;
+            return Ok(Response::Mock(Some(chunk)));
+        }
+
+        let permit = self
             .connection_semaphore
             .acquire()
             .await
             .expect("Semaphore dropped?!");
 
-        let mut res = self
+        let res = self
             .client
             .get(url.clone())
             .send()
@@ -182,28 +237,40 @@ impl Network {
                 error,
             })?;
 
-        let encoding = PayloadEncoding::for_response(&res);
+        Ok(Response::Real(res, permit))
+    }
+}
 
-        let mut output = vec![];
-        {
-            let mut writer = encoding.to_plaintext(&mut output);
-            while let Some(chunk) =
-                res.chunk()
-                    .await
-                    .map_err(|error| DownloadError::FailedToReadDownload {
-                        url: Box::new(url.clone()),
-                        error,
-                    })?
-            {
-                writer
-                    .write_all(&chunk[..])
-                    .map_err(|error| DownloadError::InvalidEncoding { encoding, error })?;
-            }
-            writer
-                .flush()
-                .map_err(|error| DownloadError::InvalidEncoding { encoding, error })?;
+#[cfg(test)]
+impl Network {
+    /// Create a new Network which is serving mocked out resources.
+    pub(crate) fn new_mock() -> Self {
+        Network {
+            client: Client::new(),
+            connection_semaphore: tokio::sync::Semaphore::new(MAX_CONCURRENT_CONNECTIONS),
+            #[cfg(test)]
+            mock_network: Some(Default::default()),
         }
+    }
 
-        Ok(output)
+    /// Add a new resource to be served by a mocked-out network.
+    pub(crate) fn mock_serve(&mut self, url: impl AsRef<str>, data: impl AsRef<[u8]>) {
+        self.mock_network
+            .as_mut()
+            .expect("not a mock network")
+            .insert(
+                url.as_ref().parse().unwrap(),
+                Bytes::copy_from_slice(data.as_ref()),
+            );
+    }
+
+    /// Add a new toml resource to be served by a mocked-out network.
+    pub(crate) fn mock_serve_toml(&mut self, url: impl AsRef<str>, data: &impl serde::Serialize) {
+        self.mock_serve(
+            url,
+            crate::serialization::to_formatted_toml(data)
+                .unwrap()
+                .to_string(),
+        );
     }
 }
diff --git a/src/resolver.rs b/src/resolver.rs
index 62b27da..93af32d 100644
--- a/src/resolver.rs
+++ b/src/resolver.rs
@@ -98,8 +98,6 @@ pub struct Success {
     pub vetted_partially: Vec<PackageIdx>,
     /// Third-party packages that were successfully vetted using only 'audits'
     pub vetted_fully: Vec<PackageIdx>,
-    /// Third-party packages that needed fresh imports to be successfully vetted
-    pub needed_fresh_imports: SortedSet<PackageIdx>,
 }
 
 #[derive(Debug, Clone)]
@@ -276,7 +274,10 @@ pub struct ResolveResult<'a> {
     /// Graph used to compute audits for this package.
     pub audit_graph: AuditGraph<'a>,
     /// Cache of search results for each criteria.
-    pub search_results: Vec<Result<Caveats, SearchFailure>>,
+    pub search_results: Vec<Result<Vec<DeltaEdgeOrigin>, SearchFailure>>,
+    /// Which edges which were used to satisfy required criteria, or `None` if
+    /// the package failed to vet.
+    pub required_edges: Option<SortedSet<DeltaEdgeOrigin>>,
 }
 
 #[derive(Debug, Clone)]
@@ -293,17 +294,6 @@ pub struct SearchFailure {
     pub reachable_from_target: SortedSet<Option<VetVersion>>,
 }
 
-/// Caveats which apply to the results of an audit.
-#[derive(Debug, Clone, Default)]
-pub struct Caveats {
-    /// The audit was certified directly because of an exemption.
-    pub directly_exempted: bool,
-    /// The audit required at least one exemption.
-    pub needed_exemptions: bool,
-    /// The audit required at least one fresh import.
-    pub needed_fresh_imports: bool,
-}
-
 type DirectedAuditGraph<'a> = SortedMap<Option<&'a VetVersion>, Vec<DeltaEdge<'a>>>;
 
 /// A graph of the audits for a package.
@@ -337,19 +327,19 @@ pub struct AuditGraph<'a> {
     backward_audits: DirectedAuditGraph<'a>,
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-enum DeltaEdgeOrigin {
-    /// This edge represents an audit from the store, either within audits.toml
-    /// or within the cached imports.lock file. These edges will be tried first.
-    StoredAudit,
-    /// This edge represents an exemption. These edges will be tried after
-    /// stored audits, but before freshly-imported audits have been attempted.
-    Exemption,
-    /// This edge represents an imported audit from a peer which is only present
-    /// on the remote server, and not present in the cached imports.lock file.
-    /// These edges will be tried after all locally-available audits have been
-    /// attempted.
-    FreshImportedAudit,
+/// The precise origin of an edge in the audit graph.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum DeltaEdgeOrigin {
+    /// This edge represents an audit from the local audits.toml.
+    StoredLocalAudit { audit_index: usize },
+    /// This edge represents an audit imported from a peer, potentially stored
+    /// in the local imports.lock.
+    ImportedAudit {
+        import_index: usize,
+        audit_index: usize,
+    },
+    /// This edge represents an exemption from the local config.toml.
+    Exemption { exemption_index: usize },
 }
 
 /// A directed edge in the graph of audits. This may be forward or backwards,
@@ -364,6 +354,9 @@ struct DeltaEdge<'a> {
     /// The origin of this edge. See `DeltaEdgeOrigin`'s documentation for more
     /// details.
     origin: DeltaEdgeOrigin,
+    /// Whether or not the edge is a "fresh import", and should be
+    /// de-prioritized to avoid unnecessary imports.lock updates.
+    is_fresh_import: bool,
 }
 
 const NUM_BUILTINS: usize = 2;
@@ -1252,7 +1245,6 @@ fn resolve_audits<'a>(
     let mut vetted_with_exemptions = Vec::new();
     let mut vetted_partially = Vec::new();
     let mut vetted_fully = Vec::new();
-    let mut needed_fresh_imports = SortedSet::new();
     let results: Vec<_> = requirements
         .iter()
         .enumerate()
@@ -1274,39 +1266,51 @@ fn resolve_audits<'a>(
                 .map(|criteria_idx| audit_graph.search(criteria_idx, &package.version))
                 .collect();
 
-            let mut caveats = Caveats::default();
+            let mut needed_exemptions = false;
+            let mut directly_exempted = false;
             let mut criteria_failures = criteria_mapper.no_criteria();
             for criteria_idx in required_criteria.indices() {
                 match &search_results[criteria_idx] {
-                    Ok(criteria_caveats) => {
-                        caveats.directly_exempted |= criteria_caveats.directly_exempted;
-                        caveats.needed_exemptions |= criteria_caveats.needed_exemptions;
-                        caveats.needed_fresh_imports |= criteria_caveats.needed_fresh_imports;
+                    Ok(path) => {
+                        needed_exemptions |= path
+                            .iter()
+                            .any(|o| matches!(o, DeltaEdgeOrigin::Exemption { .. }));
+                        directly_exempted |=
+                            path.len() == 1 && matches!(path[0], DeltaEdgeOrigin::Exemption { .. });
                     }
                     Err(_) => criteria_failures.set_criteria(criteria_idx),
                 }
             }
 
-            if !criteria_failures.is_empty() {
+            let required_edges = if criteria_failures.is_empty() {
+                // Only collect required edges from minimal_indices as those
+                // edges will be sufficient for a full audit.
+                Some(
+                    criteria_mapper
+                        .minimal_indices(required_criteria)
+                        .flat_map(|criteria_idx| search_results[criteria_idx].as_ref().unwrap())
+                        .cloned()
+                        .collect(),
+                )
+            } else {
                 failures.push((pkgidx, AuditFailure { criteria_failures }));
-            }
+                None
+            };
 
             // XXX: Callers using these fields in success should perhaps be
             // changed to instead walk the results?
-            if !caveats.needed_exemptions {
+            if !needed_exemptions {
                 vetted_fully.push(pkgidx);
-            } else if caveats.directly_exempted {
+            } else if directly_exempted {
                 vetted_with_exemptions.push(pkgidx);
             } else {
                 vetted_partially.push(pkgidx);
             }
-            if caveats.needed_fresh_imports {
-                needed_fresh_imports.insert(pkgidx);
-            }
 
             Some(ResolveResult {
                 audit_graph,
                 search_results,
+                required_edges,
             })
         })
         .collect();
@@ -1323,7 +1327,6 @@ fn resolve_audits<'a>(
             vetted_with_exemptions,
             vetted_partially,
             vetted_fully,
-            needed_fresh_imports,
         })
     };
 
@@ -1355,7 +1358,14 @@ impl<'a> AuditGraph<'a> {
             .map(|v| &v[..])
             .unwrap_or(&[])
             .iter()
-            .map(|audit| (&CriteriaNamespace::Local, audit));
+            .enumerate()
+            .map(|(audit_index, audit)| {
+                (
+                    &CriteriaNamespace::Local,
+                    DeltaEdgeOrigin::StoredLocalAudit { audit_index },
+                    audit,
+                )
+            });
 
         // Each foreign audit should be put into a "foreign" criteria namespace.
         let foreign_audits =
@@ -1363,15 +1373,25 @@ impl<'a> AuditGraph<'a> {
                 .imported_audits()
                 .values()
                 .enumerate()
-                .flat_map(|(idx, audits)| {
-                    let namespace = &foreign_namespaces[idx];
+                .flat_map(|(import_index, audits)| {
+                    let namespace = &foreign_namespaces[import_index];
                     audits
                         .audits
                         .get(package)
                         .map(|v| &v[..])
                         .unwrap_or(&[])
                         .iter()
-                        .map(move |audit| (namespace, audit))
+                        .enumerate()
+                        .map(move |(audit_index, audit)| {
+                            (
+                                namespace,
+                                DeltaEdgeOrigin::ImportedAudit {
+                                    import_index,
+                                    audit_index,
+                                },
+                                audit,
+                            )
+                        })
                 });
 
         let all_audits = own_audits.chain(foreign_audits);
@@ -1382,7 +1402,7 @@ impl<'a> AuditGraph<'a> {
         let mut violation_nodes = Vec::new();
 
         // Collect up all the deltas, and their criteria
-        for (namespace, entry) in all_audits.clone() {
+        for (namespace, origin, entry) in all_audits.clone() {
             // For uniformity, model a Full Audit as `None -> x.y.z`
             let (from_ver, to_ver) = match &entry.kind {
                 AuditKind::Full { version } => (None, version),
@@ -1395,16 +1415,11 @@ impl<'a> AuditGraph<'a> {
 
             let criteria = criteria_mapper.criteria_from_namespaced_entry(namespace, entry);
 
-            let origin = if entry.is_fresh_import {
-                DeltaEdgeOrigin::FreshImportedAudit
-            } else {
-                DeltaEdgeOrigin::StoredAudit
-            };
-
             forward_audits.entry(from_ver).or_default().push(DeltaEdge {
                 version: Some(to_ver),
                 criteria: criteria.clone(),
                 origin,
+                is_fresh_import: entry.is_fresh_import,
             });
             backward_audits
                 .entry(Some(to_ver))
@@ -1413,26 +1428,30 @@ impl<'a> AuditGraph<'a> {
                     version: from_ver,
                     criteria,
                     origin,
+                    is_fresh_import: entry.is_fresh_import,
                 });
         }
 
         // Exempted entries are equivalent to full-audits
         if let Some(alloweds) = exemptions {
-            for allowed in alloweds {
+            for (exemption_index, allowed) in alloweds.iter().enumerate() {
                 let from_ver = None;
                 let to_ver = Some(&allowed.version);
                 let criteria = criteria_mapper.criteria_from_list(&allowed.criteria);
+                let origin = DeltaEdgeOrigin::Exemption { exemption_index };
 
                 // For simplicity, turn 'exemptions' entries into deltas from None.
                 forward_audits.entry(from_ver).or_default().push(DeltaEdge {
                     version: to_ver,
                     criteria: criteria.clone(),
-                    origin: DeltaEdgeOrigin::Exemption,
+                    origin,
+                    is_fresh_import: false,
                 });
                 backward_audits.entry(to_ver).or_default().push(DeltaEdge {
                     version: from_ver,
                     criteria,
-                    origin: DeltaEdgeOrigin::Exemption,
+                    origin,
+                    is_fresh_import: false,
                 });
             }
         }
@@ -1492,7 +1511,7 @@ impl<'a> AuditGraph<'a> {
             }
 
             // Note if this entry conflicts with any audits
-            for (namespace, audit) in all_audits.clone() {
+            for (namespace, _origin, audit) in all_audits.clone() {
                 let audit_criteria =
                     criteria_mapper.criteria_from_namespaced_entry(namespace, audit);
                 let has_violation = violation_criterias
@@ -1541,22 +1560,26 @@ impl<'a> AuditGraph<'a> {
     }
 
     /// Search for a path in this AuditGraph which indicates that the given
-    /// version of the crate satisfies the given criteria. Returns the `Caveats`
-    /// which were required for that proof if successful, and information about
-    /// the versions which could be reached from both the target and root if
-    /// unsuccessful.
+    /// version of the crate satisfies the given criteria. Returns the path used
+    /// for that proof if successful, and information about the versions which
+    /// could be reached from both the target and root if unsuccessful.
     pub fn search(
         &self,
         criteria_idx: usize,
         version: &VetVersion,
-    ) -> Result<Caveats, SearchFailure> {
-        search_for_path(&self.forward_audits, criteria_idx, None, Some(version)).map_err(
-            |reachable_from_root| {
-                // The search failed, perform the search backwards in order to
-                // also get the set of nodes reachable from the target. We can
-                // `unwrap_err()` here, as we'll definitely fail.
-                let reachable_from_target =
-                    search_for_path(&self.backward_audits, criteria_idx, Some(version), None)
+    ) -> Result<Vec<DeltaEdgeOrigin>, SearchFailure> {
+        // First, search backwards, starting from the target, as that's more
+        // likely to have a limited graph to traverse.
+        // This also interacts well with the search ordering from
+        // search_for_path, which prefers edges closer to `None` when
+        // traversing.
+        search_for_path(&self.backward_audits, criteria_idx, Some(version), None).map_err(
+            |reachable_from_target| {
+                // The search failed, perform the search in the other direction
+                // in order to also get the set of nodes reachable from the
+                // root. We can `unwrap_err()` here, as we'll definitely fail.
+                let reachable_from_root =
+                    search_for_path(&self.forward_audits, criteria_idx, None, Some(version))
                         .unwrap_err();
                 SearchFailure {
                     reachable_from_root,
@@ -1575,7 +1598,7 @@ fn search_for_path(
     criteria_idx: usize,
     from_version: Option<&VetVersion>,
     to_version: Option<&VetVersion>,
-) -> Result<Caveats, SortedSet<Option<VetVersion>>> {
+) -> Result<Vec<DeltaEdgeOrigin>, SortedSet<Option<VetVersion>>> {
     // Search for any path through the graph with edges that satisfy criteria.
     // Finding any path validates that we satisfy that criteria.
     //
@@ -1594,18 +1617,26 @@ fn search_for_path(
     // fresh imports (the most-important caveat).
     struct Node<'a> {
         version: Option<&'a VetVersion>,
-        caveats: Caveats,
+        path: Vec<DeltaEdgeOrigin>,
+        needed_fresh_imports: bool,
+        needed_exemptions: bool,
     }
 
-    impl<'a> Node<'a> {
-        fn key(&self) -> Reverse<(bool, bool, bool)> {
+    impl Node<'_> {
+        fn key(&self) -> impl Ord + '_ {
             // Nodes are compared whether they require fresh imports or
             // exemptions, in that order. Fewer caveats makes the node sort
             // higher, as it will be stored in a max heap.
+            // Once we've sorted by all caveats, we sort by the length of the
+            // path (preferring short paths), the version (preferring lower
+            // versions), and then the most recently added DeltaEdgeOrigin
+            // (preferring more-local audits).
             Reverse((
-                self.caveats.needed_fresh_imports,
-                self.caveats.needed_exemptions,
-                self.caveats.directly_exempted,
+                self.needed_fresh_imports,
+                self.needed_exemptions,
+                self.version,
+                self.path.len(),
+                self.path.last(),
             ))
         }
     }
@@ -1629,11 +1660,19 @@ fn search_for_path(
     let mut queue = BinaryHeap::new();
     queue.push(Node {
         version: from_version,
-        caveats: Caveats::default(),
+        path: Vec::new(),
+        needed_fresh_imports: false,
+        needed_exemptions: false,
     });
 
     let mut visited = SortedSet::new();
-    while let Some(Node { version, caveats }) = queue.pop() {
+    while let Some(Node {
+        version,
+        path,
+        needed_fresh_imports,
+        needed_exemptions,
+    }) = queue.pop()
+    {
         // If We've been to a version before, We're not going to get a better
         // result revisiting it, as we visit the "best" edges first.
         if !visited.insert(version) {
@@ -1643,7 +1682,7 @@ fn search_for_path(
         // We found a path! Return a search result reflecting what we
         // discovered.
         if version == to_version {
-            return Ok(caveats);
+            return Ok(path);
         }
 
         // Apply deltas to move along to the next layer of the search, adding it
@@ -1659,23 +1698,12 @@ fn search_for_path(
                 continue;
             }
 
-            let mut edge_caveats = caveats.clone();
-            edge_caveats.directly_exempted = false;
-
-            match edge.origin {
-                DeltaEdgeOrigin::Exemption => {
-                    edge_caveats.needed_exemptions = true;
-                    edge_caveats.directly_exempted = true;
-                }
-                DeltaEdgeOrigin::FreshImportedAudit => {
-                    edge_caveats.needed_fresh_imports = true;
-                }
-                DeltaEdgeOrigin::StoredAudit => {}
-            }
-
             queue.push(Node {
                 version: edge.version,
-                caveats: edge_caveats,
+                path: path.iter().cloned().chain([edge.origin]).collect(),
+                needed_fresh_imports: needed_fresh_imports || edge.is_fresh_import,
+                needed_exemptions: needed_exemptions
+                    || matches!(edge.origin, DeltaEdgeOrigin::Exemption { .. }),
             });
         }
     }
@@ -2360,7 +2388,6 @@ pub fn regenerate_exemptions(
     cfg: &Config,
     store: &mut Store,
     allow_new_exemptions: bool,
-    force_update_imports: bool,
 ) -> Result<(), RegenerateExemptionsError> {
     // While minimizing exemptions, a number of different calls to the resolver
     // will be made using the same DepGraph, CriteriaMapper and requirements,
@@ -2382,10 +2409,9 @@ pub fn regenerate_exemptions(
     // immediately. We'll also do this if automatic exemption minimization is
     // disabled through the CLI.
     if !allow_new_exemptions {
-        let (_, conclusion) = resolve_audits(&graph, store, &criteria_mapper, &requirements);
+        let (results, conclusion) = resolve_audits(&graph, store, &criteria_mapper, &requirements);
         if !matches!(conclusion, Conclusion::Success(_)) || cfg.cli.no_minimize_exemptions {
-            store.imports =
-                store.get_updated_imports_file(&graph, &conclusion, force_update_imports);
+            store.imports = store.get_updated_imports_file(&graph, &results);
             return Ok(());
         }
     }
@@ -2443,16 +2469,14 @@ pub fn regenerate_exemptions(
                 // Also force an update even if we're seeing a violation
                 // conflict (to save the potentially-newly-imported violation),
                 // although our caller is unlikely to commit these changes.
-                store.imports =
-                    store.get_updated_imports_file(&graph, &conclusion, force_update_imports);
+                store.imports = store.get_updated_imports_file(&graph, &results);
                 return Err(RegenerateExemptionsError::ViolationConflict);
             }
             Conclusion::Success(..) => {
                 // We succeeded! Whatever exemptions we've recorded so-far are
                 // suficient, so we're done. Record any imports which ended up
                 // being required for the vet to pass.
-                store.imports =
-                    store.get_updated_imports_file(&graph, &conclusion, force_update_imports);
+                store.imports = store.get_updated_imports_file(&graph, &results);
                 return Ok(());
             }
         };
diff --git a/src/storage.rs b/src/storage.rs
index 6e61279..89c19c1 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -36,7 +36,7 @@ use crate::{
     },
     network::Network,
     out::{indeterminate_spinner, progress_bar, IncProgressOnDrop},
-    resolver::{self, Conclusion},
+    resolver,
     serialization::{parse_from_value, spanned::Spanned, to_formatted_toml},
     Config, PackageExt, PartialConfig, CARGO_ENV,
 };
@@ -254,31 +254,21 @@ impl Store {
     }
 
     /// Create a mock store, also mocking out the unlocked import fetching
-    /// process by providing the live values of imported AuditsFiles.
+    /// process by providing a mocked `Network` instance.
     #[cfg(test)]
     pub fn mock_online(
         config: ConfigFile,
         audits: AuditsFile,
         imports: ImportsFile,
-        fetched_audits: Vec<(ImportName, AuditsFile)>,
+        network: &Network,
         allow_criteria_changes: bool,
-    ) -> Result<Self, CriteriaChangeErrors> {
-        // For extra checking of import serialization, serialize the fetched
-        // audits, convert them to a ForeignAuditsFile, and then deserialize
-        // them back into an AuditsFile to ensure they round-trip through
-        // that process successfully, as the parsing codepaths are different.
-        for (_, original_file) in &fetched_audits {
-            let orig_toml = to_formatted_toml(original_file).unwrap().to_string();
-            let result = foreign_audit_file_to_local(toml::de::from_str(&orig_toml).unwrap());
-            assert_eq!(result.ignored_criteria, Vec::<String>::new());
-            assert_eq!(result.ignored_audits, Vec::<String>::new());
-            let new_toml = to_formatted_toml(&result.audit_file).unwrap().to_string();
-            assert_eq!(new_toml, orig_toml);
-        }
-
+    ) -> Result<Self, StoreAcquireError> {
+        let fetched_audits =
+            tokio::runtime::Handle::current().block_on(fetch_imported_audits(network, &config))?;
         let live_imports =
             process_imported_audits(fetched_audits, &config, &imports, allow_criteria_changes)?;
-        Ok(Self {
+
+        let store = Self {
             lock: None,
             config,
             imports,
@@ -287,7 +277,11 @@ impl Store {
             config_src: SourceFile::new_empty(CONFIG_TOML),
             audits_src: SourceFile::new_empty(AUDITS_TOML),
             imports_src: SourceFile::new_empty(IMPORTS_LOCK),
-        })
+        };
+
+        store.validate(false)?;
+
+        Ok(store)
     }
 
     #[cfg(test)]
@@ -593,74 +587,73 @@ impl Store {
     /// Return an updated version of the `imports.lock` file taking into account
     /// the result of running the resolver, to pick which audits need to be
     /// vendored.
-    ///
-    /// If `force_update` is specified, audits for all crates will be updated,
-    /// even if the currently vendored audits would be sufficient.
     #[must_use]
     pub fn get_updated_imports_file(
         &self,
         graph: &resolver::DepGraph<'_>,
-        conclusion: &resolver::Conclusion,
-        force_update: bool,
+        results: &[Option<resolver::ResolveResult>],
     ) -> ImportsFile {
-        if self.live_imports.is_none() {
+        let Some(live_imports) = &self.live_imports else {
             // We're locked, so can't update anything.
             return self.imports.clone();
-        }
-
-        // Determine which packages were used.
-        let mut used_packages = SortedMap::new();
-        for package in graph.nodes.iter() {
-            used_packages.insert(package.name, false);
-        }
-
-        // If we succeeded, also record which packages need fresh imports.
-        if let Conclusion::Success(success) = conclusion {
-            for &node_idx in &success.needed_fresh_imports {
-                let package = &graph.nodes[node_idx];
-                used_packages.insert(package.name, true);
-            }
-        }
+        };
 
         let mut new_imports = ImportsFile {
             audits: SortedMap::new(),
         };
-        for import_name in self.config.imports.keys() {
-            let live_audit_file = self
-                .imported_audits()
-                .get(import_name)
-                .expect("Live audits missing for import?");
-
-            // Is this the first time we're doing this import? If it is we'll
-            // want to effectively force-update this peer.
-            let first_import = !self.imports.audits.contains_key(import_name);
-
-            // Create the new audits file, and copy criteria into it on a
-            // per-package basis, only including used packages.
-            // We always use the live version of criteria in the new file.
+        for (import_index, (import_name, live_audits_file)) in
+            live_imports.audits.iter().enumerate()
+        {
             let mut new_audits_file = AuditsFile {
-                criteria: live_audit_file.criteria.clone(),
+                criteria: live_audits_file.criteria.clone(),
                 audits: SortedMap::new(),
             };
-            for (&package, &need_fresh_imports) in &used_packages {
-                // Always pull audit entries from the live version to pull in
-                // revocations, violations, updated notes, etc.
-                // Filter out entries which are fresh unless we're updating
-                // entries for this package or it's a violation.
-                let update_package = first_import || need_fresh_imports || force_update;
-                let audits = live_audit_file
+            for (pkgidx, result) in results.iter().enumerate() {
+                let package = &graph.nodes[pkgidx];
+
+                // Don't import audits for first-party packages.
+                if !package.is_third_party {
+                    continue;
+                }
+
+                // If the audit succeeded, get the set of required audits.
+                let required_edges = match result {
+                    Some(resolver::ResolveResult {
+                        required_edges: Some(required_edges),
+                        ..
+                    }) => Some(required_edges),
+                    _ => None,
+                };
+
+                // Filter the set of audits from the live audits file to
+                // only required audits.
+                let audits = live_audits_file
                     .audits
-                    .get(package)
+                    .get(package.name)
                     .map(|v| &v[..])
                     .unwrap_or(&[])
                     .iter()
-                    .filter(|audit| {
-                        update_package
-                            || !audit.is_fresh_import
-                            || matches!(audit.kind, AuditKind::Violation { .. })
+                    .enumerate()
+                    .filter(|&(audit_index, audit)| {
+                        // Always keep any violations.
+                        if matches!(audit.kind, AuditKind::Violation { .. }) {
+                            return true;
+                        }
+
+                        // If vetting succeeded for this package, keep
+                        // only the required edges, otherwise just keep
+                        // all existing imports for now.
+                        if let Some(required_edges) = required_edges {
+                            required_edges.contains(&resolver::DeltaEdgeOrigin::ImportedAudit {
+                                import_index,
+                                audit_index,
+                            })
+                        } else {
+                            !audit.is_fresh_import
+                        }
                     })
-                    .cloned()
-                    .map(|mut audit| {
+                    .map(|(_, audit)| {
+                        let mut audit = audit.clone();
                         audit.is_fresh_import = false;
                         audit
                     })
@@ -668,7 +661,9 @@ impl Store {
 
                 // Record audits in the new audits file, if there are any.
                 if !audits.is_empty() {
-                    new_audits_file.audits.insert(package.to_owned(), audits);
+                    new_audits_file
+                        .audits
+                        .insert(package.name.to_owned(), audits);
                 }
             }
             new_imports
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index d16a5b3..0f8f2d5 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -17,12 +17,6 @@ criteria = "safe-to-deploy"
 version = "0.3.66"
 notes = "I am the author of this crate."
 
-[[audits.bytecodealliance.audits.bumpalo]]
-who = "Nick Fitzgerald <fitzgen@gmail.com>"
-criteria = "safe-to-deploy"
-version = "3.9.1"
-notes = "I am the author of this crate."
-
 [[audits.bytecodealliance.audits.cc]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
@@ -84,158 +78,41 @@ criteria = "safe-to-deploy"
 version = "0.1.0"
 notes = "Inspected it and is a tiny crate with single safe macro"
 
-[[audits.embark.audits.webpki-roots]]
-who = "Johan Andersson <opensource@embark-studios.com>"
-criteria = "safe-to-deploy"
-version = "0.22.4"
-notes = "Inspected it to confirm that it only contains data definitions and no runtime code"
-
 [[audits.firefox.audits.autocfg]]
 who = "Josh Stone <jistone@redhat.com>"
 criteria = "safe-to-deploy"
 version = "1.1.0"
 notes = "All code written or reviewed by Josh Stone."
 
-[[audits.firefox.audits.bumpalo]]
-who = "Bobby Holley <bobbyholley@gmail.com>"
-criteria = "safe-to-run"
-delta = "3.9.1 -> 3.10.0"
-notes = """
-Some nontrivial functional changes but certainly meets the no-malware bar of
-safe-to-run. If we needed safe-to-deploy for this in m-c I'd ask Nick to re-
-certify this version, but we don't, so this is fine for now.
-"""
-
-[[audits.firefox.audits.bytes]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.1.0 -> 1.2.1"
-
-[[audits.firefox.audits.camino]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.9 -> 1.1.1"
-
 [[audits.firefox.audits.cargo_metadata]]
 who = "Jan-Erik Rediger <jrediger@mozilla.com>"
 criteria = "safe-to-deploy"
 version = "0.15.2"
 notes = "I reviewed the whole code base. Parser for the output of cargo-metadata, relying mostly on serde. No unsafe code used."
 
-[[audits.firefox.audits.clap_lex]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.2.0 -> 0.2.2"
-
-[[audits.firefox.audits.clap_lex]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.2.2 -> 0.2.4"
-
-[[audits.firefox.audits.either]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.6.1 -> 1.7.0"
-
-[[audits.firefox.audits.either]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.7.0 -> 1.8.0"
-
 [[audits.firefox.audits.encoding_rs]]
 who = "Henri Sivonen <hsivonen@hsivonen.fi>"
 criteria = "safe-to-deploy"
 version = "0.8.31"
 notes = "I, Henri Sivonen, wrote encoding_rs for Gecko and have reviewed contributions by others. There are two caveats to the certification: 1) The crate does things that are documented to be UB but that do not appear to actually be UB due to integer types differing from the general rule; https://github.com/hsivonen/encoding_rs/issues/79 . 2) It would be prudent to re-review the code that reinterprets buffers of integers as SIMD vectors; see https://github.com/hsivonen/encoding_rs/issues/87 ."
 
-[[audits.firefox.audits.fastrand]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.7.0 -> 1.8.0"
-
 [[audits.firefox.audits.fnv]]
 who = "Bobby Holley <bobbyholley@gmail.com>"
 criteria = "safe-to-deploy"
 version = "1.0.7"
 notes = "Simple hasher implementation with no unsafe code."
 
-[[audits.firefox.audits.futures-channel]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.3.21 -> 0.3.23"
-
-[[audits.firefox.audits.futures-core]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.3.21 -> 0.3.23"
-
-[[audits.firefox.audits.futures-sink]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.3.21 -> 0.3.23"
-
-[[audits.firefox.audits.futures-task]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.3.21 -> 0.3.23"
-
-[[audits.firefox.audits.futures-util]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.3.21 -> 0.3.23"
-
-[[audits.firefox.audits.getrandom]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.2.6 -> 0.2.7"
-
-[[audits.firefox.audits.h2]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.3.13 -> 0.3.14"
-
-[[audits.firefox.audits.hashbrown]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-version = "0.12.3"
-notes = "This version is used in rust's libstd, so effectively we're already trusting it"
-
 [[audits.firefox.audits.hex]]
 who = "Simon Friedberger <simon@mozilla.com>"
 criteria = "safe-to-deploy"
 version = "0.4.3"
 
-[[audits.firefox.audits.hyper]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.14.19 -> 0.14.20"
-
-[[audits.firefox.audits.indexmap]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.8.2 -> 1.9.1"
-
-[[audits.firefox.audits.itoa]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.2 -> 1.0.3"
-
-[[audits.firefox.audits.libc]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.2.126 -> 0.2.132"
-
 [[audits.firefox.audits.linked-hash-map]]
 who = "Aria Beingessner <a.beingessner@gmail.com>"
 criteria = "safe-to-deploy"
 version = "0.5.4"
 notes = "I own this crate (I am contain-rs) and 0.5.4 passes miri. This code is very old and used by lots of people, so I'm pretty confident in it, even though it's in maintenance-mode and missing some nice-to-have APIs."
 
-[[audits.firefox.audits.linked-hash-map]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.5.4 -> 0.5.6"
-
 [[audits.firefox.audits.log]]
 who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"
@@ -247,12 +124,6 @@ criteria = "safe-to-deploy"
 version = "0.1.9"
 notes = "This is a trivial crate."
 
-[[audits.firefox.audits.num-bigint]]
-who = "Josh Stone <jistone@redhat.com>"
-criteria = "safe-to-deploy"
-version = "0.2.6"
-notes = "All code written or reviewed by Josh Stone."
-
 [[audits.firefox.audits.num-bigint]]
 who = "Josh Stone <jistone@redhat.com>"
 criteria = "safe-to-deploy"
@@ -271,16 +142,6 @@ criteria = "safe-to-deploy"
 version = "0.2.15"
 notes = "All code written or reviewed by Josh Stone."
 
-[[audits.firefox.audits.once_cell]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.12.0 -> 1.13.1"
-
-[[audits.firefox.audits.os_str_bytes]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "6.1.0 -> 6.3.0"
-
 [[audits.firefox.audits.proc-macro2]]
 who = "Nika Layzell <nika@thelayzells.com>"
 criteria = "safe-to-deploy"
@@ -310,11 +171,6 @@ and is likely completely unused. Even when used, this API shouldn't be able to
 cause unsoundness.
 """
 
-[[audits.firefox.audits.proc-macro2]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.39 -> 1.0.43"
-
 [[audits.firefox.audits.quote]]
 who = "Nika Layzell <nika@thelayzells.com>"
 criteria = "safe-to-deploy"
@@ -330,154 +186,13 @@ read, is generally straightforward. I have audited the the quote macros, ident
 formatter, and runtime logic.
 """
 
-[[audits.firefox.audits.quote]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.18 -> 1.0.21"
-
-[[audits.firefox.audits.redox_syscall]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.2.13 -> 0.2.16"
-
-[[audits.firefox.audits.regex]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.5.6 -> 1.6.0"
-
-[[audits.firefox.audits.regex-syntax]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.6.26 -> 0.6.27"
-
 [[audits.firefox.audits.rustc-hash]]
 who = "Bobby Holley <bobbyholley@gmail.com>"
 criteria = "safe-to-deploy"
 version = "1.1.0"
 notes = "Straightforward crate with no unsafe code, does what it says on the tin."
 
-[[audits.firefox.audits.ryu]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.10 -> 1.0.11"
-
-[[audits.firefox.audits.semver]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.9 -> 1.0.10"
-
-[[audits.firefox.audits.semver]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.10 -> 1.0.13"
-
-[[audits.firefox.audits.serde]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.137 -> 1.0.143"
-
-[[audits.firefox.audits.serde]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.143 -> 1.0.144"
-
-[[audits.firefox.audits.serde_derive]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.137 -> 1.0.143"
-
-[[audits.firefox.audits.serde_derive]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.143 -> 1.0.144"
-
-[[audits.firefox.audits.serde_json]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.81 -> 1.0.83"
-
-[[audits.firefox.audits.serde_json]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.83 -> 1.0.85"
-
-[[audits.firefox.audits.serde_yaml]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.8.24 -> 0.8.26"
-
-[[audits.firefox.audits.slab]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.4.6 -> 0.4.7"
-
-[[audits.firefox.audits.smallvec]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.8.0 -> 1.9.0"
-
-[[audits.firefox.audits.syn]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.96 -> 1.0.99"
-
-[[audits.firefox.audits.thiserror]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.31 -> 1.0.32"
-
-[[audits.firefox.audits.thiserror-impl]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.31 -> 1.0.32"
-
-[[audits.firefox.audits.tower-service]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.3.1 -> 0.3.2"
-
-[[audits.firefox.audits.tracing]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.1.35 -> 0.1.36"
-
-[[audits.firefox.audits.tracing-attributes]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.1.21 -> 0.1.22"
-
-[[audits.firefox.audits.tracing-core]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-run"
-delta = "0.1.27 -> 0.1.29"
-
 [[audits.firefox.audits.typenum]]
 who = "Mike Hommey <mh+mozilla@glandium.org>"
 criteria = "safe-to-deploy"
 delta = "1.15.0 -> 1.16.0"
-
-[[audits.firefox.audits.unicode-ident]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.0 -> 1.0.1"
-
-[[audits.firefox.audits.unicode-ident]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "1.0.1 -> 1.0.3"
-
-[[audits.firefox.audits.unicode-normalization]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.1.19 -> 0.1.20"
-notes = "I am the author of most of these changes upstream, and prepared the release myself, at which point I looked at the other changes since 0.1.19."
-
-[[audits.firefox.audits.unicode-normalization]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.1.20 -> 0.1.21"
-
-[[audits.firefox.audits.unicode-normalization]]
-who = "Mike Hommey <mh+mozilla@glandium.org>"
-criteria = "safe-to-deploy"
-delta = "0.1.21 -> 0.1.22"


Test patch:
diff --git a/src/tests/import.rs b/src/tests/import.rs
index 1840d9d..e8069a9 100644
--- a/src/tests/import.rs
+++ b/src/tests/import.rs
@@ -1,13 +1,14 @@
+use crate::network::Network;
+
 use super::*;
 
 // Helper function for imports tests. Performs a vet and updates imports based
 // on it, returning a diff of the two.
-fn get_imports_file_changes(metadata: &Metadata, store: &Store, force_updates: bool) -> String {
+fn get_imports_file_changes(metadata: &Metadata, store: &Store) -> String {
     // Run the resolver before calling `get_imports_file` to compute the new
     // imports file.
     let report = crate::resolver::resolve(metadata, None, store);
-    let new_imports =
-        store.get_updated_imports_file(&report.graph, &report.conclusion, force_updates);
+    let new_imports = store.get_updated_imports_file(&report.graph, &report.results);
 
     // Format the old and new files as TOML, and write out a diff using `similar`.
     let old_imports = crate::serialization::to_formatted_toml(&store.imports)
@@ -24,9 +25,8 @@ fn get_imports_file_changes(metadata: &Metadata, store: &Store, force_updates: b
 
 #[test]
 fn new_peer_import() {
-    // (Pass) We import all audits for our third-party packages from a brand-new
-    // peer even though we are already fully audited. This won't force an import
-    // from existing peers.
+    // (Pass) We don't import any audits from a brand-new peer as we're fully
+    // audited, however we do add an entry to the table for it.
 
     let _enter = TEST_RUNTIME.enter();
     let mock = MockMetadata::simple();
@@ -39,7 +39,7 @@ fn new_peer_import() {
         audits: [
             (
                 "third-party2".to_owned(),
-                vec![delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY)],
+                vec![full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
             ),
             (
                 "unused-package".to_owned(),
@@ -59,7 +59,7 @@ fn new_peer_import() {
         criteria: SortedMap::new(),
         audits: [(
             "third-party2".to_owned(),
-            vec![delta_audit(ver(200), ver(300), SAFE_TO_DEPLOY)],
+            vec![full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
         )]
         .into_iter()
         .collect(),
@@ -85,19 +85,13 @@ fn new_peer_import() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![
-            (FOREIGN.to_owned(), new_foreign_audits),
-            (OTHER_FOREIGN.to_owned(), new_other_foreign_audits),
-        ],
-        true,
-    )
-    .unwrap();
-
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+    network.mock_serve_toml(OTHER_FOREIGN_URL, &new_other_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -122,7 +116,7 @@ fn existing_peer_skip_import() {
         audits: [
             (
                 "third-party2".to_owned(),
-                vec![delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY)],
+                vec![full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
             ),
             (
                 "unused-package".to_owned(),
@@ -145,16 +139,12 @@ fn existing_peer_skip_import() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -167,14 +157,21 @@ fn existing_peer_remove_unused() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
 
     let old_foreign_audits = AuditsFile {
         criteria: SortedMap::new(),
         audits: [
             (
                 "third-party2".to_owned(),
-                vec![delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY)],
+                vec![
+                    full_audit(ver(5), SAFE_TO_DEPLOY),
+                    delta_audit(ver(5), ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+                    delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY),
+                    full_audit(ver(10), SAFE_TO_RUN),
+                ],
             ),
             (
                 "unused-package".to_owned(),
@@ -199,16 +196,12 @@ fn existing_peer_remove_unused() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -239,7 +232,7 @@ fn existing_peer_import_delta_audit() {
         criteria: SortedMap::new(),
         audits: [
             // A new audit for third-party2 should fix our audit, so we should
-            // import all of these.
+            // import it, but not other useless audits.
             (
                 "third-party2".to_owned(),
                 vec![
@@ -266,7 +259,7 @@ fn existing_peer_import_delta_audit() {
 
     let new_other_foreign_audits = AuditsFile {
         criteria: SortedMap::new(),
-        // We'll also import unrelated audits from other sources.
+        // We won't import unrelated audits from other sources.
         audits: [(
             "third-party2".to_owned(),
             vec![delta_audit(ver(200), ver(300), SAFE_TO_DEPLOY)],
@@ -299,25 +292,19 @@ fn existing_peer_import_delta_audit() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![
-            (FOREIGN.to_owned(), new_foreign_audits),
-            (OTHER_FOREIGN.to_owned(), new_other_foreign_audits),
-        ],
-        true,
-    )
-    .unwrap();
-
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+    network.mock_serve_toml(OTHER_FOREIGN_URL, &new_other_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
 #[test]
 fn existing_peer_import_custom_criteria() {
-    // (Pass) We'll immediately import criteria changes wen unlocked, even if
+    // (Pass) We'll immediately import criteria changes when unlocked, even if
     // our peer hasn't changed or we aren't mapping them locally. This doesn't
     // force an import of other crates.
 
@@ -325,7 +312,10 @@ fn existing_peer_import_custom_criteria() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
+
     let old_foreign_audits = AuditsFile {
         criteria: SortedMap::new(),
         audits: [(
@@ -363,16 +353,12 @@ fn existing_peer_import_custom_criteria() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
 
     insta::assert_snapshot!(output);
 }
@@ -386,7 +372,10 @@ fn new_audit_for_unused_criteria_basic() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
+
     let old_foreign_audits = AuditsFile {
         criteria: [("fuzzed".to_string(), criteria("fuzzed"))]
             .into_iter()
@@ -418,16 +407,12 @@ fn new_audit_for_unused_criteria_basic() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
 
     insta::assert_snapshot!(output);
 }
@@ -441,7 +426,10 @@ fn new_audit_for_unused_criteria_transitive() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party1");
+
     let old_foreign_audits = AuditsFile {
         criteria: [("fuzzed".to_string(), criteria("fuzzed"))]
             .into_iter()
@@ -477,16 +465,12 @@ fn new_audit_for_unused_criteria_transitive() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
 
     insta::assert_snapshot!(output);
 }
@@ -494,19 +478,21 @@ fn new_audit_for_unused_criteria_transitive() {
 #[test]
 fn existing_peer_revoked_audit() {
     // (Pass) If a previously-imported audit is removed, we should also remove
-    // it locally, even if we don't use it.
+    // it locally, even if doing so would cause vet to fail.
 
     let _enter = TEST_RUNTIME.enter();
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
 
     let old_foreign_audits = AuditsFile {
         criteria: SortedMap::new(),
         audits: [(
             "third-party2".to_owned(),
-            vec![delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY)],
+            vec![full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
         )]
         .into_iter()
         .collect(),
@@ -529,16 +515,12 @@ fn existing_peer_revoked_audit() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -552,13 +534,15 @@ fn existing_peer_add_violation() {
     let mock = MockMetadata::simple();
 
     let metadata = mock.metadata();
-    let (mut config, audits, mut imports) = builtin_files_full_audited(&metadata);
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
 
     let old_foreign_audits = AuditsFile {
         criteria: SortedMap::new(),
         audits: [(
             "third-party2".to_owned(),
-            vec![delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY)],
+            vec![full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
         )]
         .into_iter()
         .collect(),
@@ -569,8 +553,8 @@ fn existing_peer_add_violation() {
         audits: [(
             "third-party2".to_owned(),
             vec![
-                delta_audit(ver(100), ver(200), SAFE_TO_DEPLOY),
-                delta_audit(ver(200), ver(300), SAFE_TO_DEPLOY),
+                full_audit(ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+                delta_audit(ver(DEFAULT_VER), ver(20), SAFE_TO_DEPLOY),
                 violation(VersionReq::parse("99.*").unwrap(), SAFE_TO_DEPLOY),
             ],
         )]
@@ -590,16 +574,12 @@ fn existing_peer_add_violation() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -641,16 +621,12 @@ fn peer_audits_exemption_no_minimize() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -692,19 +668,15 @@ fn peer_audits_exemption_minimize() {
         },
     );
 
-    let mut store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let mut store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
     // Capture the old imports before minimizing exemptions
     let old = store.mock_commit();
 
-    crate::resolver::regenerate_exemptions(&mock_cfg(&metadata), &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&mock_cfg(&metadata), &mut store, true).unwrap();
 
     // Capture after minimizing exemptions, and generate a diff.
     let new = store.mock_commit();
@@ -762,14 +734,10 @@ fn peer_audits_import_exclusion() {
         },
     );
 
-    let store = Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        true,
-    )
-    .unwrap();
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
 
     let imported = store
         .imported_audits()
@@ -789,7 +757,7 @@ fn peer_audits_import_exclusion() {
         "The `transitive-third-party1` crate should still be present in `imported_audits`"
     );
 
-    let output = get_imports_file_changes(&metadata, &store, false);
+    let output = get_imports_file_changes(&metadata, &store);
     insta::assert_snapshot!(output);
 }
 
@@ -846,19 +814,190 @@ fn existing_peer_updated_description() {
         },
     );
 
-    let error = match Store::mock_online(
-        config,
-        audits,
-        imports,
-        vec![(FOREIGN.to_owned(), new_foreign_audits)],
-        false,
-    ) {
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let error = match Store::mock_online(config, audits, imports, &network, false) {
         Ok(_) => panic!("expected store creation to fail due to updated criteria"),
         Err(err) => miette::Report::from(err),
     };
     insta::assert_snapshot!("existing_peer_updated_description", format!("{error:?}"));
 }
 
+#[test]
+fn fresh_import_preferred_audits() {
+    // (Pass) We prefer shorter audit chains over longer ones.
+
+    let _enter = TEST_RUNTIME.enter();
+    let mock = MockMetadata::simple();
+
+    let metadata = mock.metadata();
+    let (mut config, mut audits, imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
+
+    let new_foreign_audits = AuditsFile {
+        criteria: SortedMap::new(),
+        audits: [(
+            "third-party2".to_owned(),
+            vec![
+                full_audit(ver(5), SAFE_TO_DEPLOY),
+                delta_audit(ver(5), ver(6), SAFE_TO_DEPLOY),
+                delta_audit(ver(6), ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+            ],
+        )]
+        .into_iter()
+        .collect(),
+    };
+
+    let new_other_foreign_audits = AuditsFile {
+        criteria: SortedMap::new(),
+        audits: [(
+            "third-party2".to_owned(),
+            vec![delta_audit(ver(5), ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
+        )]
+        .into_iter()
+        .collect(),
+    };
+
+    config.imports.insert(
+        FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: FOREIGN_URL.to_owned(),
+            ..Default::default()
+        },
+    );
+    config.imports.insert(
+        OTHER_FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: OTHER_FOREIGN_URL.to_owned(),
+            ..Default::default()
+        },
+    );
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+    network.mock_serve_toml(OTHER_FOREIGN_URL, &new_other_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
+    insta::assert_snapshot!(output);
+}
+
+#[test]
+fn old_import_preferred_audits() {
+    // (Pass) we don't switch to a shorter audit path if we already have a longer one imported.
+
+    let _enter = TEST_RUNTIME.enter();
+    let mock = MockMetadata::simple();
+
+    let metadata = mock.metadata();
+    let (mut config, mut audits, mut imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
+
+    let old_foreign_audits = AuditsFile {
+        criteria: SortedMap::new(),
+        audits: [(
+            "third-party2".to_owned(),
+            vec![
+                full_audit(ver(5), SAFE_TO_DEPLOY),
+                delta_audit(ver(5), ver(6), SAFE_TO_DEPLOY),
+                delta_audit(ver(6), ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+            ],
+        )]
+        .into_iter()
+        .collect(),
+    };
+
+    let new_foreign_audits = old_foreign_audits.clone();
+
+    let new_other_foreign_audits = AuditsFile {
+        criteria: SortedMap::new(),
+        audits: [(
+            "third-party2".to_owned(),
+            vec![delta_audit(ver(5), ver(DEFAULT_VER), SAFE_TO_DEPLOY)],
+        )]
+        .into_iter()
+        .collect(),
+    };
+
+    imports
+        .audits
+        .insert(FOREIGN.to_owned(), old_foreign_audits);
+
+    config.imports.insert(
+        FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: FOREIGN_URL.to_owned(),
+            ..Default::default()
+        },
+    );
+    config.imports.insert(
+        OTHER_FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: OTHER_FOREIGN_URL.to_owned(),
+            ..Default::default()
+        },
+    );
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+    network.mock_serve_toml(OTHER_FOREIGN_URL, &new_other_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
+    insta::assert_snapshot!(output);
+}
+
+#[test]
+fn equal_length_preferred_audits() {
+    // (Pass) Between two audit paths of the same length, we prefer one
+    // arbitrarily (the output of this test documents our preference, which is
+    // based on how quickly the edges approach the start node).
+
+    let _enter = TEST_RUNTIME.enter();
+    let mock = MockMetadata::simple();
+
+    let metadata = mock.metadata();
+    let (mut config, mut audits, imports) = builtin_files_full_audited(&metadata);
+
+    audits.audits.remove("third-party2");
+
+    let new_foreign_audits = AuditsFile {
+        criteria: SortedMap::new(),
+        audits: [(
+            "third-party2".to_owned(),
+            vec![
+                full_audit(ver(2), SAFE_TO_DEPLOY),
+                full_audit(ver(8), SAFE_TO_DEPLOY),
+                delta_audit(ver(2), ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+                delta_audit(ver(8), ver(DEFAULT_VER), SAFE_TO_DEPLOY),
+            ],
+        )]
+        .into_iter()
+        .collect(),
+    };
+
+    config.imports.insert(
+        FOREIGN.to_owned(),
+        crate::format::RemoteImport {
+            url: FOREIGN_URL.to_owned(),
+            ..Default::default()
+        },
+    );
+
+    let mut network = Network::new_mock();
+    network.mock_serve_toml(FOREIGN_URL, &new_foreign_audits);
+
+    let store = Store::mock_online(config, audits, imports, &network, true).unwrap();
+
+    let output = get_imports_file_changes(&metadata, &store);
+    insta::assert_snapshot!(output);
+}
+
 #[test]
 fn foreign_audit_file_to_local() {
     let _enter = TEST_RUNTIME.enter();
@@ -997,7 +1136,3 @@ fn foreign_audit_file_to_local() {
             .to_string()
     );
 }
-
-// Other tests worth adding:
-//
-// - used edges with dependency-criteria should cause criteria to be imported
diff --git a/src/tests/regenerate_unaudited.rs b/src/tests/regenerate_unaudited.rs
index 0c3db89..475b7b6 100644
--- a/src/tests/regenerate_unaudited.rs
+++ b/src/tests/regenerate_unaudited.rs
@@ -29,7 +29,7 @@ fn builtin_simple_exemptions_not_a_real_dep_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-not-a-real-dep-regenerate", exemptions);
@@ -55,7 +55,7 @@ fn builtin_simple_deps_exemptions_overbroad_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-unaudited-overbroad-regenerate", exemptions);
@@ -84,7 +84,7 @@ fn builtin_complex_exemptions_twins_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-unaudited-twins-regenerate", exemptions);
@@ -113,7 +113,7 @@ fn builtin_complex_exemptions_partial_twins_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -149,7 +149,7 @@ fn builtin_simple_exemptions_in_delta_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-unaudited-in-delta-regenerate", exemptions);
@@ -182,7 +182,7 @@ fn builtin_simple_exemptions_in_full_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-unaudited-in-full-regenerate", exemptions);
@@ -213,7 +213,7 @@ fn builtin_simple_deps_exemptions_adds_uneeded_criteria_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -249,7 +249,7 @@ fn builtin_dev_detection_exemptions_adds_uneeded_criteria_indirect_regenerate()
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -281,7 +281,7 @@ fn builtin_simple_exemptions_extra_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-unaudited-extra-regenerate", exemptions);
@@ -310,7 +310,7 @@ fn builtin_simple_exemptions_in_direct_full_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -361,7 +361,7 @@ fn builtin_simple_exemptions_nested_weaker_req_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -417,7 +417,7 @@ fn builtin_simple_exemptions_nested_stronger_req_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -446,7 +446,7 @@ fn builtin_simple_audit_as_default_root_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -479,7 +479,7 @@ fn builtin_simple_audit_as_weaker_root_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-audit-as-weaker-root-regenerate", exemptions);
@@ -507,7 +507,7 @@ fn builtin_simple_exemptions_larger_diff_regenerate() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -567,7 +567,7 @@ fn builtin_simple_exemptions_broaden_basic() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-exemptions-broaden-basic", exemptions);
@@ -620,7 +620,7 @@ fn builtin_simple_exemptions_regenerate_merge() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, true, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, true).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!("builtin-simple-exemptions-regenerate-merge", exemptions);
@@ -673,7 +673,7 @@ fn builtin_simple_exemptions_regenerate_merge_nonew() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, false, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, false).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
@@ -713,7 +713,7 @@ fn builtin_simple_exemptions_regenerate_nonew_failed() {
 
     let mut store = Store::mock(config, audits, imports);
     let cfg = mock_cfg(&metadata);
-    crate::resolver::regenerate_exemptions(&cfg, &mut store, false, false).unwrap();
+    crate::resolver::regenerate_exemptions(&cfg, &mut store, false).unwrap();
 
     let exemptions = get_exemptions(&store);
     insta::assert_snapshot!(
diff --git a/src/tests/snapshots/cargo_vet__tests__import__equal_length_preferred_audits.snap b/src/tests/snapshots/cargo_vet__tests__import__equal_length_preferred_audits.snap
new file mode 100644
index 0000000..59039db
--- /dev/null
+++ b/src/tests/snapshots/cargo_vet__tests__import__equal_length_preferred_audits.snap
@@ -0,0 +1,14 @@
+---
+source: src/tests/import.rs
+expression: output
+---
+ 
+-[audits]
++[[audits.peer-company.audits.third-party2]]
++criteria = "safe-to-deploy"
++version = "2.0.0"
++
++[[audits.peer-company.audits.third-party2]]
++criteria = "safe-to-deploy"
++delta = "2.0.0 -> 10.0.0"
+
diff --git a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_add_violation.snap b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_add_violation.snap
index 873f958..38410c4 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_add_violation.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_add_violation.snap
@@ -5,7 +5,7 @@ expression: output
  
  [[audits.peer-company.audits.third-party2]]
  criteria = "safe-to-deploy"
- delta = "100.0.0 -> 200.0.0"
+ version = "10.0.0"
 +
 +[[audits.peer-company.audits.third-party2]]
 +criteria = "safe-to-deploy"
diff --git a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_import_delta_audit.snap b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_import_delta_audit.snap
index 4574e73..a9f35af 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_import_delta_audit.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_import_delta_audit.snap
@@ -7,16 +7,9 @@ expression: output
  criteria = "safe-to-deploy"
  version = "9.0.0"
  
--[audits.rival-company.audits]
 +[[audits.peer-company.audits.third-party2]]
 +criteria = "safe-to-deploy"
 +delta = "9.0.0 -> 10.0.0"
 +
-+[[audits.peer-company.audits.third-party2]]
-+criteria = "safe-to-deploy"
-+delta = "100.0.0 -> 200.0.0"
-+
-+[[audits.rival-company.audits.third-party2]]
-+criteria = "safe-to-deploy"
-+delta = "200.0.0 -> 300.0.0"
+ [audits.rival-company.audits]
 
diff --git a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_remove_unused.snap b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_remove_unused.snap
index ab84c69..30ed274 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_remove_unused.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_remove_unused.snap
@@ -5,7 +5,19 @@ expression: output
  
  [[audits.peer-company.audits.third-party2]]
  criteria = "safe-to-deploy"
- delta = "100.0.0 -> 200.0.0"
+ version = "5.0.0"
+ 
+ [[audits.peer-company.audits.third-party2]]
+ criteria = "safe-to-deploy"
+ delta = "5.0.0 -> 10.0.0"
+-
+-[[audits.peer-company.audits.third-party2]]
+-criteria = "safe-to-deploy"
+-delta = "100.0.0 -> 200.0.0"
+-
+-[[audits.peer-company.audits.third-party2]]
+-criteria = "safe-to-run"
+-version = "10.0.0"
 -
 -[[audits.peer-company.audits.unused-package]]
 -criteria = "safe-to-deploy"
diff --git a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_revoked_audit.snap b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_revoked_audit.snap
index cc9e5db..551cef4 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__existing_peer_revoked_audit.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__existing_peer_revoked_audit.snap
@@ -5,6 +5,6 @@ expression: output
  
 -[[audits.peer-company.audits.third-party2]]
 -criteria = "safe-to-deploy"
--delta = "100.0.0 -> 200.0.0"
+-version = "10.0.0"
 +[audits.peer-company.audits]
 
diff --git a/src/tests/snapshots/cargo_vet__tests__import__fresh_import_preferred_audits.snap b/src/tests/snapshots/cargo_vet__tests__import__fresh_import_preferred_audits.snap
new file mode 100644
index 0000000..c37d264
--- /dev/null
+++ b/src/tests/snapshots/cargo_vet__tests__import__fresh_import_preferred_audits.snap
@@ -0,0 +1,14 @@
+---
+source: src/tests/import.rs
+expression: output
+---
+ 
+-[audits]
++[[audits.peer-company.audits.third-party2]]
++criteria = "safe-to-deploy"
++version = "5.0.0"
++
++[[audits.rival-company.audits.third-party2]]
++criteria = "safe-to-deploy"
++delta = "5.0.0 -> 10.0.0"
+
diff --git a/src/tests/snapshots/cargo_vet__tests__import__new_peer_import.snap b/src/tests/snapshots/cargo_vet__tests__import__new_peer_import.snap
index 43f221e..e030896 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__new_peer_import.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__new_peer_import.snap
@@ -3,9 +3,7 @@ source: src/tests/import.rs
 expression: output
 ---
  
-+[[audits.peer-company.audits.third-party2]]
-+criteria = "safe-to-deploy"
-+delta = "100.0.0 -> 200.0.0"
++[audits.peer-company.audits]
 +
  [audits.rival-company.audits]
 
diff --git a/src/tests/snapshots/cargo_vet__tests__import__old_import_preferred_audits.snap b/src/tests/snapshots/cargo_vet__tests__import__old_import_preferred_audits.snap
new file mode 100644
index 0000000..abaee7c
--- /dev/null
+++ b/src/tests/snapshots/cargo_vet__tests__import__old_import_preferred_audits.snap
@@ -0,0 +1,19 @@
+---
+source: src/tests/import.rs
+expression: output
+---
+ 
+ [[audits.peer-company.audits.third-party2]]
+ criteria = "safe-to-deploy"
+ version = "5.0.0"
+ 
+ [[audits.peer-company.audits.third-party2]]
+ criteria = "safe-to-deploy"
+ delta = "5.0.0 -> 6.0.0"
+ 
+ [[audits.peer-company.audits.third-party2]]
+ criteria = "safe-to-deploy"
+ delta = "6.0.0 -> 10.0.0"
++
++[audits.rival-company.audits]
+