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:
megaproxy 2026-05-26 12:05:31 +01:00
parent b14b450577
commit 464c576b79
11 changed files with 2512 additions and 144 deletions

View file

@ -4,11 +4,12 @@ mod commands;
mod creds;
mod hosts;
mod mcp;
mod mcp_policy;
mod pty;
use std::sync::Arc;
use crate::mcp::{McpServerHandle, McpState};
use crate::mcp::{McpServerHandle, McpState, PendingActions};
use crate::pty::PtyManager;
pub fn run() {
@ -36,6 +37,9 @@ pub fn run() {
let ptys: Arc<PtyManager> = Arc::new(PtyManager::new());
let mcp_state: Arc<tokio::sync::RwLock<McpState>> =
Arc::new(tokio::sync::RwLock::new(McpState::default()));
// Pending action registry — separate managed state so mcp_action_reply can
// grab it without needing to lock McpState or reach into TileService.
let pending_actions: Arc<PendingActions> = Arc::new(PendingActions::default());
tauri::Builder::default()
.plugin(tauri_plugin_clipboard_manager::init())
@ -43,6 +47,7 @@ pub fn run() {
.manage(ptys)
.manage(mcp_state)
.manage(McpServerHandle::default())
.manage(pending_actions)
.invoke_handler(tauri::generate_handler![
commands::list_distros,
commands::spawn_pane,
@ -61,6 +66,9 @@ pub fn run() {
commands::mcp_status,
commands::mcp_regenerate_token,
commands::mcp_update_state,
commands::mcp_action_reply,
commands::mcp_policy_load,
commands::mcp_policy_save,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");