From fca6890738036eeed54e2c4e9e38ed4f6553ba1d Mon Sep 17 00:00:00 2001 From: Muawiya Amir Date: Mon, 7 Sep 2026 10:23:45 +0500 Subject: [PATCH 1/2] fix(cli): correct the --rules help text The flag no longer replaces the whole database: the embedded protected rules are prepended, so a supplied file cannot shadow them. --help still promised the old behaviour, and it was the only place left saying so after the README row was fixed. --- crates/diskern-cli/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/diskern-cli/src/main.rs b/crates/diskern-cli/src/main.rs index 86a9339..b2474c2 100644 --- a/crates/diskern-cli/src/main.rs +++ b/crates/diskern-cli/src/main.rs @@ -29,7 +29,7 @@ enum Command { /// Only show findings with this verdict #[arg(long, value_enum)] verdict: Option, - /// Load rules from a JSON file instead of the embedded database + /// Load rules from a JSON file; embedded protected rules still apply #[arg(long, value_name = "FILE")] rules: Option, }, From 7bc0e23acc8df3d329e3853c1a07f7ae010b617f Mon Sep 17 00:00:00 2001 From: Muawiya Amir Date: Mon, 7 Sep 2026 10:36:22 +0500 Subject: [PATCH 2/2] fix(rules): reject a rule with no patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An empty pattern list builds a GlobSet that matches nothing, so it fails exactly the way an invalid glob does — silently, and only for the rule whose author cared about it. validate() exists to stop a supplied file from reporting success for a rule that can never fire, and this was the other way to write one. --- crates/diskern-core/src/rules.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/diskern-core/src/rules.rs b/crates/diskern-core/src/rules.rs index ef4cd93..25a0b2e 100644 --- a/crates/diskern-core/src/rules.rs +++ b/crates/diskern-core/src/rules.rs @@ -81,6 +81,15 @@ impl RulesDb { /// for a rule that can never match. pub fn validate(&self) -> Result<()> { for rule in &self.rules { + // An empty pattern list compiles cleanly into a GlobSet that + // matches nothing, so it fails the same way a bad glob does — + // silently, and only for the rule the author cared about. + if rule.patterns.is_empty() { + return Err(GenomeError::Rules(format!( + "rule '{}' has no patterns, so it can never match", + rule.id + ))); + } for pattern in &rule.patterns { build_glob(pattern).map_err(|error| { GenomeError::Rules(format!( @@ -274,6 +283,26 @@ mod tests { assert!(error.to_string().contains("invalid glob pattern")); } + #[test] + fn rules_without_patterns_are_rejected() { + let db = RulesDb::new( + 1, + vec![Rule { + id: "empty".into(), + patterns: vec![], + category: Category::Unknown, + verdict: Verdict::Review, + description: "rule with no patterns".into(), + }], + ); + + let error = db + .validate() + .expect_err("a rule with no patterns must fail validation"); + assert!(error.to_string().contains("empty")); + assert!(error.to_string().contains("can never match")); + } + #[test] fn embedded_protected_rules_precede_external_rules() { let external = RulesDb::new(