desktop: Fix with AI on scan findings that need a code change (aiFix: true, AI_FIX_RULES). The host (src-tauri/src/ai_fix.rs) runs the owner's local Claude Code headless in the app folder with Read / Edit / Write / Grep / Glob only (Bash, web tools, sub-agents disallowed) and a prompt it builds from the finding's fields; it refuses while the folder has uncommitted files, streams progress, times out after 10 min with a heartbeat, kills the process group on Cancel or exit, and records the run's changed files so Undo restores exactly those; the window shows the diff with Keep / Undo and rescans. Without claude the button copies the prompt (Copy for AI). Only the claude binary, git and kill / taskkill are started there (tests/security.rs); tests/ai_fix.rs covers the prompt, dirty-tree refusal, undo scope, timeout and cancel
This commit is contained in:
parent
992f5bcc0e
commit
c95f2af8ff
24 changed files with 1023 additions and 10 deletions
|
|
@ -179,6 +179,16 @@ fn sidecar_starts_with_fixed_arguments_from_one_place() {
|
|||
assert!(src.contains("Command::new(&bin)\n .args(policy::SIDECAR_ARGS)"));
|
||||
continue;
|
||||
}
|
||||
// Fix with AI: only the claude binary (claude_command checks its name), git, and kill / taskkill by PID
|
||||
if p.ends_with("ai_fix.rs") {
|
||||
for (i, _) in src.match_indices("Command::new(") {
|
||||
let arg = &src[i + "Command::new(".len()..];
|
||||
let ok = ["claude)", "\"git\")", "\"kill\")", "\"taskkill\")"];
|
||||
assert!(ok.iter().any(|o| arg.starts_with(o)), "unexpected program: {}", &arg[..arg.find(')').unwrap_or(20).min(40)]);
|
||||
}
|
||||
assert!(src.contains("if !matches!(name, \"claude\" | \"claude.exe\" | \"claude.cmd\")"));
|
||||
continue;
|
||||
}
|
||||
// other programs: only the download checks (unpack, signature, version banner), with fixed program names
|
||||
if src.contains("Command::new(") {
|
||||
assert!(p.ends_with("downloads.rs"), "{} starts a program", p.display());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue