Skip to content

fix(security): tokenize the execute_bash destructive-command screen (#128) - #195

Open
rifkir23 wants to merge 1 commit into
HKUDS:mainfrom
rifkir23:fix/command-guard-tokenized-blocklist
Open

fix(security): tokenize the execute_bash destructive-command screen (#128)#195
rifkir23 wants to merge 1 commit into
HKUDS:mainfrom
rifkir23:fix/command-guard-tokenized-blocklist

Conversation

@rifkir23

Copy link
Copy Markdown
Contributor

Follow-up to #128.

Problem

The execute_bash guard in tools/code_implementation_server.py decides whether a command is destructive by matching it as a substring of the raw command string:

dangerous_commands = ["rm -rf", "sudo", "chmod 777", "mkfs", "dd if="]
normalized_command = re.sub(r"\s+", " ", command).lower()
if any(d in normalized_command for d in dangerous_commands):
    block

The whitespace-normalization that was already added here helps (RM -rf no longer slips through), but a normalized substring check is still bypassable, and issue #128 explicitly kept this open to track it:

command classified as reality
rm -r -f / safe ❌ split short flags — destructive
rm --recursive --force / safe ❌ long flags — destructive
chmod 0777 file safe ❌ leading zero — same as 777
touch rm-rf-notes.txt blocked benign, only contains the text

So the layer both misses real destructive commands and false-positives on harmless ones.

Change

Add core/harness/command_guard.py with screen_command(command) -> str | None. It splits the command on the shell control operators (; && || | & and newlines) and tokenizes each segment with shlex, then matches on the argv (command name + flags) rather than substrings. That:

  • treats -rf, -fr, -r -f, --recursive --force as equivalent (flag order / combination / spelling no longer matter);
  • recognizes numeric (777, 0777) and symbolic (a+rwx) chmod forms;
  • catches a destructive stage hidden in a pipeline or sequence (echo hi && rm -rf /, cd /tmp; rm -rf build);
  • stops false-positiving on benign commands that merely contain the text.

execute_bash now calls screen_command in place of the substring list.

This is defense-in-depth, not the boundary

I have deliberately not touched the sandbox. As #128 concluded, the workspace sandbox in core.harness.sandbox is the real enforcement; this screen is the cheap first pass in front of it. It never raises, and any command it cannot tokenize (unbalanced quotes, etc.) is passed straight through to the sandbox unchanged — the goal is to make the shallow layer honestly catch what it claims to, without pretending to be the boundary.

Tests

tests/test_command_guard.py covers the historical bypasses, the old false-positives, multi-segment commands, empty input, and unparseable input. ruff check and ruff format --check are clean on the new and modified files.

…S#128)

The execute_bash guard matched destructive commands as substrings of the
raw command string. Even after whitespace normalization it stayed
bypassable: split short flags (rm -r -f), long flags
(rm --recursive --force), and numeric permission variants (chmod 0777)
all slip through, while benign commands that merely contain the text
(touch rm-rf-notes.txt) are falsely blocked.

Replace it with core.harness.command_guard.screen_command, which splits
the command on shell operators (; && || | &) and tokenizes each segment
with shlex, matching on argv (command name + flags) instead of
substrings. This closes the flag-order / flag-spelling / numeric gaps and
also catches a destructive stage hidden in a pipeline or sequence
(echo hi && rm -rf /).

This is cheap defense-in-depth, not the security boundary: the workspace
sandbox remains the real enforcement, and any command the screen cannot
parse is passed through to it unchanged.

Add tests/test_command_guard.py covering the historical bypasses, the old
false-positives, multi-segment commands, empty input, and unparseable
input.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant