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": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(cargo build:*)",
|
"Bash(cargo:*)",
|
||||||
"Bash(cargo check:*)",
|
"Bash(git:*)",
|
||||||
"Bash(git *)",
|
"Bash(ls:*)",
|
||||||
"Bash(ls *)",
|
"Bash(mkdir:*)",
|
||||||
"Bash(mkdir *)",
|
"Bash(mv:*)",
|
||||||
"Bash(mv *)",
|
"Bash(rm:*)",
|
||||||
"Bash(rm *)",
|
"Bash(touch:*)",
|
||||||
"Bash(touch *)",
|
|
||||||
"Bash(echo:*)",
|
"Bash(echo:*)",
|
||||||
"Bash(pwd *)",
|
"Bash(pwd:*)",
|
||||||
"Bash(grep:*)",
|
"Bash(grep:*)",
|
||||||
"Bash(find *)",
|
"Bash(find:*)",
|
||||||
"Bash(head *)",
|
"Bash(head:*)",
|
||||||
"Bash(tail *)",
|
"Bash(tail:*)",
|
||||||
"Bash(wc *)",
|
"Bash(wc:*)",
|
||||||
"Bash(cat *)",
|
"Bash(cat:*)",
|
||||||
|
"Bash(python3:*)",
|
||||||
|
"Bash(node:*)",
|
||||||
|
"Bash(npm:*)",
|
||||||
|
"Bash(which:*)",
|
||||||
|
"Bash(sed:*)",
|
||||||
|
"Bash(awk:*)",
|
||||||
|
"Bash(sort:*)",
|
||||||
|
"Bash(uniq:*)",
|
||||||
|
"Bash(diff:*)",
|
||||||
|
"Bash(rg:*)",
|
||||||
|
"Read",
|
||||||
"Edit",
|
"Edit",
|
||||||
"Write",
|
"Write",
|
||||||
|
"Glob",
|
||||||
|
"Grep",
|
||||||
"mcp__huskies__*"
|
"mcp__huskies__*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enabledMcpjsonServers": [
|
"enabledMcpjsonServers": ["huskies"]
|
||||||
"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#"{
|
pub(super) const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(cargo build:*)",
|
"Bash(cargo:*)",
|
||||||
"Bash(cargo check:*)",
|
"Bash(git:*)",
|
||||||
"Bash(git *)",
|
"Bash(ls:*)",
|
||||||
"Bash(ls *)",
|
"Bash(mkdir:*)",
|
||||||
"Bash(mkdir *)",
|
"Bash(mv:*)",
|
||||||
"Bash(mv *)",
|
"Bash(rm:*)",
|
||||||
"Bash(rm *)",
|
"Bash(touch:*)",
|
||||||
"Bash(touch *)",
|
|
||||||
"Bash(echo:*)",
|
"Bash(echo:*)",
|
||||||
"Bash(pwd *)",
|
"Bash(pwd:*)",
|
||||||
"Bash(grep:*)",
|
"Bash(grep:*)",
|
||||||
"Bash(find *)",
|
"Bash(find:*)",
|
||||||
"Bash(head *)",
|
"Bash(head:*)",
|
||||||
"Bash(tail *)",
|
"Bash(tail:*)",
|
||||||
"Bash(wc *)",
|
"Bash(wc:*)",
|
||||||
"Bash(cat *)",
|
"Bash(cat:*)",
|
||||||
|
"Bash(python3:*)",
|
||||||
|
"Bash(node:*)",
|
||||||
|
"Bash(npm:*)",
|
||||||
|
"Bash(which:*)",
|
||||||
|
"Bash(sed:*)",
|
||||||
|
"Bash(awk:*)",
|
||||||
|
"Bash(rg:*)",
|
||||||
|
"Bash(diff:*)",
|
||||||
|
"Bash(sort:*)",
|
||||||
|
"Bash(uniq:*)",
|
||||||
"Read",
|
"Read",
|
||||||
"Edit",
|
"Edit",
|
||||||
"Write",
|
"Write",
|
||||||
|
|||||||
@@ -592,3 +592,46 @@ fn scaffold_does_not_overwrite_existing_project_toml_with_components() {
|
|||||||
"scaffold should not overwrite existing project.toml"
|
"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