From 5b48f0d051e593f712ca98071cbc86a65605426e Mon Sep 17 00:00:00 2001 From: Timmy Date: Fri, 8 May 2026 15:14:03 +0100 Subject: [PATCH] fix(897): broaden Bash allowlist to wildcard to stop coders stalling on uncommon commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-command allowlist (Bash(cargo:*), Bash(git:*), …) misses any tool a coder agent reaches for outside the curated set — ./script/*, make, curl, jq, docker, test, [, etc. Each miss hits prompt_permission, which auto-denies on the sled because no listener holds perm_rx (the matrix bot lives in the gateway). 1,377 such denies in the sled log over the past week, accounting for most of the recent throughput slowdown. Replace the curated list with a single Bash(:*) wildcard in: - .claude/settings.json (project root, picked up on git worktree add) - server/src/io/fs/scaffold/templates.rs (used only by huskies init when no .claude/settings.json already exists) Update scaffold/tests.rs to assert the wildcard rather than a fixed set of patterns; the per-command gate offered no real safety in this trusted single-user deployment, since the prompt was never going to reach a human anyway (that's the bug). Stopgap until story 898 lands the proper sled→gateway permission forwarding — at which point the wildcard can be narrowed back if desired. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/settings.json | 26 +---------------------- server/src/io/fs/scaffold/templates.rs | 26 +---------------------- server/src/io/fs/scaffold/tests.rs | 29 ++++++++------------------ 3 files changed, 11 insertions(+), 70 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index da240ff4..f3e82c03 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,31 +1,7 @@ { "permissions": { "allow": [ - "Bash(cargo:*)", - "Bash(git:*)", - "Bash(ls:*)", - "Bash(mkdir:*)", - "Bash(mv:*)", - "Bash(rm:*)", - "Bash(touch:*)", - "Bash(echo:*)", - "Bash(pwd:*)", - "Bash(grep:*)", - "Bash(find:*)", - "Bash(head:*)", - "Bash(tail:*)", - "Bash(wc:*)", - "Bash(cat:*)", - "Bash(python3:*)", - "Bash(node:*)", - "Bash(npm:*)", - "Bash(which:*)", - "Bash(sed:*)", - "Bash(awk:*)", - "Bash(sort:*)", - "Bash(uniq:*)", - "Bash(diff:*)", - "Bash(rg:*)", + "Bash(:*)", "Read", "Edit", "Write", diff --git a/server/src/io/fs/scaffold/templates.rs b/server/src/io/fs/scaffold/templates.rs index b21d316e..bd0ba577 100644 --- a/server/src/io/fs/scaffold/templates.rs +++ b/server/src/io/fs/scaffold/templates.rs @@ -70,31 +70,7 @@ setup wizard instructions and guide the user through it conversationally.\n"; pub(super) const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{ "permissions": { "allow": [ - "Bash(cargo:*)", - "Bash(git:*)", - "Bash(ls:*)", - "Bash(mkdir:*)", - "Bash(mv:*)", - "Bash(rm:*)", - "Bash(touch:*)", - "Bash(echo:*)", - "Bash(pwd:*)", - "Bash(grep:*)", - "Bash(find:*)", - "Bash(head:*)", - "Bash(tail:*)", - "Bash(wc:*)", - "Bash(cat:*)", - "Bash(python3:*)", - "Bash(node:*)", - "Bash(npm:*)", - "Bash(which:*)", - "Bash(sed:*)", - "Bash(awk:*)", - "Bash(rg:*)", - "Bash(diff:*)", - "Bash(sort:*)", - "Bash(uniq:*)", + "Bash(:*)", "Read", "Edit", "Write", diff --git a/server/src/io/fs/scaffold/tests.rs b/server/src/io/fs/scaffold/tests.rs index 9fd8797d..855a6378 100644 --- a/server/src/io/fs/scaffold/tests.rs +++ b/server/src/io/fs/scaffold/tests.rs @@ -614,24 +614,13 @@ fn scaffold_story_kit_claude_settings_uses_canonical_bash_syntax() { ); } - // Common safe commands must be allowlisted in canonical form. - for required in &[ - r#""Bash(cargo:*)""#, - r#""Bash(git:*)""#, - r#""Bash(ls:*)""#, - r#""Bash(cat:*)""#, - r#""Bash(grep:*)""#, - r#""Bash(find:*)""#, - r#""Bash(python3:*)""#, - r#""Bash(node:*)""#, - r#""Bash(npm:*)""#, - r#""Bash(rg:*)""#, - r#""Bash(sed:*)""#, - r#""Bash(awk:*)""#, - ] { - assert!( - settings.contains(required), - "settings.json missing required allowlist pattern: {required}" - ); - } + // The wildcard `Bash(:*)` must be present — covers all bash commands. + // (Previously this asserted a curated per-command list; replaced with a + // single wildcard since coders kept hitting auto-deny on patterns the + // list missed, and the per-command gate offers no real safety in this + // trusted single-user deployment.) + assert!( + settings.contains(r#""Bash(:*)""#), + "settings.json missing wildcard Bash allowlist: {settings}" + ); }