Summary
Harden the read-only bash classifier so it stops blocking harmless inspection commands and stops letting a real write through as "read-only".
Use case
Sub-agents run inside a read-only bash sandbox. The classifier currently has two correctness gaps:
-
False positives (over-blocking) — safe commands are rejected as non-read-only, forcing unnecessary confirmation or failing the sub-agent:
ls 2>&1, cat foo 2>&1 — 2>&1 duplicates a file descriptor; the & was mistaken for a background operator or write redirect.
ls *.txt — unquoted globs were blocked, even though glob expansion never mutates the filesystem.
find . -exec grep foo {} \; — -exec was blanket-blocked regardless of the embedded command.
-
False negative (under-blocking / security) — >&word where word is not a digit redirects stdout to a file in bash (verified: echo x >& /tmp/f writes the file), but the new >& allowance treated every >& as fd duplication and classified echo x >& /tmp/f as read-only. That bypasses the sub-agent's only filesystem-write gate.
Proposed solution
- fd redirects: treat
>&N (digit, e.g. 2>&1, >&2) and >&- (close) as read-only fd duplication; treat >&word (non-digit) as a write redirect so echo x >& /tmp/f is blocked. Same fix applied in split_compound_segments and has_background_operator so 2>&1 is not split or misread as a background job.
- globs: allow unquoted globs — writes like
rm *.txt / cp *.x /d are still caught by the blocked-command list, and echo *.x > f by the write-redirect check.
- find -exec: recursively classify the embedded command —
find -exec grep/wc/head {} \; passes, find -exec rm {} \; is blocked; -delete stays destructive. Handles both \; and + terminators.
Alternatives considered
- Keep blocking globs and
find -exec outright — leaves sub-agents unable to run ordinary inspection commands, defeating the point of a read-only sandbox.
- Whitelist specific
find -exec commands — brittle and maintenance-heavy; recursive classification is general.
Additional context
Regression tests added: 2>&1/>&2/>&- read-only; >& /tmp/f, >&/tmp/f, >& f blocked; find -exec rm blocked, find -exec grep allowed; unquoted glob allowed. The >&word write bypass was caught during review of the fd-redirect support and is fixed in the same change rather than shipped as a hole.
Summary
Harden the read-only bash classifier so it stops blocking harmless inspection commands and stops letting a real write through as "read-only".
Use case
Sub-agents run inside a read-only bash sandbox. The classifier currently has two correctness gaps:
False positives (over-blocking) — safe commands are rejected as non-read-only, forcing unnecessary confirmation or failing the sub-agent:
ls 2>&1,cat foo 2>&1—2>&1duplicates a file descriptor; the&was mistaken for a background operator or write redirect.ls *.txt— unquoted globs were blocked, even though glob expansion never mutates the filesystem.find . -exec grep foo {} \;—-execwas blanket-blocked regardless of the embedded command.False negative (under-blocking / security) —
>&wordwherewordis not a digit redirects stdout to a file in bash (verified:echo x >& /tmp/fwrites the file), but the new>&allowance treated every>&as fd duplication and classifiedecho x >& /tmp/fas read-only. That bypasses the sub-agent's only filesystem-write gate.Proposed solution
>&N(digit, e.g.2>&1,>&2) and>&-(close) as read-only fd duplication; treat>&word(non-digit) as a write redirect soecho x >& /tmp/fis blocked. Same fix applied insplit_compound_segmentsandhas_background_operatorso2>&1is not split or misread as a background job.rm *.txt/cp *.x /dare still caught by the blocked-command list, andecho *.x > fby the write-redirect check.find -exec grep/wc/head {} \;passes,find -exec rm {} \;is blocked;-deletestays destructive. Handles both\;and+terminators.Alternatives considered
find -execoutright — leaves sub-agents unable to run ordinary inspection commands, defeating the point of a read-only sandbox.find -execcommands — brittle and maintenance-heavy; recursive classification is general.Additional context
Regression tests added:
2>&1/>&2/>&-read-only;>& /tmp/f,>&/tmp/f,>& fblocked;find -exec rmblocked,find -exec grepallowed; unquoted glob allowed. The>&wordwrite bypass was caught during review of the fd-redirect support and is fixed in the same change rather than shipped as a hole.