Repository: mozilla/cargo-vet (rust)

Issue:
Don't warn about expired/expiring wildcard audits if the crate hasn't been updated in a while
Right now cargo-vet on mozilla-central produces the following:

```
Vetting Succeeded (385 fully audited, 75 partially audited, 56 exempted)
 WARN Your audit set contains wildcard audits for bhttp, cexpr, ohttp, dogear, and 17 others which have expired.
 WARN Your audit set contains wildcard audits for qcms, glean, mozdevice, and 6 others which expire within the next six weeks.
 WARN   Consider running `cargo vet renew --expiring` or adding `renew = false` to the wildcard entries in audits.toml.
```

One thing I noticed while trying to refresh them is that many of the crates haven't seen updates in quite some time. Conceptually, the point of wildcard audits is to ensure that future versions of a crate developed by the same person can be automatically covered. However, for crates that are not under active development, a wildcard audit becomes an ongoing maintenance hassle due to the need to renew. You don't quite want to mark them as `renew = false` (since that's something you'd do if the crate transferred ownership or the owner left the project), but it's also annoying to keep renewing.

A simple heuristic would be to skip the warning if the crate hasn't been updated in the last, say, 4 months. I suspect this would be trivial to do. @mystor WDYT?



Pull request description:
 Don't prompt to renew expiring wildcard audits for inactive crates
This silences the wildcard audit renewal warning for crates which have not been updated in at least 4 months, and haven't been updated since the wildcard audit has expired.

Fixes #647

Patch:
diff --git a/src/git_tool.rs b/src/git_tool.rs
index 344d8b7..11d1e84 100644
--- a/src/git_tool.rs
+++ b/src/git_tool.rs
@@ -136,7 +136,7 @@ pub struct Editor<'a> {
     run_editor: Box<dyn FnOnce(&Path) -> io::Result<bool> + 'a>,
 }
 
-impl<'a> Editor<'a> {
+impl Editor<'_> {
     /// Create a new editor for a temporary file.
     pub fn new(name: &str) -> io::Result<Self> {
         let tempfile = tempfile::Builder::new()
@@ -174,12 +174,6 @@ impl<'a> Editor<'a> {
         );
     }
 
-    #[cfg(test)]
-    /// Test-only method to mock out the actual invocation of the editor.
-    pub fn set_run_editor(&mut self, run_editor: impl FnOnce(&Path) -> io::Result<bool> + 'a) {
-        self.run_editor = Box::new(run_editor);
-    }
-
     /// Add comment lines to the editor. Any newlines in the input will be
     /// normalized to the current platform, and a comment character will be
     /// added.
@@ -260,6 +254,14 @@ impl<'a> Editor<'a> {
     }
 }
 
+#[cfg(test)]
+impl<'a> Editor<'a> {
+    /// Test-only method to mock out the actual invocation of the editor.
+    pub fn set_run_editor(&mut self, run_editor: impl FnOnce(&Path) -> io::Result<bool> + 'a) {
+        self.run_editor = Box::new(run_editor);
+    }
+}
+
 /// Read the git configuration to determine the value for GIT_EDITOR.
 fn git_pager() -> Option<String> {
     let output = Command::new("git")
diff --git a/src/main.rs b/src/main.rs
index 03b1ff4..d908a1a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -155,6 +155,8 @@ const DURATION_DAY: Duration = Duration::from_secs(60 * 60 * 24);
 
 lazy_static! {
     static ref WILDCARD_AUDIT_EXPIRATION_DURATION: chrono::Duration = chrono::Duration::weeks(6);
+    static ref WILDCARD_AUDIT_INACTIVE_CRATE_DURATION: chrono::Duration =
+        chrono::Duration::weeks(16);
 }
 /// This string is always used in a context such as "in the next {STR}".
 const WILDCARD_AUDIT_EXPIRATION_STRING: &str = "six weeks";
@@ -1880,7 +1882,7 @@ fn do_cmd_renew(out: &Arc<dyn Out>, cfg: &Config, store: &mut Store, sub_args: &
     } else {
         // Find and update all expiring crates.
         assert!(sub_args.expiring);
-        renewing = WildcardAuditRenewal::expiring(cfg, store);
+        renewing = WildcardAuditRenewal::expiring(cfg, store, false);
 
         if renewing.is_empty() {
             info!("no wildcard audits that are eligible for renewal have expired or are expiring in the next {WILDCARD_AUDIT_EXPIRATION_STRING}");
@@ -2268,7 +2270,7 @@ fn cmd_check(
             }
 
             // Warn about wildcard audits which will be expiring soon or have expired.
-            let expiry = WildcardAuditRenewal::expiring(cfg, &mut store);
+            let expiry = WildcardAuditRenewal::expiring(cfg, &mut store, true);
 
             if !expiry.is_empty() {
                 let expired = expiry.expired_crates();
@@ -2305,17 +2307,42 @@ impl<'a> WildcardAuditRenewal<'a> {
     ///
     /// This function _does not_ modify the store, but since the mutable references to the entries
     /// are stored (for potential use by `renew`), it must take a mutable Store.
-    pub fn expiring(cfg: &Config, store: &'a mut Store) -> Self {
+    pub fn expiring(cfg: &Config, store: &'a mut Store, ignore_inactive: bool) -> Self {
         let expire_date = cfg.today() + *WILDCARD_AUDIT_EXPIRATION_DURATION;
 
         let mut crates: SortedMap<PackageStr<'a>, Vec<(&'a mut WildcardEntry, bool)>> =
             Default::default();
         for (name, audits) in store.audits.wildcard_audits.iter_mut() {
+            // Get the most recent publication time for this crate on crates.io,
+            // which will be used to avoid expiry warnings for inactive crates.
+            let last_publish_date = store
+                .live_imports
+                .as_ref()
+                .and_then(|imports| imports.publisher.get(name))
+                .map(|publishers| &publishers[..])
+                .unwrap_or(&[])
+                .iter()
+                .map(|p| p.when)
+                .max()
+                .unwrap_or(cfg.today());
+
             // Check whether there are any audits expiring by the expiration date. Of those
             // audits, check whether all of them are already expired (to change the warning
             // message to be more informative).
             for entry in audits.iter_mut().filter(|e| e.should_renew(expire_date)) {
                 let expired = entry.should_renew(cfg.today());
+
+                // If the crate has not been published since the wildcard audit
+                // expired, and the last published version by that user is over
+                // 4 months ago, we silence the expiring/expired renewal
+                // warning.
+                if ignore_inactive
+                    && last_publish_date < *entry.end
+                    && last_publish_date < cfg.today() - *WILDCARD_AUDIT_INACTIVE_CRATE_DURATION
+                {
+                    continue;
+                }
+
                 crates.entry(name).or_default().push((entry, expired));
             }
         }


Test patch:
diff --git a/src/tests/audit_as_crates_io.rs b/src/tests/audit_as_crates_io.rs
index 85757ba..80604ba 100644
--- a/src/tests/audit_as_crates_io.rs
+++ b/src/tests/audit_as_crates_io.rs
@@ -25,7 +25,11 @@ fn build_registry(network: &mut Network, extra_packages: &[&str]) {
                 }),
                 repository: None,
             },
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-01-01")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_months_ago(12),
+            )],
         );
     }
     registry.serve(network);
diff --git a/src/tests/certify.rs b/src/tests/certify.rs
index 40e47fe..949e1ff 100644
--- a/src/tests/certify.rs
+++ b/src/tests/certify.rs
@@ -619,7 +619,7 @@ fn mock_wildcard_certify_flow() {
         |_| Ok("\n".to_owned()),
         |_| {
             Ok("\
-            I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-12 and 2024-01-01 will satisfy the above criteria.\n\
+            I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-18 and 2024-01-01 will satisfy the above criteria.\n\
             \n\
             These are testing notes. They contain some\n\
             newlines. Trailing whitespace        \n    \
@@ -655,8 +655,8 @@ fn mock_wildcard_certify_flow() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(9), Some(5), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(9), Some(5), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
@@ -709,9 +709,9 @@ fn mock_trust_flow_simple() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(2), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(2), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
@@ -764,9 +764,9 @@ fn mock_trust_flow_ambiguous() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(5), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(5), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
@@ -808,9 +808,9 @@ fn mock_trust_flow_explicit() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(5), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(5), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
@@ -863,22 +863,26 @@ fn mock_trust_flow_all() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(5), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(5), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .package(
             "transitive-third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(2), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(2), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .package(
             "third-party2",
-            &[reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(2),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
@@ -940,22 +944,26 @@ fn mock_trust_flow_all_allow_multiple() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(5), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(5), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .package(
             "transitive-third-party1",
             &[
-                reg_published_by(ver(1), None, "2022-10-12"),
-                reg_published_by(ver(9), Some(2), "2022-10-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
+                reg_published_by(ver(1), None, mock_weeks_ago(10)),
+                reg_published_by(ver(9), Some(2), mock_weeks_ago(10)),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
             ],
         )
         .package(
             "third-party2",
-            &[reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(2),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
diff --git a/src/tests/import.rs b/src/tests/import.rs
index bc1de9a..8e5b9dd 100644
--- a/src/tests/import.rs
+++ b/src/tests/import.rs
@@ -193,7 +193,11 @@ fn existing_peer_skip_import() {
         .user(1, "user1", "User One")
         .package(
             "third-party2",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
@@ -1650,15 +1654,15 @@ fn import_wildcard_audit_publisher() {
         .package(
             "third-party1",
             &[
-                reg_published_by(ver(DEFAULT_VER), Some(2), "2022-12-12"),
-                reg_published_by(ver(5), Some(2), "2022-12-12"),
+                reg_published_by(ver(DEFAULT_VER), Some(2), mock_weeks_ago(2)),
+                reg_published_by(ver(5), Some(2), mock_weeks_ago(2)),
             ],
         )
         .package(
             "third-party2",
             &[
-                reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12"),
-                reg_published_by(ver(5), Some(2), "2022-12-12"),
+                reg_published_by(ver(DEFAULT_VER), Some(1), mock_weeks_ago(2)),
+                reg_published_by(ver(5), Some(2), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
@@ -1983,7 +1987,11 @@ fn existing_import_kept_despite_local_wildcard_audit() {
         .user(1, "user1", "User One")
         .package(
             "third-party2",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-15")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
     network.mock_serve_toml(FOREIGN_URL, &foreign_audits);
@@ -2044,7 +2052,11 @@ fn local_wildcard_audit_preferred_to_fresh_import() {
         .user(1, "user1", "User One")
         .package(
             "third-party2",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-15")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
     network.mock_serve_toml(FOREIGN_URL, &foreign_audits);
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 1bd26ce..dcb3570 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -264,8 +264,8 @@ fn wildcard_audit(user_id: u64, criteria: CriteriaStr) -> WildcardEntry {
         notes: None,
         criteria: vec![criteria.to_string().into()],
         user_id,
-        start: chrono::NaiveDate::from_ymd_opt(2022, 12, 1).unwrap().into(),
-        end: chrono::NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().into(),
+        start: mock_months_ago(1).date_naive().into(),
+        end: mock_today().into(),
         renew: None,
         aggregated_from: vec![],
         is_fresh_import: false,
@@ -281,8 +281,8 @@ fn wildcard_audit_m(
         notes: None,
         criteria: criteria.into_iter().map(|s| s.into().into()).collect(),
         user_id,
-        start: chrono::NaiveDate::from_ymd_opt(2022, 12, 1).unwrap().into(),
-        end: chrono::NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().into(),
+        start: mock_months_ago(1).date_naive().into(),
+        end: mock_today().into(),
         renew: None,
         aggregated_from: vec![],
         is_fresh_import: false,
@@ -294,8 +294,8 @@ fn trusted_entry(user_id: u64, criteria: CriteriaStr) -> TrustEntry {
         notes: None,
         criteria: vec![criteria.to_string().into()],
         user_id,
-        start: chrono::NaiveDate::from_ymd_opt(2022, 12, 1).unwrap().into(),
-        end: chrono::NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().into(),
+        start: mock_months_ago(1).date_naive().into(),
+        end: mock_today().into(),
         aggregated_from: vec![],
     }
 }
@@ -303,7 +303,7 @@ fn trusted_entry(user_id: u64, criteria: CriteriaStr) -> TrustEntry {
 fn publisher_entry(version: VetVersion, user_id: u64) -> CratesPublisher {
     CratesPublisher {
         version,
-        when: chrono::NaiveDate::from_ymd_opt(2022, 12, 15).unwrap(),
+        when: mock_weeks_ago(2).date_naive(),
         user_id,
         user_login: format!("user{user_id}"),
         user_name: None,
@@ -319,7 +319,7 @@ fn publisher_entry_named(
 ) -> CratesPublisher {
     CratesPublisher {
         version,
-        when: chrono::NaiveDate::from_ymd_opt(2022, 12, 15).unwrap(),
+        when: mock_weeks_ago(2).date_naive(),
         user_id,
         user_login: login.to_owned(),
         user_name: Some(name.to_owned()),
@@ -1078,7 +1078,17 @@ fn mock_now() -> chrono::DateTime<chrono::Utc> {
     )
 }
 
-/// Returns a fixed datetime that should be considered `today`: 2023-01-01.
+/// Returns a fixed datetime that is `months` months ago relative to `mock_now()`
+fn mock_months_ago(months: u32) -> chrono::DateTime<chrono::Utc> {
+    mock_now() - chrono::Months::new(months)
+}
+
+/// Returns a fixed datetime that is `weeks` weeks ago relative to `mock_now()`
+fn mock_weeks_ago(weeks: i64) -> chrono::DateTime<chrono::Utc> {
+    mock_now() - chrono::Duration::weeks(weeks)
+}
+
+/// Returns a fixed date that should be considered `today`: 2023-01-01.
 ///
 /// This is derived from `mock_now()`.
 fn mock_today() -> chrono::NaiveDate {
@@ -1256,7 +1266,7 @@ struct MockRegistryVersion {
 fn reg_published_by(
     version: VetVersion,
     published_by: Option<CratesUserId>,
-    when: &str,
+    when: chrono::DateTime<chrono::Utc>,
 ) -> MockRegistryVersion {
     assert!(
         version.git_rev.is_none(),
@@ -1265,13 +1275,7 @@ fn reg_published_by(
     MockRegistryVersion {
         version: version.semver,
         published_by,
-        created_at: chrono::DateTime::from_utc(
-            chrono::NaiveDateTime::new(
-                when.parse::<chrono::NaiveDate>().unwrap(),
-                chrono::NaiveTime::from_hms_opt(12, 0, 0).unwrap(),
-            ),
-            chrono::Utc,
-        ),
+        created_at: when,
     }
 }
 
diff --git a/src/tests/renew.rs b/src/tests/renew.rs
index f5de6c7..2103beb 100644
--- a/src/tests/renew.rs
+++ b/src/tests/renew.rs
@@ -269,7 +269,7 @@ fn renew_expiring_selection_logic() {
         "renew-expiring-selection-logic",
         ["--expiring"],
         |cfg, store| {
-            let renewal = WildcardAuditRenewal::expiring(cfg, store);
+            let renewal = WildcardAuditRenewal::expiring(cfg, store, false);
             assert_eq!(renewal.expired_crates(), vec!["foo", "quux"]);
             assert_eq!(renewal.expiring_crates(), vec!["baz", "foo"]);
             renewal
@@ -283,3 +283,130 @@ fn renew_specific_selection_logic() {
         WildcardAuditRenewal::single_crate("foo", store).expect("store inconsistent")
     });
 }
+
+enum ExpiringStatus {
+    None,
+    Expiring,
+    Expired,
+}
+
+fn expiring_ignore_inactive_test(
+    audit_start: chrono::DateTime<chrono::Utc>,
+    audit_end: chrono::DateTime<chrono::Utc>,
+    last_update: chrono::DateTime<chrono::Utc>,
+    expected_status: ExpiringStatus,
+) {
+    let _enter = TEST_RUNTIME.enter();
+    let metadata = MockMetadata::simple().metadata();
+    let (config, mut audits, imports) = builtin_files_no_exemptions(&metadata);
+
+    let package = "third-party1";
+    let user_id = 1;
+
+    audits.wildcard_audits.insert(
+        package.to_owned(),
+        vec![WildcardEntry {
+            who: vec!["user".to_owned().into()],
+            criteria: vec!["safe-to-deploy".to_owned().into()],
+            user_id,
+            start: audit_start.date_naive().into(),
+            end: audit_end.date_naive().into(),
+            renew: None,
+            notes: None,
+            aggregated_from: Default::default(),
+            is_fresh_import: false,
+        }],
+    );
+
+    let mut network = Network::new_mock();
+    MockRegistryBuilder::new()
+        .user(user_id, "user1", "User One")
+        .package(
+            package,
+            &[
+                reg_published_by(
+                    ver(9),
+                    Some(user_id),
+                    last_update - chrono::Duration::weeks(4),
+                ),
+                reg_published_by(ver(10), Some(user_id), last_update),
+                reg_published_by(
+                    ver(11),
+                    Some(user_id),
+                    last_update - chrono::Duration::weeks(2),
+                ),
+            ],
+        )
+        .serve(&mut network);
+
+    let cfg = mock_cfg(&metadata);
+    let mut store = Store::mock_online(&cfg, config, audits, imports, &network, false)
+        .expect("store acquisition failed");
+
+    let renewal = WildcardAuditRenewal::expiring(&cfg, &mut store, true);
+    match expected_status {
+        ExpiringStatus::None => {
+            assert!(renewal.is_empty(), "expected no audits needing renewal");
+        }
+        ExpiringStatus::Expiring => {
+            assert!(!renewal.is_empty(), "expected an audit needing renewal");
+            assert_eq!(renewal.expiring_crates(), vec![package]);
+            assert!(renewal.expired_crates().is_empty());
+        }
+        ExpiringStatus::Expired => {
+            assert!(!renewal.is_empty(), "expected an audit needing renewal");
+            assert!(renewal.expiring_crates().is_empty());
+            assert_eq!(renewal.expired_crates(), vec![package]);
+        }
+    }
+}
+
+#[test]
+fn expiring_ignore_inactive_test_expired_inactive() {
+    // If the last update was over WILDCARD_AUDIT_INACTIVE_CRATE_DURATION ago,
+    // and was before the end of the audit, don't generate a warning if we're
+    // ignoring inactive.
+    expiring_ignore_inactive_test(
+        mock_months_ago(10),
+        mock_months_ago(6),
+        mock_months_ago(7),
+        ExpiringStatus::None,
+    )
+}
+
+#[test]
+fn expiring_ignore_inactive_test_expired_inactive_after_end() {
+    // If the last update was over WILDCARD_AUDIT_INACTIVE_CRATE_DURATION ago,
+    // but was after the end of the audit, generate a warning as it should be
+    // extended to cover all versions.
+    expiring_ignore_inactive_test(
+        mock_months_ago(10),
+        mock_months_ago(6),
+        mock_months_ago(5),
+        ExpiringStatus::Expired,
+    )
+}
+
+#[test]
+fn expiring_ignore_inactive_test_expiring_inactive() {
+    // If the last update was over WILDCARD_AUDIT_INACTIVE_CRATE_DURATION ago,
+    // and the audit is expiring soon, don't generate a warning.
+    expiring_ignore_inactive_test(
+        mock_months_ago(10),
+        mock_weeks_ago(-1),
+        mock_months_ago(7),
+        ExpiringStatus::None,
+    )
+}
+
+#[test]
+fn expiring_ignore_inactive_test_expiring_active() {
+    // If the last update was less than WILDCARD_AUDIT_INACTIVE_CRATE_DURATION
+    // ago, and the audit is expiring soon, do generate a warning.
+    expiring_ignore_inactive_test(
+        mock_months_ago(10),
+        mock_weeks_ago(-1),
+        mock_months_ago(1),
+        ExpiringStatus::Expiring,
+    )
+}
diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all.snap
index 33859c7..07c0853 100644
--- a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all.snap
+++ b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all.snap
@@ -38,12 +38,12 @@ description = "weakly reviewed"
 [[trusted.third-party2]]
 criteria = "reviewed"
 user-id = 2 # Test user (testuser)
-start = "2022-12-12"
+start = "2022-12-18"
 end = "2024-01-01"
 
 [[trusted.transitive-third-party1]]
 criteria = "reviewed"
 user-id = 2 # Test user (testuser)
-start = "2022-10-12"
+start = "2022-10-23"
 end = "2024-01-01"
 
diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all_allow_multiple.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all_allow_multiple.snap
index 7d27994..92a287b 100644
--- a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all_allow_multiple.snap
+++ b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_all_allow_multiple.snap
@@ -38,18 +38,18 @@ description = "weakly reviewed"
 [[trusted.third-party1]]
 criteria = "reviewed"
 user-id = 2 # Test user (testuser)
-start = "2022-12-12"
+start = "2022-12-18"
 end = "2024-01-01"
 
 [[trusted.third-party2]]
 criteria = "reviewed"
 user-id = 2 # Test user (testuser)
-start = "2022-12-12"
+start = "2022-12-18"
 end = "2024-01-01"
 
 [[trusted.transitive-third-party1]]
 criteria = "reviewed"
 user-id = 2 # Test user (testuser)
-start = "2022-10-12"
+start = "2022-10-23"
 end = "2024-01-01"
 
diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_explicit.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_explicit.snap
index b4124d5..01c5103 100644
--- a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_explicit.snap
+++ b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_explicit.snap
@@ -38,6 +38,6 @@ description = "weakly reviewed"
 [[trusted.third-party1]]
 criteria = "safe-to-deploy"
 user-id = 2
-start = "2022-12-12"
+start = "2022-12-18"
 end = "2024-01-01"
 
diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_simple.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_simple.snap
index e95a881..6a20c98 100644
--- a/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_simple.snap
+++ b/src/tests/snapshots/cargo_vet__tests__certify__mock_trust_flow_simple.snap
@@ -38,6 +38,6 @@ description = "weakly reviewed"
 [[trusted.third-party1]]
 criteria = "safe-to-deploy"
 user-id = 2
-start = "2022-10-12"
+start = "2022-10-23"
 end = "2024-01-01"
 
diff --git a/src/tests/snapshots/cargo_vet__tests__certify__mock_wildcard_certify_flow.snap b/src/tests/snapshots/cargo_vet__tests__certify__mock_wildcard_certify_flow.snap
index 8f98c64..90a970c 100644
--- a/src/tests/snapshots/cargo_vet__tests__certify__mock_wildcard_certify_flow.snap
+++ b/src/tests/snapshots/cargo_vet__tests__certify__mock_wildcard_certify_flow.snap
@@ -44,13 +44,13 @@ current selection: ["safe-to-deploy"]
 #
 # Uncomment the following statement:
 
-# I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-12 and 2024-01-01 will satisfy the above criteria.
+# I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-18 and 2024-01-01 will satisfy the above criteria.
 
 # Add any notes about your audit below this line:
 
 
 <<<EDIT OK>>>
-I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-12 and 2024-01-01 will satisfy the above criteria.
+I, testing, certify that any version of third-party1 published by 'testuser' between 2022-12-18 and 2024-01-01 will satisfy the above criteria.
 
 These are testing notes. They contain some
 newlines. Trailing whitespace        
@@ -79,7 +79,7 @@ description = "weakly reviewed"
 who = "testing"
 criteria = "safe-to-deploy"
 user-id = 2
-start = "2022-12-12"
+start = "2022-12-18"
 end = "2024-01-01"
 notes = """
 These are testing notes. They contain some
diff --git a/src/tests/snapshots/cargo_vet__tests__import__import_wildcard_audit_publisher.snap b/src/tests/snapshots/cargo_vet__tests__import__import_wildcard_audit_publisher.snap
index a2dc303..1078f5b 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__import_wildcard_audit_publisher.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__import_wildcard_audit_publisher.snap
@@ -5,14 +5,14 @@ expression: output
  
 +[[publisher.third-party1]]
 +version = "10.0.0"
-+when = "2022-12-12"
++when = "2022-12-18"
 +user-id = 2
 +user-login = "user2"
 +user-name = "User Two"
 +
 +[[publisher.third-party2]]
 +version = "10.0.0"
-+when = "2022-12-12"
++when = "2022-12-18"
 +user-id = 1
 +user-login = "user1"
 +user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__import__local_wildcard_audit_preferred_to_fresh_import.snap b/src/tests/snapshots/cargo_vet__tests__import__local_wildcard_audit_preferred_to_fresh_import.snap
index f1e7c72..2bd4e33 100644
--- a/src/tests/snapshots/cargo_vet__tests__import__local_wildcard_audit_preferred_to_fresh_import.snap
+++ b/src/tests/snapshots/cargo_vet__tests__import__local_wildcard_audit_preferred_to_fresh_import.snap
@@ -5,7 +5,7 @@ expression: output
 +
 +[[publisher.third-party2]]
 +version = "10.0.0"
-+when = "2022-12-15"
++when = "2022-12-18"
 +user-id = 1
 +user-login = "user1"
 +user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prefer_exemptions.snap b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prefer_exemptions.snap
index d5ad564..3c15db2 100644
--- a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prefer_exemptions.snap
+++ b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prefer_exemptions.snap
@@ -15,7 +15,7 @@ imports.lock:
  [[publisher.descriptive]]
 -version = "8.0.0"
 +version = "9.0.0"
- when = "2022-12-15"
+ when = "2022-12-18"
  user-id = 1
  user-login = "user1"
  user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prune.snap b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prune.snap
index d5ad564..3c15db2 100644
--- a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prune.snap
+++ b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_prune.snap
@@ -15,7 +15,7 @@ imports.lock:
  [[publisher.descriptive]]
 -version = "8.0.0"
 +version = "9.0.0"
- when = "2022-12-15"
+ when = "2022-12-18"
  user-id = 1
  user-login = "user1"
  user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_regenerate_exemptions.snap b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_regenerate_exemptions.snap
index d5ad564..3c15db2 100644
--- a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_regenerate_exemptions.snap
+++ b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_nounpublished_regenerate_exemptions.snap
@@ -15,7 +15,7 @@ imports.lock:
  [[publisher.descriptive]]
 -version = "8.0.0"
 +version = "9.0.0"
- when = "2022-12-15"
+ when = "2022-12-18"
  user-id = 1
  user-login = "user1"
  user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_prune.snap b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_prune.snap
index 2208d9c..9fed7d7 100644
--- a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_prune.snap
+++ b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_prune.snap
@@ -16,7 +16,7 @@ imports.lock:
  [[publisher.descriptive]]
 -version = "8.0.0"
 +version = "9.0.0"
- when = "2022-12-15"
+ when = "2022-12-18"
  user-id = 1
  user-login = "user1"
  user-name = "User One"
diff --git a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_regenerate_exemptions.snap b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_regenerate_exemptions.snap
index 2208d9c..9fed7d7 100644
--- a/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_regenerate_exemptions.snap
+++ b/src/tests/snapshots/cargo_vet__tests__unpublished__audit_as_crates_io_unpublished_wildcard_regenerate_exemptions.snap
@@ -16,7 +16,7 @@ imports.lock:
  [[publisher.descriptive]]
 -version = "8.0.0"
 +version = "9.0.0"
- when = "2022-12-15"
+ when = "2022-12-18"
  user-id = 1
  user-login = "user1"
  user-name = "User One"
diff --git a/src/tests/trusted.rs b/src/tests/trusted.rs
index c9c687a..7a01706 100644
--- a/src/tests/trusted.rs
+++ b/src/tests/trusted.rs
@@ -124,7 +124,11 @@ fn trusted_suggest_local() {
         .user(1, "testuser", "Test user")
         .package(
             "transitive-third-party1",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
@@ -170,7 +174,11 @@ fn trusted_suggest_import() {
         .user(1, "testuser", "Test user")
         .package(
             "transitive-third-party1",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
@@ -225,7 +233,11 @@ fn trusted_suggest_import_multiple() {
         .user(1, "testuser", "Test user")
         .package(
             "transitive-third-party1",
-            &[reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12")],
+            &[reg_published_by(
+                ver(DEFAULT_VER),
+                Some(1),
+                mock_weeks_ago(2),
+            )],
         )
         .serve(&mut network);
 
@@ -261,8 +273,8 @@ fn trusted_suggest_local_ambiguous() {
         .package(
             "transitive-third-party1",
             &[
-                reg_published_by(ver(9), Some(2), "2022-12-12"),
-                reg_published_by(ver(DEFAULT_VER), Some(1), "2022-12-12"),
+                reg_published_by(ver(9), Some(2), mock_weeks_ago(2)),
+                reg_published_by(ver(DEFAULT_VER), Some(1), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);
diff --git a/src/tests/unpublished.rs b/src/tests/unpublished.rs
index a2c9e2b..9eb5421 100644
--- a/src/tests/unpublished.rs
+++ b/src/tests/unpublished.rs
@@ -70,8 +70,8 @@ fn unpublished_basic_regenerate(
         .package(
             "descriptive",
             &[
-                reg_published_by(ver(8), Some(1), "2022-12-15"),
-                reg_published_by(ver(9), Some(1), "2022-12-15"),
+                reg_published_by(ver(8), Some(1), mock_weeks_ago(2)),
+                reg_published_by(ver(9), Some(1), mock_weeks_ago(2)),
             ],
         )
         .serve(&mut network);