Skip to content

fix(tools): enforce shell blocklist before permission policy check#1529

Merged
bug-ops merged 1 commit intomainfrom
shell-blocklist-bypass
Mar 10, 2026
Merged

fix(tools): enforce shell blocklist before permission policy check#1529
bug-ops merged 1 commit intomainfrom
shell-blocklist-bypass

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Mar 10, 2026

Summary

  • find_blocked_command() was in the else branch of the PermissionPolicy check in ShellExecutor::execute_inner(), so it never ran in normal operation (a PermissionPolicy is always set when autonomy_level is configured in agent_setup.rs)
  • This silently bypassed the entire blocklist — blocked_commands, DEFAULT_BLOCKED, and allow_network = false — for all autonomy levels, including subshell bypass vectors ($(...), backtick, <(...))
  • Fix: move find_blocked_command() unconditionally before the policy check so it acts as a hard security gate; PermissionPolicy remains the UX/confirmation layer on top

Changes

  • crates/zeph-tools/src/shell.rs: restructure execute_inner() permission/blocklist logic
  • 3 new regression tests: blocklist not bypassed by permissive policy, DEFAULT_BLOCKED not bypassed by AutonomyLevel::Full, confirm-commands path preserved when no policy is set
  • CHANGELOG.md: document the fix under [Unreleased]

Test plan

  • cargo nextest run -p zeph-tools --lib — all 3 new regression tests pass
  • cargo clippy --workspace --features full -- -D warnings — 0 warnings
  • Full suite: 5047 passed

Fixes #1525

find_blocked_command() was in the else-branch of the PermissionPolicy
check, so it never ran in normal operation (PermissionPolicy is always
set when autonomy_level is configured). This silently bypassed the
entire blocklist — blocked_commands, DEFAULT_BLOCKED, and
allow_network=false — for all autonomy levels.

Move find_blocked_command() unconditionally before the policy check so
it acts as a hard security gate regardless of the attached policy.
PermissionPolicy remains the UX/confirmation layer on top.

Adds 3 regression tests: blocklist not bypassed by permissive policy,
DEFAULT_BLOCKED not bypassed by full autonomy, confirm-commands path
preserved when no policy is set.

Fixes #1525
@bug-ops bug-ops added bug Something isn't working security Security-related issue labels Mar 10, 2026
@github-actions github-actions bot added documentation Improvements or additions to documentation rust Rust code changes size/M Medium PR (51-200 lines) labels Mar 10, 2026
@bug-ops bug-ops enabled auto-merge (squash) March 10, 2026 21:00
@bug-ops bug-ops merged commit 6c85a22 into main Mar 10, 2026
18 checks passed
@bug-ops bug-ops deleted the shell-blocklist-bypass branch March 10, 2026 21:10
bug-ops added a commit that referenced this pull request Mar 13, 2026
Asserts that execute_confirmed() with a blocklisted command returns
ToolError::Blocked, covering the code path verified in #1529.

Closes #1530.
bug-ops added a commit that referenced this pull request Mar 13, 2026
Asserts that execute_confirmed() with a blocklisted command returns
ToolError::Blocked, covering the code path verified in #1529.

Closes #1530.
bug-ops added a commit that referenced this pull request Mar 13, 2026
Asserts that execute_confirmed() with a blocklisted command returns
ToolError::Blocked, covering the code path verified in #1529.

Closes #1530.
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 security Security-related issue size/M Medium PR (51-200 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shell blocklist bypassed when PermissionPolicy is set (autonomy_level != None)

1 participant