fix(883): canonical Bash(:*) syntax in scaffold settings template
Claude Code 2.1.123+ honours wildcard Bash allowlist patterns only in the canonical form `Bash(cmd:*)`. The space form `Bash(cmd *)` falls through to prompt_permission and gets auto-denied in agent mode, breaking spawned coders. - Rewrite all `Bash(cmd *)` patterns in STORY_KIT_CLAUDE_SETTINGS to the colon form. - Replace separate `Bash(cargo build:*)` / `Bash(cargo check:*)` with a single `Bash(cargo:*)`. - Add commonly-needed patterns: python3, node, npm, which, sed, awk, rg, diff, sort, uniq. - Patch the live project-root .claude/settings.json so the running system picks up the fix immediately (rebuilt scaffolds will match). - Add regression test asserting no `Bash(... *)` patterns survive and required common commands are present.
This commit is contained in:
+27
-17
@@ -1,28 +1,38 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cargo build:*)",
|
||||
"Bash(cargo check:*)",
|
||||
"Bash(git *)",
|
||||
"Bash(ls *)",
|
||||
"Bash(mkdir *)",
|
||||
"Bash(mv *)",
|
||||
"Bash(rm *)",
|
||||
"Bash(touch *)",
|
||||
"Bash(cargo:*)",
|
||||
"Bash(git:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(mv:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(touch:*)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(pwd *)",
|
||||
"Bash(pwd:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(find *)",
|
||||
"Bash(head *)",
|
||||
"Bash(tail *)",
|
||||
"Bash(wc *)",
|
||||
"Bash(cat *)",
|
||||
"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:*)",
|
||||
"Read",
|
||||
"Edit",
|
||||
"Write",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"mcp__huskies__*"
|
||||
]
|
||||
},
|
||||
"enabledMcpjsonServers": [
|
||||
"huskies"
|
||||
]
|
||||
"enabledMcpjsonServers": ["huskies"]
|
||||
}
|
||||
|
||||
@@ -70,22 +70,31 @@ setup wizard instructions and guide the user through it conversationally.\n";
|
||||
pub(super) const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cargo build:*)",
|
||||
"Bash(cargo check:*)",
|
||||
"Bash(git *)",
|
||||
"Bash(ls *)",
|
||||
"Bash(mkdir *)",
|
||||
"Bash(mv *)",
|
||||
"Bash(rm *)",
|
||||
"Bash(touch *)",
|
||||
"Bash(cargo:*)",
|
||||
"Bash(git:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(mkdir:*)",
|
||||
"Bash(mv:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(touch:*)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(pwd *)",
|
||||
"Bash(pwd:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(find *)",
|
||||
"Bash(head *)",
|
||||
"Bash(tail *)",
|
||||
"Bash(wc *)",
|
||||
"Bash(cat *)",
|
||||
"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:*)",
|
||||
"Read",
|
||||
"Edit",
|
||||
"Write",
|
||||
|
||||
@@ -592,3 +592,46 @@ fn scaffold_does_not_overwrite_existing_project_toml_with_components() {
|
||||
"scaffold should not overwrite existing project.toml"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaffold_story_kit_claude_settings_uses_canonical_bash_syntax() {
|
||||
// Regression for bug 883. Claude Code 2.1.123+ honours wildcard Bash
|
||||
// allowlist patterns only in the canonical form `Bash(cmd:*)`. The space
|
||||
// form `Bash(cmd *)` falls through to prompt_permission and gets
|
||||
// auto-denied in agent mode, breaking spawned coders.
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path(), 3001).unwrap();
|
||||
|
||||
let settings = fs::read_to_string(dir.path().join(".claude/settings.json")).unwrap();
|
||||
|
||||
// No `Bash(... *)` (space before asterisk) patterns may survive.
|
||||
let space_form = regex::Regex::new(r#""Bash\([^"]* \*\)""#).unwrap();
|
||||
if let Some(m) = space_form.find(&settings) {
|
||||
panic!(
|
||||
"settings.json contains non-canonical Bash pattern: {} \
|
||||
(use `Bash(cmd:*)` form instead)",
|
||||
m.as_str()
|
||||
);
|
||||
}
|
||||
|
||||
// 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}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user