feat(examples): 添加 sandbox-backend,通过 PreToolUse hook 透明隔离 Bash#783
feat(examples): 添加 sandbox-backend,通过 PreToolUse hook 透明隔离 Bash#783shsaihdsaiudh wants to merge 2 commits into
Conversation
… 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
| EXEC_BIN = "cubesandbox-exec" | ||
|
|
||
|
|
||
| def _has_unquoted_newline(command: str) -> bool: |
There was a problem hiding this comment.
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([ |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
| import traceback | ||
| from dotenv import load_dotenv | ||
|
|
||
| load_dotenv() |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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.
PR #783 - Comprehensive Code ReviewThis PR introduces a well-architected examples/sandbox-backend/ directory implementing transparent Bash sandboxing via Claude Code PreToolUse hook. Below are noteworthy findings. CRITICAL
HIGH
MEDIUM
POSITIVESecurity-conscious rewrite layer. Clean separation. Lazy bootstrapping. File locking. Graceful degradation. No jq dep. SummaryPriority fix: ANSI-C quoting in _has_unquoted_newline() plus read-only host-mount. |
|
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! |
Review fixes and verificationI addressed the security and robustness findings in commit Highlights:
VerificationFull 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. |
fslongjin
left a comment
There was a problem hiding this comment.
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!!!!!! |
|
Closing this PR as superseded by #765, following the maintainer review. The unique PreToolUse hook capability has been selectively integrated into The branch is intentionally retained for history. |

要解决的问题
基于 MCP 的沙箱接入依赖 coding agent 主动选择沙箱工具,普通的 Claude Code
Bash调用仍可能直接在宿主机执行,因此无法提供透明且完整的命令隔离。Refs #644。本 PR 是 PR #765 的补充方向:使用
PreToolUsehook 自动把 Bash 命令转发到 CubeSandbox。解决方案
新增 hook-based backend,把每次 Claude Code Bash 调用改写为当前会话对应的 CubeSandbox 执行命令。hook 保留命令输出和退出码,执行层复用同一个沙箱,并在多次调用之间延续工作目录和环境变量状态。
具体改动
cubesandbox_rewrite.py,校验并改写 Claude CodePreToolUse输入。cubesandbox_exec.py,实现会话级沙箱复用、文件锁、状态持久化、重置和可选的宿主机只读挂载。审阅反馈处理
Commit
e8f9256处理了自动安全与健壮性审阅提出的问题:范围与取舍
验证
review检查通过。Assisted-by: Codex:GPT-5