Enforce tool permissions before execution

The Rust CLI/runtime now models permissions as ordered access levels, derives tool requirements from the shared tool specs, and prompts REPL users before one-off danger-full-access escalations from workspace-write sessions. This also wires explicit --permission-mode parsing and makes /permissions operate on the live session state instead of an implicit env-derived default.

Constraint: Must preserve the existing three user-facing modes read-only, workspace-write, and danger-full-access

Constraint: Must avoid new dependencies and keep enforcement inside the existing runtime/tool plumbing

Rejected: Keep the old Allow/Deny/Prompt policy model | could not represent ordered tool requirements across the CLI surface

Rejected: Continue sourcing live session mode solely from RUSTY_CLAUDE_PERMISSION_MODE | /permissions would not reliably reflect the current session state

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Add required_permission entries for new tools before exposing them to the runtime

Tested: cargo fmt; cargo clippy --workspace --all-targets -- -D warnings; cargo test -q

Not-tested: Manual interactive REPL approval flow in a live Anthropic session
This commit is contained in:
Yeachan-Heo
2026-04-01 00:06:15 +00:00
parent c139fe9bee
commit e2f061fd08
4 changed files with 331 additions and 101 deletions

View File

@@ -408,7 +408,8 @@ mod tests {
.sum::<i32>();
Ok(total.to_string())
});
let permission_policy = PermissionPolicy::new(PermissionMode::Prompt);
let permission_policy = PermissionPolicy::new(PermissionMode::WorkspaceWrite)
.with_tool_requirement("add", PermissionMode::DangerFullAccess);
let system_prompt = SystemPromptBuilder::new()
.with_project_context(ProjectContext {
cwd: PathBuf::from("/tmp/project"),
@@ -487,7 +488,8 @@ mod tests {
Session::new(),
SingleCallApiClient,
StaticToolExecutor::new(),
PermissionPolicy::new(PermissionMode::Prompt),
PermissionPolicy::new(PermissionMode::WorkspaceWrite)
.with_tool_requirement("blocked", PermissionMode::DangerFullAccess),
vec!["system".to_string()],
);
@@ -536,7 +538,7 @@ mod tests {
session,
SimpleApi,
StaticToolExecutor::new(),
PermissionPolicy::new(PermissionMode::Allow),
PermissionPolicy::new(PermissionMode::ReadOnly),
vec!["system".to_string()],
);
@@ -563,7 +565,7 @@ mod tests {
Session::new(),
SimpleApi,
StaticToolExecutor::new(),
PermissionPolicy::new(PermissionMode::Allow),
PermissionPolicy::new(PermissionMode::ReadOnly),
vec!["system".to_string()],
);
runtime.run_turn("a", None).expect("turn a");