MCP v2 PR-1: policy engine + audit log + Config/Audit/Policy panel tabs
Foundation for Claude-drives-the-workspace writes. Nothing wired end-to-end yet (App.tsx dispatcher comes next); this lands the machinery + UI. mcp_policy.rs (new) — three-tier allow/ask/deny policy with deny-first precedence and a compiled-in non-overridable hard-deny list (10 patterns covering rm -rf /, fork bombs, mkfs on device, dd to raw disk, /etc/passwd overwrite, curl|sh, chmod -R 777 /, etc.). Shell-operator-aware glob matcher mirroring Claude Code's Bash(*) syntax. Restrictive default — empty policy means every non-hard- denied call falls to Ask. Persisted to mcp-policy.json in app_config_dir. Includes a PolicyClassifier scaffold (no-op) for a future v2.1 LLM-classifier hook. 1152 lines incl. ~100 unit + fuzz tests covering the matchers and lookalike negatives. mcp.rs — TileService now holds AppHandle + Arc<PendingActions> (oneshot registry keyed by uuid). New async dispatch_action helper runs the policy check, emits "mcp://request" for the frontend to handle, awaits a oneshot reply (30s timeout), then emits "mcp:// audit" with the outcome regardless. set_label tool wired through this path as the demo for PR-1b's dispatcher. commands.rs / lib.rs — new Tauri commands mcp_action_reply, mcp_policy_load, mcp_policy_save; PendingActions registered as managed state. McpPanel.tsx — refactored into Config / Audit / Policy tabs. AuditTab listens on mcp://audit, keeps a 200-entry ring with ok/denied/failed chips. PolicyTab edits the allow/ask/deny buckets (stacked vertically — three columns overflowed the panel) and shows the hard-deny rules read-only at the bottom with "Cannot be disabled" badges. Themed scrollbar on mcp-body to match xterm panes. Caveat: set_label calls from Claude will currently time out — the App.tsx side that listens on mcp://request and replies via mcp_action_reply lands in PR-1b. Co-authored by Sonnet (policy engine, backend plumbing, panel UI) and Haiku (hard-deny fuzz test suite); integration + bug fixes here.
This commit is contained in:
parent
b14b450577
commit
464c576b79
11 changed files with 2512 additions and 144 deletions
41
src/ipc.ts
41
src/ipc.ts
|
|
@ -134,3 +134,44 @@ export const mcpRegenerateToken = (): Promise<McpStatus> =>
|
|||
invoke("mcp_regenerate_token");
|
||||
export const mcpUpdateState = (mirror: McpMirror): Promise<void> =>
|
||||
invoke("mcp_update_state", { mirror });
|
||||
|
||||
// ---- MCP audit log (events) ---------------------------------------------
|
||||
|
||||
export interface McpAuditEntry {
|
||||
tsMs: number;
|
||||
tool: string;
|
||||
argsSummary: string; // already truncated to 80 chars by backend
|
||||
result:
|
||||
| { kind: "ok" }
|
||||
| { kind: "denied"; reason: string; hard: boolean }
|
||||
| { kind: "failed"; msg: string };
|
||||
durationMs: number;
|
||||
}
|
||||
|
||||
export interface McpActionRequest {
|
||||
requestId: string;
|
||||
tool: string;
|
||||
args: unknown;
|
||||
needsConfirm: boolean;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
// ---- MCP policy ---------------------------------------------------------
|
||||
|
||||
export interface McpPolicy {
|
||||
version: number;
|
||||
permissions: {
|
||||
deny: string[];
|
||||
ask: string[];
|
||||
allow: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export const mcpPolicyLoad = (): Promise<McpPolicy> =>
|
||||
invoke("mcp_policy_load");
|
||||
|
||||
export const mcpPolicySave = (policy: McpPolicy): Promise<void> =>
|
||||
invoke("mcp_policy_save", { policy });
|
||||
|
||||
// (No JS wrapper for mcp_action_reply or events — App.tsx wires those
|
||||
// directly in the integration step.)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue