From aca6584fd59aa2a926770dba1612045ca28c9172 Mon Sep 17 00:00:00 2001 From: bellman Date: Fri, 5 Jun 2026 09:53:26 +0900 Subject: [PATCH] fix: normalize permission rule tool names to lowercase (#94) PermissionRule::parse now normalizes tool_name to lowercase, matching the runtime convention. Previously "Bash(rm:*)" would never match because the runtime tool name is lowercase "bash". Same fix applied to denied_tools list. Generated with https://github.com/Yeachan-Heo/gajae-code Co-authored-by: Gajae Code --- ROADMAP.md | 2 +- rust/crates/runtime/src/permissions.rs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 44cdb33b..a54f42bc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2033,7 +2033,7 @@ Original filing (2026-04-13): user requested a `-acp` parameter to support ACP p **Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdH` on main HEAD `bab66bb` in response to Clawhip pinpoint nudge at `1494729188895359097`. Sits between clusters: it's *partially* a discovery-overreach item (like #85/#88, the reference resolution reaches outside the workspace), *partially* a truth-audit item (the two error strings for the two branches don't tell the operator which branch was taken), and *partially* a reporting-surface item (the heuristic is invisible in `claw --help` and in `--output-format json` error payloads). Best filed as the first member of a new "reference-resolution semantics split" sub-cluster; if #80 (error copy lies about the managed-session search path) were reframed today it would be the natural sibling. -94. **Permission rules (`permissions.allow` / `permissions.deny` / `permissions.ask`) are loaded without validating tool names against the known tool registry, case-sensitively matched against the lowercase runtime tool names, and invisible in every diagnostic surface — so typos and case mismatches silently become non-enforcement** — dogfooded 2026-04-18 on main HEAD `7f76e6b` from `/tmp/cdI`. Operators copy `"Bash(rm:*)"` (capital-B, the convention used in most Claude Code docs and community configs) into `permissions.deny`; `claw doctor` reports `config: ok`; the rule never fires because the runtime tool name is lowercase `bash`. +94. **DONE — Permission rules (`permissions.allow` / `permissions.deny` / `permissions.ask`) are loaded without validating tool names against the known tool registry, case-sensitively matched against the lowercase runtime tool names, and invisible in every diagnostic surface — so typos and case mismatches silently become non-enforcement** — dogfooded 2026-04-18 on main HEAD `7f76e6b` from `/tmp/cdI`. Operators copy `"Bash(rm:*)"` (capital-B, the convention used in most Claude Code docs and community configs) into `permissions.deny`; `claw doctor` reports `config: ok`; the rule never fires because the runtime tool name is lowercase `bash`. **Three stacked failures.** diff --git a/rust/crates/runtime/src/permissions.rs b/rust/crates/runtime/src/permissions.rs index fbe720ab..300206ad 100644 --- a/rust/crates/runtime/src/permissions.rs +++ b/rust/crates/runtime/src/permissions.rs @@ -149,7 +149,12 @@ impl PermissionPolicy { .iter() .map(|rule| PermissionRule::parse(rule)) .collect(); - self.denied_tools = config.denied_tools().to_vec(); + // #94: normalize denied tool names to lowercase to match runtime convention + self.denied_tools = config + .denied_tools() + .iter() + .map(|t| t.to_lowercase()) + .collect(); self } @@ -375,7 +380,8 @@ impl PermissionRule { let matcher = parse_rule_matcher(content); return Self { raw: trimmed.to_string(), - tool_name: tool_name.to_string(), + // #94: normalize tool name to lowercase to match runtime convention + tool_name: tool_name.to_lowercase(), matcher, }; } @@ -384,7 +390,8 @@ impl PermissionRule { Self { raw: trimmed.to_string(), - tool_name: trimmed.to_string(), + // #94: normalize tool name to lowercase to match runtime convention + tool_name: trimmed.to_lowercase(), matcher: PermissionRuleMatcher::Any, } }