Skip to content

[FEATURE] add Exec tool as more secure alternative to Bash tool #6046

@aspiers

Description

@aspiers

TL;DR summary

I propose a new Exec tool which invokes subprocess commands directly, not via bash or any other shell.

Rationale: Bash tool is inherently unsafe

https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules states:

Claude Code is aware of shell operators (like &&) so a prefix match rule like Bash(safe-cmd:*) won’t give it permission to run the command safe-cmd && other-cmd

However this is very vague and fundamentally unsafe. It's impossible for any kind of pattern-matching approach to guarantee that arbitrary shell code is both secure and not bypassing file access controls imposed on the agent.

For example, even if you ban operators like &&, ||, and ;, the agent can still construct Bash commands to circumvent these which would be allowed by innocent looking patterns like npm run test:* or git status:*, e.g.

npm run test <(rm -rf $HOME/*)
git status $(rm -rf $HOME/*)

This issue is demonstrated in depth by the report in #4956 which lists many other techniques for bypassing basic checks.

The above problems are a symptom of Claude's fundamentally flawed approach which only allows command invocation via the Bash tool. It has been well-known for decades that allowing semi-arbitrary shell code is a significant vector for attacks and accidental side effects.

Proposed (partial) solution: a new Exec tool

It is also well-established for decades that when building mechanisms which launch new processes, it's best practice to simply avoid using shells to launch them.

For example, Python defaults to invoking subprocess commands directly rather than via a shell.

So in many cases, a similar approach of avoiding usage of bash or any other shell should work fine for Claude Code.

Therefore I propose a new Exec tool which invokes commands directly, not via bash or any shell.

For example, with this new tool, even if combined with Claude Code's existing very limited :* pattern matching scheme, if we put the following in settings.json:

{
  "permissions": {
    "allow": [
      "Exec(npm run test:*)"
    ]
  }
}

then it would guarantee that anything after the npm run test would be supplied as arguments to the npm command and therefore could not directly launch any other subprocess. So npm run test <(rm -rf $HOME/*) would have the equivalent effect of running this from a shell:

npm run test '<(rm' '-rf' '$HOME/*)'

which doesn't make sense, but at least it would be safe.

Beyond an Exec tool: the need for a full sandboxing solution

Whilst the above proposal for a new Exec tool is serious, would certainly offer Claude Code users better protection in many circumstances, and should be easy to implement, it can never be a full solution. For example, here are a few ways to edit an arbitrary file without requiring any shell at all:

sed -i -e '... some editing code ...' foo.txt
python -c '... some editing code ...'
perl -i -pe '... some editing code ...' foo.txt

and of course the latter two examples allow not just file editing but arbitrary execution.

And it doesn't always make sense to add these commands to the deny list because there are perfectly valid use cases for invoking those. There is no pattern-matching method which can determine whether one of these commands is legitimate or not.

In general, attempting to determine the safety or side effects of arbitrary commands by performing pattern matching or even complex static analysis on those commands is doomed to fail. A much more effective approach is to ensure that the commands are run within a sandboxed environment which limits what can be done at the OS level.

One proposal along these lines has already been made:

However there are probably other approaches which could also work, e.g. on Linux apparmor and selinux are alternative sandboxing mechanisms.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions