Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
35 changes: 34 additions & 1 deletion src/config/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,10 @@ pub struct LlmSection {
pub cache_ttl: Option<u64>,
}

fn default_rules_enabled() -> bool {
true
}

fn default_max_findings() -> usize {
5
}
Expand All @@ -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(
Expand Down Expand Up @@ -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();
Expand Down
Loading