Skip to content

fix(tools): harden shell command pattern-matching against flag-order and blocklist bypasses#6521

Merged
bug-ops merged 1 commit into
mainfrom
fix/6497-shell-guard-bypass
Jul 19, 2026
Merged

fix(tools): harden shell command pattern-matching against flag-order and blocklist bypasses#6521
bug-ops merged 1 commit into
mainfrom
fix/6497-shell-guard-bypass

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • Generalizes the tokenized recursive/force-flag detector already used for .git/worktrees protection (is_blocked_rm_worktrees) to the general root/home rm guard, closing bypasses via reordered (-fr), separated (-r -f), long-form (--force), and post-path flags (rm / -f). Restores the original literal substring patterns in DESTRUCTIVE_PATTERNS alongside the new detector as belt-and-suspenders coverage for chained (cd /tmp && rm -rf /), prefixed (sudo rm -rf /), and non-rm-anchored invocations the tokenized detector alone can't see.
  • Expands the sub-agent NetworkScope::Deny / allow_network = false blocklist (NETWORK_COMMANDS) from 5 to 16 entries, covering ssh/scp/rsync, openssl s_client, socat, common interpreter one-liners (python3 -c, perl -e, ruby -e), and bash's /dev/tcp//dev/udp pseudo-devices via a new embedded-substring fallback matcher.
  • Unifies ShellExecutor::find_blocked_command (the real bash-execution dispatch path) with check_blocklist's matching logic via a shared match_blocklist_tokens helper — these had drifted into separate, unsynchronized implementations, so the hardening above now reaches the actual bash tool dispatch path, not just external callers like NetworkDenyToolExecutor/ACP terminal.
  • Doc comments on NETWORK_COMMANDS and NetworkDenyToolExecutor now honestly disclose residual, out-of-scope bypass vectors (flag-insertion before -c/-e, versioned interpreter names, non-transparent wrapper commands) rather than implying full coverage.

Closes #6497
Closes #6473

Test plan

  • cargo +nightly fmt --check — clean
  • cargo clippy --profile ci -p zeph-tools -p zeph-subagent --all-targets -- -D warnings — clean, 0 warnings
  • cargo nextest run --config-file .github/nextest.toml -p zeph-tools -p zeph-subagent --no-fail-fast — 1955/1955 passed (22 new tests)
  • cargo test --doc -p zeph-tools — 34/34 passed
  • RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps -p zeph-tools -p zeph-subagent — clean
  • gitleaks protect --staged --no-banner --redact — no leaks
  • New regression tests cover every bypass vector from both issues' reproduction steps, including a follow-up round after an adversarial critique caught a coverage regression (chained/prefixed/recursive-only-on-syspath/home-subpath rm forms), with negative tests confirming no over-broad false positives (rm -f /tmp/x.log, openssl enc ..., relative paths all still allowed)

@github-actions github-actions Bot added documentation Improvements or additions to documentation rust Rust code changes bug Something isn't working size/XL Extra large PR (500+ lines) labels Jul 19, 2026
@bug-ops
bug-ops enabled auto-merge (squash) July 19, 2026 21:31
@bug-ops
bug-ops force-pushed the fix/6497-shell-guard-bypass branch 2 times, most recently from ab9ba9f to 2a8b1be Compare July 19, 2026 21:40
…and blocklist bypasses

Generalize the tokenized recursive/force-flag detector already used for
.git/worktrees protection to the general root/home rm guard, closing
bypasses via reordered (-fr), separated (-r -f), long-form (--force), and
post-path flags. Restore the original literal substring patterns
alongside the new detector as belt-and-suspenders coverage for chained,
prefixed, and non-rm-anchored invocations. Expand the sub-agent
network-deny blocklist to cover ssh/scp/rsync, openssl s_client, socat,
common interpreter one-liners, and bash's /dev/tcp//dev/udp pseudo-devices.
Unify ShellExecutor's dispatch-path blocklist check with the shared
matcher so the hardening reaches the actual bash execution path, not just
external callers.

Closes #6497
Closes #6473
@bug-ops
bug-ops force-pushed the fix/6497-shell-guard-bypass branch from 2a8b1be to 7200d8d Compare July 19, 2026 21:55
@bug-ops
bug-ops merged commit ad70e2a into main Jul 19, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6497-shell-guard-bypass branch July 19, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

1 participant