id: sandbox-effect-cap
module: C
week: 8
title: "硬沙箱:把'写文件'这一 effect 限制在能力边界内"

concept:
  textbook: |
    智能体一旦能写文件、跑命令,就具备了改变环境的副作用(side effect)。仅靠
    prompt 约束("请只在 F 目录内操作")是不可靠的——模型会越界。工程上必须在
    **工具层**做硬性能力限制(capability confinement):无论模型怎么想,越界的写
    操作在执行边界被直接拒绝。

    这对应操作系统里的沙箱/权限模型:能力(capability)显式授予且不可逾越,
    realpath 解析符号链接以防 `..` 或 symlink 逃逸。
  key_point: >
    prompt 约束挡不住模型(实测它把 5 次 WriteFile 全写到 F 之外、还跑偏续跑别人的研究);
    必须工具层硬拦。只拦写不拦读;Bash 靠 CWD + dangerousCommandBlock(shell 无法工具层硬沙箱)。

code:
  - file: lambdagent/src/lambdagent/builtin_tools/_sandbox.py
    symbol: check_write_allowed
    lines: "38-56"
    note: >
      模块级 root + 锁。未设 root → 总允许;设了 → realpath 后必须 == root 或落在
      root/ 之下,否则返回 [SANDBOX_DENIED]。realpath 防 symlink/.. 逃逸。
  - file: lambdagent/src/lambdagent/builtin_tools/file_tools.py
    symbol: _resolve
    lines: "21-35"
    note: >
      相对路径必须按会话 CWD(shell_tools._get_cwd)解析,而非服务进程 CWD。
      配套坑:加任何新文件工具都要走 _resolve,否则读侧目录会和写侧对不上。

formal:
  term: "Guard(WriteFile, check_write_allowed) ; root = realpath(F)"
  effect: "State[fs] ↾ Cap(root)   (effect 被限制到能力 root 内)"
  note: >
    沙箱在形式上是一个 effect 限制算子:把 WriteFile 的 State[fs] effect 投影
    (restrict, ↾)到能力集 Cap(root) 之内。越界写不是"运行期再补救",而是被
    Guard 直接拒绝并把 [SANDBOX_DENIED] 反馈给模型纠正——这正是"形式化提前拒绝"
    在安全维度的兑现,和范式③的能力授权(Cap)是同一套语言。

live_demo:
  run: ""
  highlight: >
    回归测试 test_sandbox_blocks_writes_outside_root 验证越界写被拒。
    教学演示:在工作区模式下令 agent 尝试写 F 之外,观察 trace 里的 [SANDBOX_DENIED]
    与模型随后的自我纠正。

related:
  - tool-claude-code-native
  - tool-native-fc

takeaway: |
  学生应能把三件事连起来:
  1. 副作用(effect)是危险的来源,所以需要边界;
  2. 边界必须在工具层强制(prompt 不够),代码落点是 check_write_allowed + _resolve;
  3. 形式上这是 effect 到能力集的限制(↾ Cap),与范式③的外包能力授权是同一抽象。
  这张卡把"安全"从一句口号变成一个可指认的 effect 限制算子。
