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
|
|
@ -6,7 +6,8 @@ import { cpSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { mint } from '../cli/lib/confirm-token';
|
||||
import { FIX_METHODS, scanFixAction } from '../cli/lib/fix-actions';
|
||||
import { AI_FIX_RULES, FIX_METHODS, SCAN_WRITE_RULES, scanFixAction } from '../cli/lib/fix-actions';
|
||||
import { readFileSync as read } from 'node:fs';
|
||||
import { runScan } from '../cli/lib/scan';
|
||||
import { appleRules, copyrightRules, playRules } from '../cli/lib/validate-rules';
|
||||
import { FORBIDDEN_ARGS, TOOLS, toArgv } from '../cli/mcp-tools';
|
||||
|
|
@ -57,6 +58,16 @@ describe('finding -> fix action', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Fix with AI rules', () => {
|
||||
test('only real scan rules that need a code change; never a compliance answer or a deterministic fix', () => {
|
||||
const src = ['rules.ts', 'play-rules.ts'].map(f => read(join(import.meta.dir, '..', 'cli', 'lib', 'scan', f), 'utf8')).join('\n');
|
||||
const ids = new Set([...src.matchAll(/rule: '([a-z0-9-]+)'/g)].map(m => m[1]!));
|
||||
for (const r of AI_FIX_RULES) expect({ r, known: ids.has(r) }).toEqual({ r, known: true });
|
||||
for (const r of ['export-compliance', 'demo-account', 'template-answers', 'privacy-labels', 'play-health-connect', 'metadata-keywords']) expect(AI_FIX_RULES.has(r)).toBe(false);
|
||||
for (const r of SCAN_WRITE_RULES) expect(AI_FIX_RULES.has(r)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scan.write round trip', () => {
|
||||
test('dry run shows the config diff and writes nothing; the confirmed run writes; the finding is gone', async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'sb-fix-'));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue