目录
【Claude】Claude Code 沙箱代码全链路追踪
/    

【Claude】Claude Code 沙箱代码全链路追踪

GeminiGeneratedImagess5xzlss5xzlss5x.png

Claude Code 沙箱代码全链路追踪

cli.tsx 入口到 Shell.ts 执行的完整沙箱调用链


1. 入口概览:沙箱在启动流程中的位置

flowchart TD
    A["cli.tsx:310<br>await cliMain()"] --> B["main.tsx:585<br>main()"]
    B --> C["main.tsx:201<br>import SandboxManager"]
    B --> D["main.tsx:307-316<br>logStartupTelemetry()<br>记录沙箱状态"]
    B --> E["REPL.tsx 渲染"]
    E --> F["REPL.tsx:2337-2343<br>SandboxManager.initialize()"]
    E --> G["REPL.tsx:2312-2336<br>沙箱不可用检测"]
    F --> H["命令执行流程"]
    H --> I["BashTool.tsx:896<br>shouldUseSandbox(input)"]
    I --> J["Shell.ts:259-265<br>SandboxManager.wrapWithSandbox()"]
    J --> K["Shell.ts:316<br>spawn(包装后命令)"]
    K --> L["Shell.ts:391-393<br>SandboxManager.cleanupAfterCommand()"]

1.1 入口:cli.tsxmain.tsx

📝 cli.tsx:308-310

const { main: cliMain } = await import('../main.js');
await cliMain();

入口文件不直接涉及沙箱。所有沙箱逻辑从 main.tsx 开始。

1.2 main.tsx — 导入和遥测

📝 main.tsx:201 — SandboxManager 导入

import { SandboxManager } from './utils/sandbox/sandbox-adapter.js';

📝 main.tsx:314-316 — 启动遥测中记录沙箱状态

sandbox_enabled: SandboxManager.isSandboxingEnabled(),
are_unsandboxed_commands_allowed: SandboxManager.areUnsandboxedCommandsAllowed(),
is_auto_bash_allowed_if_sandbox_enabled: SandboxManager.isAutoAllowBashIfSandboxedEnabled(),

📝 main.tsx:3012-3017 — AppState 中的沙箱状态

workerSandboxPermissions: { ... },
pendingSandboxRequest: null,

2. REPL 初始化:沙箱的核心生命周期

📝 文件: REPL.tsx

2.1 沙箱不可用检测(#34044 修复)

📝 REPL.tsx:2312-2336

useEffect(() => {
  const reason = SandboxManager.getSandboxUnavailableReason();
  if (!reason) return;
  
  // 如果配置要求沙箱但不可用 → 强制退出
  if (SandboxManager.isSandboxRequired()) {
    process.stderr.write(`\nError: sandbox required but unavailable: ${reason}\n`);
    gracefulShutdownSync(1, 'other');
    return;
  }
  
  // 否则:记录日志 + 显示通知条
  logForDebugging(`sandbox disabled: ${reason}`, { level: 'warn' });
  addNotification({
    key: 'sandbox-unavailable',
    jsx: <><Text color="warning">sandbox disabled</Text><Text dimColor> · /sandbox</Text></>
  });
}, [addNotification]);

2.2 沙箱初始化

📝 REPL.tsx:2337-2343

if (SandboxManager.isSandboxingEnabled()) {
  SandboxManager.initialize(sandboxAskCallback).catch(err => {
    process.stderr.write(`\n❌ Sandbox Error: ${errorMessage(err)}\n`);
    gracefulShutdownSync(1, 'other');
  });
}

2.3 网络权限回调(sandboxAskCallback)

📝 REPL.tsx:2216-2310

当沙箱拦截未授权网络请求时的处理流程:

flowchart TD
    A["沙箱拦截网络请求"] --> B{"是 Swarm Worker?"}
    B -- Yes --> C["发送到 Leader 的 mailbox"]
    C --> D["注册回调等待 Leader 回复"]
    B -- No --> E["队列化本地权限弹窗"]
    E --> F{"连接了 Bridge?"}
    F -- Yes --> G["同时转发到远程 Bridge<br>race 模式:哪边先回复算哪边"]
    F -- No --> H["仅显示本地 TUI 弹窗"]

2.4 对话框优先级

📝 REPL.tsx:2017-2064

沙箱权限弹窗在所有对话框中的优先级:

优先级对话框类型
1message-selector
2sandbox-permission ← 沙箱请求(高于工具权限!)
3tool-permission
4prompt
5worker-sandbox-permission
6elicitation
...其他(cost, idle-return 等)

[!IMPORTANT]
沙箱权限弹窗优先级高于普通工具权限弹窗,即使用户正在输入也会立即显示。


3. BashTool 集成层

📝 文件: BashTool.tsx

3.1 Input Schema — dangerouslyDisableSandbox 参数

📝 BashTool.tsx:242

dangerouslyDisableSandbox: semanticBoolean(z.boolean().optional())
  .describe('Set this to true to dangerously override sandbox mode...')

[!WARNING]
BashTool.tsx:249-252_simulatedSedEdit 被故意从 schema 中 omit,因为暴露它会让模型绕过权限检查和沙箱。

3.2 UI 标识 — userFacingName

📝 BashTool.tsx:498-502

// 环境变量控制是否在 UI 上显示 "SandboxedBash" 标签
return isEnvTruthy(process.env.CLAUDE_CODE_BASH_SANDBOX_SHOW_INDICATOR)
  && shouldUseSandbox(input) ? 'SandboxedBash' : 'Bash';

3.3 命令执行 — shouldUseSandbox 传递

📝 BashTool.tsx:881-898

const shellCommand = await exec(command, abortController.signal, 'bash', {
  // ...
  shouldUseSandbox: shouldUseSandbox(input),  // ← 关键决策点
  shouldAutoBackground
});

3.4 执行后 — 违规标注

📝 BashTool.tsx:709-710

// 在输出中注入沙箱违规信息(model 可以看到,用于解释错误)
const outputWithSbFailures = SandboxManager.annotateStderrWithSandboxFailures(
  input.command, result.stdout || ''
);

4. 沙箱决策引擎 — shouldUseSandbox.ts

📝 文件: shouldUseSandbox.ts

flowchart TD
    A["shouldUseSandbox(input)"] --> B{"SandboxManager<br>.isSandboxingEnabled()?"}
    B -- No --> Z["return false"]
    B -- Yes --> C{"input.dangerouslyDisableSandbox<br>&& areUnsandboxedCommandsAllowed()?"}
    C -- Yes --> Z
    C -- No --> D{"command 为空?"}
    D -- Yes --> Z
    D -- No --> E{"containsExcludedCommand()?"}
    E -- Yes --> Z
    E -- No --> Y["return true ✅ 使用沙箱"]

containsExcludedCommand() 的处理步骤:

  1. 复合命令拆分(&&, ||, ;
  2. 环境变量剥离(FOO=bar cmdcmd
  3. 包装器剥离(timeout 30 cmdcmd
  4. 迭代不动点(交替剥离直到稳定)
  5. 多模式匹配(前缀、精确、通配符)

5. Shell 执行层 — 命令包装

📝 文件: Shell.ts

5.1 exec() 函数接收 sandbox 标志

📝 Shell.ts:171, 191

export type ExecOptions = {
  shouldUseSandbox?: boolean   // ← 从 BashTool 传入
  // ...
}

5.2 构建命令时传递沙箱信息

📝 Shell.ts:209-214

const { commandString, cwdFilePath } = await provider.buildExecCommand(command, {
  id,
  sandboxTmpDir: shouldUseSandbox ? sandboxTmpDir : undefined,
  useSandbox: shouldUseSandbox ?? false,
});

5.3 核心:wrapWithSandbox() 包装命令

📝 Shell.ts:259-273

if (shouldUseSandbox) {
  commandString = await SandboxManager.wrapWithSandbox(
    commandString,
    sandboxBinShell,  // macOS: "bash/zsh", PowerShell: "/bin/sh"
    undefined,
    abortSignal,
  );
  // 创建沙箱的临时目录(安全权限 0o700)
  try {
    await fs.mkdir(sandboxTmpDir, { mode: 0o700 });
  } catch (error) { /* ... */ }
}

5.4 PowerShell 特殊处理

📝 Shell.ts:247-257

// 沙箱化的 PowerShell:先由 powershellProvider.buildExecCommand 把 pwsh 命令
// 封装为 base64 编码,再用 /bin/sh 作为沙箱内 shell
const isSandboxedPowerShell = shouldUseSandbox && shellType === 'powershell'
const sandboxBinShell = isSandboxedPowerShell ? '/bin/sh' : binShell

5.5 命令后清理

📝 Shell.ts:385-393

void shellCommand.result.then(async result => {
  // Linux bwrap 在宿主上创建 0-byte 占位文件,命令结束后同步清理
  if (shouldUseSandbox) {
    SandboxManager.cleanupAfterCommand()
  }
  // ...更新 CWD 等
});

6. 核心适配器 — sandbox-adapter.ts

📝 文件: sandbox-adapter.ts (~986 行)

6.1 SandboxManager 导出接口总览

方法来源作用
initialize(askCallback)自定义检测 git worktree → 转换 config → 初始化运行时 → 订阅设置变更
isSandboxingEnabled()自定义综合判断:平台 + 依赖 + 设置
getSandboxUnavailableReason()自定义返回沙箱不可用原因(#34044修复)
wrapWithSandbox(cmd, shell)转发调用底层运行时包装命令
cleanupAfterCommand()增强底层清理 + scrubBareGitRepoFiles()
setSandboxSettings(settings)自定义写入 localSettings
refreshConfig()自定义立即刷新沙箱配置
annotateStdErr...转发在 stderr 中注入违规标签
getSandboxViolationStore()转发获取违规事件存储
getFsReadConfig()转发获取文件系统读限制
getFsWriteConfig()转发获取文件系统写限制
getNetworkRestrictionConfig()转发获取网络限制配置

6.2 convertToSandboxRuntimeConfig() — 配置转换核心

将 Claude Code 的多层配置合并为底层运行时理解的 SandboxRuntimeConfig

permissions.allow[WebFetch(domain:xxx)]  ──┐
permissions.deny[WebFetch(domain:xxx)]   ──┤
sandbox.network.allowedDomains          ──┤──→ SandboxRuntimeConfig.network
sandbox.network.allowManagedDomainsOnly ──┘

permissions.allow[Edit(path)]           ──┐
sandbox.filesystem.allowWrite           ──┤──→ SandboxRuntimeConfig.filesystem
sandbox.filesystem.denyWrite            ──┤
settings.json 路径 (硬编码 deny)        ──┤
.claude/skills 路径 (硬编码 deny)       ──┘

6.3 安全硬编码项

始终 deny-write(无法覆盖):

  • settings.json / settings.local.json
  • managed settings 文件路径
  • .claude/skills 目录

7. 底层 Stub — anthropic-sandbox-runtime.ts

📝 文件: anthropic-sandbox-runtime.ts

export class SandboxManager {
  static isSupportedPlatform(): boolean { return false }
  static async wrapWithSandbox(command: string): Promise<string> {
    return command  // No-op: 原样返回,不做任何包装
  }
  // ... 全部空实现
}

[!CAUTION]
源码还原版中沙箱实际完全禁用。所有命令 bypass 直接执行。


8. UI 组件体系

8.1 /sandbox 命令入口

📝 sandbox-toggle.tsx

flowchart TD
    A["/sandbox"] --> B{"参数?"}
    B -- 无 --> C["SandboxSettings UI<br>交互式菜单"]
    B -- "exclude pattern" --> D["addToExcludedCommands()"]
    B -- 其他 --> E["Unknown subcommand 错误"]
    
    C --> F["4 个 Tab 页"]
    F --> G["Mode: auto-allow / regular / disabled"]
    F --> H["Dependencies: 依赖检查"]
    F --> I["Overrides: 排除命令管理"]
    F --> J["Config: 当前生效配置"]

8.2 设置面板 — SandboxSettings.tsx

📝 SandboxSettings.tsx

三种沙箱模式:

模式描述对应设置
Auto-allow沙箱内命令自动批准enabled: true, autoAllowBashIfSandboxed: true
Regular沙箱内仍需权限确认enabled: true, autoAllowBashIfSandboxed: false
Disabled关闭沙箱enabled: false

8.3 配置查看 — SandboxConfigTab.tsx

📝 SandboxConfigTab.tsx

展示当前生效的沙箱配置:

  • Excluded Commands: 排除的命令列表
  • Filesystem Read/Write Restrictions: 允许/拒绝的路径
  • Network Restrictions: 域名白名单/黑名单 + 是否是 Managed 模式
  • Unix Sockets: 允许的 Unix socket 列表
  • Linux Glob Pattern Warnings: Linux 上不完全支持的 glob 模式

8.4 覆盖规则 — SandboxOverridesTab.tsx

📝 SandboxOverridesTab.tsx

两种覆盖模式:

  • Allow unsandboxed fallback: 命令失败时可用 dangerouslyDisableSandbox 重试
  • Strict sandbox mode: 所有命令必须在沙箱内,除非在 excludedCommands

8.5 依赖检查 — SandboxDoctorSection.tsx

📝 SandboxDoctorSection.tsx

/doctor 命令中显示沙箱依赖状态(sandbox-exec / bwrap / seccomp)。

8.6 网络权限弹窗 — SandboxPermissionRequest.tsx

📝 SandboxPermissionRequest.tsx

交互选项:

  • Yes — 允许本次连接
  • Yes, don't ask again — 允许并记住(allowManagedDomainsOnly 时隐藏)
  • No (Esc) — 拒绝并告诉 Claude

8.7 违规展示 — SandboxViolationExpandedView.tsx

📝 SandboxViolationExpandedView.tsx

订阅 SandboxViolationStore 展示被拦截的操作记录。

8.8 即时违规提示 — SandboxPromptFooterHint.tsx

📝 SandboxPromptFooterHint.tsx

在输入框底部实时显示违规通知(5秒后自动消失):

⧈ Sandbox blocked 3 operations · ctrl+o for details · /sandbox to disable

8.9 输出清理 — sandbox-ui-utils.ts & BashToolResultMessage.tsx

📝 sandbox-ui-utils.ts

// 从显示文本中移除 <sandbox_violations>...</sandbox_violations> 标签
export function removeSandboxViolationTags(text: string): string {
  return text.replace(/<sandbox_violations>[\s\S]*?<\/sandbox_violations>/g, '')
}

📝 BashToolResultMessage.tsx:24-38

UI 渲染时清理违规标签(确保 model 能看到违规信息解释错误,但 UI 上不显示原始 XML)。


9. 类型定义 — sandboxTypes.ts

📝 文件: sandboxTypes.ts

三个核心 Zod Schema:

SandboxSettingsSchema
├── enabled: boolean              // 主开关
├── autoAllowBashIfSandboxed      // 沙箱内自动批准
├── failIfUnavailable             // 严格模式
├── allowUnsandboxedCommands      // 允许跳出沙箱
├── enabledPlatforms              // 平台白名单
├── enableWeakerNetworkIsolation  // macOS trustd 放松
├── excludedCommands              // 排除命令列表
├── network: SandboxNetworkConfigSchema
│   ├── allowedDomains
│   ├── allowManagedDomainsOnly
│   ├── allowUnixSockets
│   ├── httpProxyPort
│   └── localBindPorts
└── filesystem: SandboxFilesystemConfigSchema
    ├── allowWrite / denyWrite
    ├── denyRead / allowRead
    └── (allowRead 优先于 denyRead)

10. 完整文件索引

核心逻辑

文件行数沙箱相关内容
sandbox-adapter.ts~986核心适配器:配置转换、安全防护、生命周期管理
shouldUseSandbox.ts~154沙箱决策引擎
sandboxTypes.ts~157配置类型定义 (Zod Schema)
anthropic-sandbox-runtime.ts~107底层运行时 Stub
Shell.ts~475命令执行:wrapWithSandbox + cleanupAfterCommand
sandbox-ui-utils.ts13UI 工具函数

BashTool 集成

文件关键行沙箱相关内容
BashTool.tsxL242, L502, L710, L896Schema定义、UI标签、违规标注、决策传递
BashToolResultMessage.tsxL24-38输出中违规标签清理

UI 组件

文件作用
SandboxSettings.tsx设置面板主框架(4 Tab)
SandboxConfigTab.tsx当前生效配置查看
SandboxOverridesTab.tsx覆盖规则管理
SandboxDependenciesTab.tsx依赖检查页
SandboxDoctorSection.tsx/doctor 沙箱段
SandboxPermissionRequest.tsx网络权限弹窗
SandboxViolationExpandedView.tsx违规事件历史列表
SandboxPromptFooterHint.tsx输入框底部即时违规提示

命令入口

文件作用
sandbox-toggle.tsx/sandbox 命令处理

初始化和状态

文件关键行沙箱相关内容
main.tsxL201, L314-316, L3012导入、遥测、AppState
REPL.tsxL2216-2343, L4432初始化、权限回调、违规UI渲染
setup.tsL401-437--dangerously-skip-permissions 的沙箱检测

11. 数据流总结

用户输入 "npm run build"
       │
       ▼
 ┌─[ BashTool.call(input) ]──────────────────────┐
 │  input.dangerouslyDisableSandbox = false       │
 │  input.command = "npm run build"               │
 └────────┬──────────────────────────────────────-┘
          │
          ▼
 ┌─[ shouldUseSandbox(input) ]───────────────────┐
 │  1. isSandboxingEnabled() → true              │
 │  2. dangerouslyDisableSandbox? → false         │
 │  3. containsExcludedCommand("npm run build")?  │
 │     → false                                    │
 │  return true ✅                                │
 └────────┬──────────────────────────────────────-┘
          │
          ▼
 ┌─[ Shell.exec(command, { shouldUseSandbox: true }) ]─┐
 │  1. provider.buildExecCommand(cmd, {useSandbox})    │
 │  2. SandboxManager.wrapWithSandbox(cmdString,shell) │
 │  3. spawn(wrappedCommand)                           │
 │  4. .result.then → cleanupAfterCommand()            │
 └────────┬────────────────────────────────────────────┘
          │
          ▼
 ┌─[ 命令执行结果 ]──────────────────────────────┐
 │  SandboxManager.annotateStderrWithSandbox...  │
 │  (如果有违规:注入 <sandbox_violations> 标签) │
 │  UI 层清理标签显示 → ViolationStore 记录     │
 └───────────────────────────────────────────────┘

标题:【Claude】Claude Code 沙箱代码全链路追踪
作者:gitsilence
地址:https://blog.lacknb.cn/articles/2026/04/14/1776147394950.html