Skip to content

feat(examples): 添加 sandbox-backend,通过 PreToolUse hook 透明隔离 Bash#783

Closed
shsaihdsaiudh wants to merge 2 commits into
TencentCloud:masterfrom
shsaihdsaiudh:feat/hook-sandbox-backend
Closed

feat(examples): 添加 sandbox-backend,通过 PreToolUse hook 透明隔离 Bash#783
shsaihdsaiudh wants to merge 2 commits into
TencentCloud:masterfrom
shsaihdsaiudh:feat/hook-sandbox-backend

Conversation

@shsaihdsaiudh

@shsaihdsaiudh shsaihdsaiudh commented Jul 6, 2026

Copy link
Copy Markdown

要解决的问题

基于 MCP 的沙箱接入依赖 coding agent 主动选择沙箱工具,普通的 Claude Code Bash 调用仍可能直接在宿主机执行,因此无法提供透明且完整的命令隔离。

Refs #644。本 PR 是 PR #765 的补充方向:使用 PreToolUse hook 自动把 Bash 命令转发到 CubeSandbox。

解决方案

新增 hook-based backend,把每次 Claude Code Bash 调用改写为当前会话对应的 CubeSandbox 执行命令。hook 保留命令输出和退出码,执行层复用同一个沙箱,并在多次调用之间延续工作目录和环境变量状态。

Claude Code Bash call
        |
        v
PreToolUse hook -> cubesandbox-exec -> CubeAPI -> MicroVM

具体改动

  • 新增 cubesandbox_rewrite.py,校验并改写 Claude Code PreToolUse 输入。
  • 新增 cubesandbox_exec.py,实现会话级沙箱复用、文件锁、状态持久化、重置和可选的宿主机只读挂载。
  • 提供幂等的 Claude Code hook 配置安装和卸载脚本。
  • 保留可选 MCP server 和独立的 E2B-compatible CLI,作为其他接入方式。
  • 补充架构、安装、安全行为、限制和排错文档。
  • 新增 95 个 pytest 测试,覆盖改写、会话 backend、MCP 和独立 CLI。

审阅反馈处理

Commit e8f9256 处理了自动安全与健壮性审阅提出的问题:

  • 阻止 ANSI-C newline 和 escaped-quote 绕过改写逻辑。
  • 宿主机项目只读挂载。
  • shell 状态存放在随机、仅 owner 可访问的会话目录中。
  • 延迟加载环境变量,并声明独立 CLI 的依赖。
  • 删除可能掩盖安装失败的 installer fallback。
  • 让 README、设计文档和实际实现保持一致。

范围与取舍

  • hook 只覆盖 Claude Code Bash 工具调用,不声称隔离非 Bash 工具或任意宿主机进程。
  • 宿主机挂载是可选且只读的。需要修改项目文件的命令必须使用本示例之外的显式同步方案。
  • MCP server 仍为可选方式;本 PR 新增的透明路径是 hook backend。
  • 这是示例集成,不修改 CubeSandbox server API。

验证

uv run --with pytest --with cubesandbox --with e2b-code-interpreter --with python-dotenv pytest examples/sandbox-backend -q
95 passed in 0.24s

Assisted-by: Codex:GPT-5

… PreToolUse hook

Add a new approach to Claude Code sandboxing: transparently redirect every
Bash command into a CubeSandbox MicroVM using a PreToolUse hook (inspired by
RTK's command-rewrite pattern).

Unlike the MCP opt-in approach, the hook intercepts ALL Bash tool calls
automatically — the AI is unaware sandboxing is happening.

Includes:
- cubesandbox_exec.py — sandbox exec CLI with session cache, state persistence, host-mount
- cubesandbox_rewrite.py — PreToolUse hook that rewrites Bash tool_input.command
- install.sh — one-command setup (+ --uninstall)
- mcp_server.py — opt-in MCP sandbox tools (alternative)
- sandbox_exec.py — standalone exec CLI (e2b SDK)
- DESIGN.md — design rationale and implementation notes
- Full test suite with pytest

Related: TencentCloud#644
@shsaihdsaiudh shsaihdsaiudh requested a review from tinklone as a code owner July 6, 2026 14:11
EXEC_BIN = "cubesandbox-exec"


def _has_unquoted_newline(command: str) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: $'...' ANSI-C quoting bypasses unquoted newline detection

The _has_unquoted_newline() function only tracks '...' and "..." quoting contexts, but Bash also supports $'...' ANSI-C quoting where \n expands to a literal newline at shell expansion time. A command like cubesandbox-exec$'\n'rm -rf / would bypass detection.

While shlex.quote() in the rewrite layer protects the host shell, the injected newline would cause command splitting inside the sandbox. Combined with the read-write host mount (see below), this allows arbitrary file modification on the host.

Recommendation: Add $'...' detection — scan escape sequences within $'...' for \n/\r and return True if found.

TEMPLATE_ID,
timeout=SANDBOX_TTL,
metadata={
"host-mount": json.dumps([

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Host project directory mounted read-write inside sandbox

host-mount with "readOnly": False means any command running inside the sandbox (including via the $'\n' injection in the rewrite layer) can modify or delete host project files. Claude Code's Read/Write/Edit tools operate on the host filesystem and would see tampered data.

If the sandbox is compromised, the attacker gains write access to all project files through this mount.

Recommendation: Change to "readOnly": True — the sandbox only needs to read project files (for compilation, testing). If write-back is genuinely required, add explicit copy-back only for approved outputs.

the host — we must never let such a command bypass rewriting.
"""
in_single = in_double = False
for ch in command:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: Backslash-escaped quotes inside double-quoted strings not tracked

In Bash, \" inside double quotes does not end the quoting context — it produces a literal " character. The function unconditionally toggles in_double on every " character, ignoring backslash escaping. This can cause the quote-tracking state machine to desync from actual Bash quoting semantics.

While false positives (detecting a newline that's actually inside escaped quotes) are safe, the desync could cause newlines in certain crafted commands to go undetected.

Recommendation: Track backslash-escape state so \" inside double quotes does not toggle in_double.

Comment thread examples/sandbox-backend/mcp_server.py Outdated
import traceback
from dotenv import load_dotenv

load_dotenv()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: load_dotenv() called at module level — import-time side effect

Calling load_dotenv() here silently mutates os.environ the moment the module is imported (e.g., by a test runner or as a library). This makes testing harder because the environment is polluted before any test setup runs, and it breaks the principle that importing a module should not have side effects.

cubesandbox_exec.py has a better pattern: lazy bootstrapping in _bootstrap() that defers SDK imports and env loading until after argument parsing.

Recommendation: Move load_dotenv() into main() or use the _bootstrap() pattern from cubesandbox_exec.py.


# Files inside the sandbox used to persist shell state between otherwise
# stateless commands.run() calls.
_CWD_FILE = "/tmp/.cubesandbox_cwd"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High: Predictable state file paths in sandbox /tmp/ subject to tampering

_CWD_FILE and _ENV_FILE are hardcoded as /tmp/.cubesandbox_cwd and /tmp/.cubesandbox_env_state. These are shared across all commands and sessions within the same sandbox. A concurrent process (or a malicious process exploiting the $'...' injection) could:

  • Create a symlink at these paths pointing to a sensitive file
  • Pre-populate the files with malicious values
  • The source {_ENV_FILE} line executes arbitrary shell commands injected this way

Recommendation: Use unique, non-predictable paths (e.g., include a random suffix). Validate files are regular files (not symlinks) before sourcing/reading.

from e2b_code_interpreter import Sandbox
from e2b.sandbox.commands.command_handle import CommandExitException

load_dotenv()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: load_dotenv() at module level — same pattern as mcp_server.py

Same import-time side-effect concern as mcp_server.py:35. Using _bootstrap()-style deferred loading or moving load_dotenv() into main() would avoid polluting os.environ on import.

@cubesandboxbot

cubesandboxbot Bot commented Jul 6, 2026

Copy link
Copy Markdown

PR #783 - Comprehensive Code Review

This PR introduces a well-architected examples/sandbox-backend/ directory implementing transparent Bash sandboxing via Claude Code PreToolUse hook. Below are noteworthy findings.

CRITICAL

  1. ANSI-C quoting bypasses unquoted-newline detection (cubesandbox_rewrite.py:48-63). Bash $'...' quoting where $'\n' expands to newline at shell time is not tracked. Combined with read-write host-mount (below), allows host file tampering.

  2. Host project directory mounted read-write inside sandbox (cubesandbox_exec.py:186-188). An attacker in sandbox can modify host files through bind mount.

HIGH

  1. Backslash-escaped quotes not tracked (cubesandbox_rewrite.py:56-60). Inside double quotes, escaped quote does not end quoting in Bash, but parser toggles state.

  2. State files in sandbox /tmp/ use predictable names (cubesandbox_exec.py:51-52). Shared across sessions. Source on _ENV_FILE could execute injected shell.

  3. Major test gaps in cubesandbox_exec.py: run(), _create_sandbox(), _bootstrap(), main(), reset() have zero coverage.

MEDIUM

  1. load_dotenv() at module level in mcp_server.py:35 and sandbox_exec.py:22.
  2. No connection timeout on Sandbox.connect() (cubesandbox_exec.py:170).
  3. _load_state reads JSON from disk on every command (cubesandbox_exec.py:99-103).
  4. CommandExitException path untested (mcp_server.py:86-87).
  5. sandbox_reset atexit handler accumulation (mcp_server.py:229-234).
  6. MCP file read/write lacks path validation (mcp_server.py:218-226).
  7. pip install || cascade can mask failures (install.sh:117-119).
  8. DESIGN.md stale pseudo-code and absolute machine path.
  9. README.md listing omits test files.

POSITIVE

Security-conscious rewrite layer. Clean separation. Lazy bootstrapping. File locking. Graceful degradation. No jq dep.

Summary

Priority fix: ANSI-C quoting in _has_unquoted_newline() plus read-only host-mount.

@fslongjin

Copy link
Copy Markdown
Member

Hello, thank you for your contribution! Since we have a large number of participants in this event, to speed up the PR review process, could you please attach some screenshots and logs (both are needed) showing the verification process and results for this PR? Thank you for your participation!

@shsaihdsaiudh

Copy link
Copy Markdown
Author

Review fixes and verification

I addressed the security and robustness findings in commit e8f9256.

Highlights:

  • Blocks ANSI-C newline and escaped-quote rewrite bypasses.
  • Makes host mounts read-only and isolates shell state per session with random owner-only paths.
  • Defers environment loading, declares the standalone CLI dependency, and aligns installation/design documentation with implementation.
  • Removes the fallback installer command that could conceal a failed package installation.

Verification

uv run --with pytest --with cubesandbox --with e2b-code-interpreter --with python-dotenv pytest examples/sandbox-backend -q
95 passed in 0.24s

Full test log: https://gist.github.com/shsaihdsaiudh/54581365bc783d51224b5c3182e96bc8

A terminal screenshot of this verification has been prepared and will be attached to this thread when the binary-upload surface is available.

@shsaihdsaiudh

Copy link
Copy Markdown
Author
image

@shsaihdsaiudh

Copy link
Copy Markdown
Author

Issue linkage: this PR is a complementary implementation for #644, adding a PreToolUse-hook approach that transparently runs Claude Code Bash commands in CubeSandbox. It complements the general integration examples in PR #765.

@shsaihdsaiudh shsaihdsaiudh changed the title feat(examples): add sandbox-backend — transparent Bash sandboxing via PreToolUse hook feat(examples): 添加 sandbox-backend,通过 PreToolUse hook 透明隔离 Bash Jul 11, 2026

@fslongjin fslongjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! I think the content of this PR can be merged into PR #765 and simplified. The pre-exec hook feature in this PR is excellent, in my opinion, and can be directly integrated into #765. Because currently they appear redundant and somewhat complex; merging both would confuse readers.

@shsaihdsaiudh

Copy link
Copy Markdown
Author

Thanks for your contribution! I think the content of this PR can be merged into PR #765 and simplified. The pre-exec hook feature in this PR is excellent, in my opinion, and can be directly integrated into #765. Because currently they appear redundant and somewhat complex; merging both would confuse readers.

3q,I will do it soon!!!!!!

@shsaihdsaiudh

Copy link
Copy Markdown
Author

Closing this PR as superseded by #765, following the maintainer review.

The unique PreToolUse hook capability has been selectively integrated into examples/claude-code-integration/hooks/ in commit 13674c9, while #765's existing MCP server, standalone executor, requirements, and documentation are reused instead of duplicated. The consolidated suite reports 110 passed, and the GitHub review check is green.

The branch is intentionally retained for history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants