diff --git a/README.md b/README.md index 59d02bc..17d1a56 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ effort = "" # the Model's variant, passed as #effort; default blank, for OpenC [security] fix = false # Security runs may fix reproduced findings; default false review = false # Runs review their change for Security findings; default false -harness = "" # the Harness a Security run uses unless its command names one; default blank, for harness.default or Claude Code +harness = "" # the Harness a Security run uses unless its command names one; default blank; blank or missing uses harness.default, or Claude Code if harness.default is missing ``` Every key holds its real value, so a Run reading it does exactly what it does with no file. `email.to` has no default, so `setup` suggests one: the public email of your GitHub profile (from `gh api user`), else your global git `user.email`, unless that is a `@users.noreply.github.com` address, which can't receive mail. With neither, `email.to` is the only line written commented out, as above. `setup` never asks `gh` for more scopes, so a private GitHub email is not read, and a Run never looks the suggestion up: `--email` with no address and no `email.to` still stops the Run. From a terminal (stdin and stderr both terminals), `setup` first asks, on stderr: @@ -762,7 +762,7 @@ A failed fix keeps its **Claim**, even if it pushed nothing, and stays open for `email`, optionally followed by an address, or `email.always` in the User config asks for one **Run notification** when the Security run ends, whether the audit succeeded, failed or was interrupted. `no-email` overrides the default. The notification says how the audit ended and lists each finding it recorded or matched to an existing private record: its severity when known, title and private link. It includes no finding write-up, trace or evidence, since it passes through Resend. Detailed failure causes stay in the local logs; the notification gives the audit's status and log paths. A recording failure still lists the records reached before it failed. When a run leaves a reproduced finding unfixed and neither the command nor the User config decided against fixing, its notification and stderr offer both `thirdshift secure security-fix` and `fix = true` under `[security]`. With `no-security-fix`, or the setting turned off, it offers nothing. A skipped Security run sends none. The usual address and Resend API key checks run before the skip checks or any work; a failed send is a warning and never changes the run's outcome. -A Security run chooses its Harness from the command's `harness` word, then `[security] harness` in the User config, then `harness.default`, then Claude Code. A blank or missing Security setting keeps that default. Model and Effort come from the chosen Harness's own `[harness.]` section, with command words taking precedence. For example, `harness claude` overrides `[security] harness = "codex"` and uses `[harness.claude]`. +A Security run chooses its Harness from the command's `harness` word, then `[security] harness` in the User config, then `harness.default`, then Claude Code. When `security.harness` is blank or missing, it uses `harness.default`; when `harness.default` is also missing, it uses Claude Code. Model and Effort come from the chosen Harness's own `[harness.]` section, with command words taking precedence. For example, `harness claude` overrides `[security] harness = "codex"` and uses `[harness.claude]`. Codex security sessions and their Resumes set `agents.max_concurrent_threads_per_session=8` and ask for fresh sub-agents with `fork_turns: "none"`, so the skill's verifiers stay independent. diff --git a/src/config.rs b/src/config.rs index 3f2c565..19d6e86 100644 --- a/src/config.rs +++ b/src/config.rs @@ -771,7 +771,7 @@ effort = "" # the Model's variant, passed as #effort; default blank, for OpenC [security] fix = false # Security runs may fix reproduced findings; default false review = false # Runs review their change for Security findings; default false -harness = "" # the Harness a Security run uses unless its command names one; default blank, for harness.default or Claude Code +harness = "" # the Harness a Security run uses unless its command names one; default blank; blank or missing uses harness.default, or Claude Code if harness.default is missing "#; /// The line `DEFAULTS` holds for `email.to`, which has no default. diff --git a/tests/security_run.rs b/tests/security_run.rs index b62864a..87e7599 100644 --- a/tests/security_run.rs +++ b/tests/security_run.rs @@ -1897,14 +1897,15 @@ fn a_blank_or_missing_security_harness_preserves_the_default_harness_and_its_set args.windows(2) .any(|pair| pair == [json!("-c"), json!("model_reasoning_effort=\"high\"")]) ); + + let scenario = Scenario::new(); + scenario.user_config_is(security); + scenario.agent_does(&audit_script("[]")); + let result = scenario.run(&["secure"]); + assert_eq!(result.code, Some(0), "{security}: {}", result.stderr); + assert_eq!(scenario.claude_calls().len(), 1, "{security}"); + assert!(scenario.codex_calls().is_empty(), "{security}"); } - let scenario = Scenario::new(); - scenario.user_config_is("[security]\nharness = \"\"\n"); - scenario.agent_does(&audit_script("[]")); - let result = scenario.run(&["secure"]); - assert_eq!(result.code, Some(0), "{}", result.stderr); - assert_eq!(scenario.claude_calls().len(), 1); - assert!(scenario.codex_calls().is_empty()); } #[test] diff --git a/tests/setup.rs b/tests/setup.rs index 63f5ab6..60b4aa1 100644 --- a/tests/setup.rs +++ b/tests/setup.rs @@ -371,7 +371,12 @@ fn setup_asks_once_about_security_fixing_with_off_as_the_default_and_writes_yes( let config: toml::Table = text.parse().unwrap(); assert_eq!(config["security"]["fix"].as_bool(), Some(true)); assert_eq!(config["security"]["harness"].as_str(), Some("")); - assert!(text.contains("# the Harness a Security run uses unless its command names one")); + assert!( + text.contains( + "# the Harness a Security run uses unless its command names one; default blank; blank or missing uses harness.default, or Claude Code if harness.default is missing" + ), + "{text}" + ); } #[test]