Skip to content

Bash Tool

The Bash Tool provides shell command execution with built-in safety validation. It is designed for AI agent workflows that need to run shell commands programmatically.

Class Overview

  • BashTool - Executes shell commands via subprocess.run with a built-in deny list that blocks known-dangerous patterns before execution.

Usage

Basic Usage

from toolregistry_hub import BashTool

# Execute a simple command
result = BashTool.execute("echo hello world")
print(result["stdout"])   # "hello world\n"
print(result["exit_code"])  # 0

# Execute with a working directory
result = BashTool.execute("ls -la", cwd="/tmp")

# Execute with a custom timeout (seconds)
result = BashTool.execute("long_running_script.sh", timeout=300)

Return Value

execute() returns a dict with the following keys:

Key Type Description
stdout str Captured standard output (truncated at 64 KB)
stderr str Captured standard error (truncated at 64 KB)
exit_code int Process exit code, or -1 on timeout
timed_out bool Whether the process was killed due to timeout

Handling Errors

# Non-zero exit code
result = BashTool.execute("ls /nonexistent")
if result["exit_code"] != 0:
    print(f"Error: {result['stderr']}")

# Timeout
result = BashTool.execute("sleep 60", timeout=5)
if result["timed_out"]:
    print("Command timed out")

# Dangerous command (raises ValueError)
try:
    BashTool.execute("rm -rf /")
except ValueError as e:
    print(f"Blocked: {e}")

Security

Built-in Deny List

BashTool includes a hardcoded deny list that blocks known-dangerous command patterns. This deny list cannot be disabled and serves as a safety floor.

Commands are segmented by shell operators (&&, ||, ;) and each segment is checked independently against the deny list.

Category Blocked Patterns
Destructive FS rm -rf /, rm -rf ~, rm -rf *, mkfs, dd if=, > /dev/sd*
Privilege escalation sudo, su -, chmod -R 777 /, chown -R
Code injection eval, exec, curl\|sh, wget\|sh
Fork bomb :(){ :\|:& };:
Git destructive git push --force, git reset --hard, git clean -f
System control shutdown, reboot, halt, kill -9 1

Design Rationale

The deny list is based on a survey of security approaches across 6 AI coding CLI tools (Claude Code, Codex, Aider, Kilo Code, Cline/Roo Code, Pi). It covers the most commonly blocked patterns found across the industry.

What this tool does NOT provide:

  • OS-level sandboxing (use containers or VM isolation at the deployment level)
  • Interactive approval prompts (MCP protocol limitation)
  • AST-based shell parsing (regex-based for v1)

MCP Server Endpoint

When running via the MCP server, this tool is exposed as:

POST /tools/bash/execute

Parameters:

Parameter Type Required Default Description
command string Yes - Shell command to execute
timeout integer No 120 Max seconds before kill
cwd string No null Working directory

API Reference

toolregistry_hub.bash_tool.BashTool

Shell command execution with built-in safety validation.

execute staticmethod

execute(command: str, timeout: int = 120, cwd: str | None = None) -> dict

Execute a shell command and return its output.

The command is first validated against a built-in deny list of known-dangerous patterns. If the command passes validation it is executed via subprocess.run with shell=True.

Parameters:

Name Type Description Default
command str

The shell command to execute.

required
timeout int

Maximum wall-clock seconds before the process is killed. Defaults to 120.

120
cwd str | None

Working directory for the command. If None, the current process working directory is used.

None

Returns:

Type Description
dict

A dict with keys:

dict
  • stdout (str): captured standard output (truncated at 64 KB).
dict
  • stderr (str): captured standard error (truncated at 64 KB).
dict
  • exit_code (int): process exit code, or -1 on timeout.
dict
  • timed_out (bool): whether the process was killed due to timeout.

Raises:

Type Description
ValueError

If the command matches a dangerous pattern.

FileNotFoundError

If cwd is specified but does not exist.

Source code in toolregistry_hub/bash_tool.py
@staticmethod
def execute(
    command: str,
    timeout: int = 120,
    cwd: str | None = None,
) -> dict:
    """Execute a shell command and return its output.

    The command is first validated against a built-in deny list of
    known-dangerous patterns.  If the command passes validation it is
    executed via ``subprocess.run`` with ``shell=True``.

    Args:
        command: The shell command to execute.
        timeout: Maximum wall-clock seconds before the process is
            killed.  Defaults to 120.
        cwd: Working directory for the command.  If ``None``, the
            current process working directory is used.

    Returns:
        A dict with keys:

        - ``stdout`` (str): captured standard output (truncated at 64 KB).
        - ``stderr`` (str): captured standard error (truncated at 64 KB).
        - ``exit_code`` (int): process exit code, or ``-1`` on timeout.
        - ``timed_out`` (bool): whether the process was killed due to
          timeout.

    Raises:
        ValueError: If the command matches a dangerous pattern.
        FileNotFoundError: If *cwd* is specified but does not exist.
    """
    # Validate safety
    _validate_command(command)

    # Validate cwd
    if cwd is not None:
        cwd_path = Path(cwd)
        if not cwd_path.is_dir():
            raise FileNotFoundError(f"Working directory does not exist: {cwd}")

    try:
        result = subprocess.run(
            command,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout,
            cwd=cwd,
        )
        return {
            "stdout": _truncate(result.stdout),
            "stderr": _truncate(result.stderr),
            "exit_code": result.returncode,
            "timed_out": False,
        }
    except subprocess.TimeoutExpired as exc:
        stdout = exc.stdout
        stderr = exc.stderr
        return {
            "stdout": _truncate(stdout if isinstance(stdout, str) else ""),
            "stderr": _truncate(stderr if isinstance(stderr, str) else ""),
            "exit_code": -1,
            "timed_out": True,
        }