Development record
The guard now refuses an unquoted brace group rather than expanding it (itd-156/spc-49 scoped the expander out), so every ordinary shell brace an agent writes is blocked: mkdir -p foo/{a,b}, cp x{,.bak}, rm -rf dir{1..9}. That is the intended fail-closed posture — a word whose argv the guard cannot compute is one it cannot check — but it is a real usability cost on the PreToolUse path, paid on every command that uses a shell convenience nobody meant as a hazard. The scoped follow-up is the bounded expander the intent already names: enumerate the Cartesian product of a group's alternatives (nested groups and {a..z} ranges included) under a hard cap on the number of words produced, check each expansion against the registry, and refuse only when the cap is hit or an expansion matches a blocker. Detector: a corpus of everyday brace commands whose verdicts should be allow; acceptance: mkdir -p foo/{a,b} allows while git push {--force,} origin main still blocks.
The guard now refuses an unquoted brace group rather than expanding it (itd-156/spc-49 scoped the expander out), so every ordinary shell brace an agent writes is blocked: mkdir -p foo/{a,b}, cp x{,.bak}, rm -rf dir{1..9}. That is the intended fail-closed posture — a word whose argv the guard cannot compute is one it cannot check — but it is a real usability cost on the PreToolUse path, paid on every command that uses a shell convenience nobody meant as a hazard. The scoped follow-up is the bounded expander the intent already names: enumerate the Cartesian product of a group's alternatives (nested groups and {a..z} ranges included) under a hard cap on the number of words produced, check each expansion against the registry, and refuse only when the cap is hit or an expansion matches a blocker. Detector: a corpus of everyday brace commands whose verdicts should be allow; acceptance: mkdir -p foo/{a,b} allows while git push {--force,} origin main still blocks.
Grounds
- pursued: the guard checks the argv bash builds from a brace group; shown wrong by a word whose expansion differs from bash's (TestBraceExpansionMatchesBash) or an everyday group refused (TestEverydayBraceCommandsAllow)
Frontmatter
| id | iss-2608282026038930 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2608282026038930-the-guard-now-refuses-an-unquoted-brace.md
open on GitHub ↗ · commit history ↗
mentions2
- itd-156The guard tokenizer does not perform brace expansion, so a flag wrapped in a single-element brace group with an empty alternative (git push {--force,} origin main) expands in bash to byte-identical argv --force yet the guard reads the literal token {--force,} and allows it — a Tier-1 blocker miss of the same mutate-the-flag-token shape as the round-6 redirection fix. Distinct from the $'...' quoting gap (this is expansion, not quoting, and breaks no written invariant); recorded for a scoped follow-up because a correct bounded brace-expander is larger than this round's scope.
- spc-49the-guard-tokenizer-does-not-perform-brace-expansion-so-a-fl