From 0a5a7de8e86f27bdd0b23dfbf54d5f2758b5f401 Mon Sep 17 00:00:00 2001 From: ajianaz Date: Fri, 9 Oct 2026 14:04:07 +0700 Subject: [PATCH] fix(config): rules_engine section defaults enabled to true (#638) Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 4 ++++ src/config/schema.rs | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e3fde..0f47131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **A `rules_engine:` section without `enabled: true` no longer disables the built-in rules.** `enabled` now defaults to `true` when the section is present, so `rules_engine: { max_findings: 0 }` only changes the cap; set `enabled: false` to turn the engine off (#638). + ### Security - **Release checksums are signed with cosign keyless and verified by `install.sh` and `cora upgrade` (#591).** `release.yml` now signs `checksums-sha256.txt` (which covers every archive) via Sigstore/GitHub OIDC before creating the release and uploads `checksums-sha256.txt.sigstore.json`; a signing failure aborts the job so no unsigned release is published. `install.sh` and `cora upgrade` verify the bundle with `cosign verify-blob` (identity: this repo's `release.yml` on a `v*` tag, issuer `token.actions.githubusercontent.com`) when `cosign` is on `PATH`; an invalid signature aborts. Without `cosign`, or for releases without a bundle, they print a notice and fall back to checksum-only. `CORA_REQUIRE_SIGNATURE=1` makes a missing `cosign` or bundle fatal. See `docs/installation.md`. diff --git a/src/config/schema.rs b/src/config/schema.rs index 11f7395..b9cb6da 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -609,6 +609,10 @@ pub struct LlmSection { pub cache_ttl: Option, } +fn default_rules_enabled() -> bool { + true +} + fn default_max_findings() -> usize { 5 } @@ -625,7 +629,10 @@ where #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct RulesSection { - #[serde(default, skip_serializing_if = "is_default")] + /// Built-in rule engine switch. Defaults to `true` (the same as having no + /// `rules_engine` section at all), so tuning another key such as + /// `max_findings` does not silently turn the built-in rules off (#638). + #[serde(default = "default_rules_enabled", skip_serializing_if = "is_true")] pub enabled: bool, /// Cap on deterministic findings shown per run. `0` or `null` = unlimited (#624). #[serde( @@ -1136,6 +1143,32 @@ provider: zai assert_eq!(nm_count, 1, "duplicate entry should be deduplicated"); } + #[test] + fn rules_engine_section_without_enabled_keeps_rules_on() { + // #638: `rules_engine: { max_findings: 0 }` must not disable built-in rules. + let cora: CoraFile = serde_yaml_ng::from_str("rules_engine:\n max_findings: 0\n").unwrap(); + let mut cfg = Config::default(); + cora.merge_into(&mut cfg).unwrap(); + assert!(cfg.rules_config.enabled); + assert_eq!(cfg.rules_config.max_findings, 0); + } + + #[test] + fn rules_engine_enabled_false_still_disables_and_round_trips() { + let cora: CoraFile = + serde_yaml_ng::from_str("rules_engine:\n enabled: false\n max_findings: 7\n") + .unwrap(); + let mut cfg = Config::default(); + cora.merge_into(&mut cfg).unwrap(); + assert!(!cfg.rules_config.enabled); + let out = serde_yaml_ng::to_string(&cora).unwrap(); + let again: CoraFile = serde_yaml_ng::from_str(&out).unwrap(); + assert!( + !again.rules_engine.unwrap().enabled, + "false must survive a round trip" + ); + } + #[test] fn merge_index_skip_files_preserves_defaults() { let mut cfg = Config::default();