Pi 没有内置沙箱,这意味着什么
把 Project Trust、内置工具权限、extension 执行位置与 Docker、Gondolin、OpenShell 的隔离范围分开,说明 Pi 默认继承用户权限时哪些风险仍由部署者负责。
第 46 章看到 ToolExecutionComponent 收到 start、partial result 和 end。那只是执行过程在屏幕上的投影:真正的 bash 子进程、文件写入和 extension 代码都在 TUI 之外发生。Pi 的固定版本没有内置沙箱,也不通过每次工具调用前弹权限框来收缩能力。默认安全边界很直接:Pi 进程能做什么,它组装出来的本地工具和扩展通常就能做什么。
这不等于“Pi 没有安全设计”。它有 Project Trust、明确的资源加载门、可替换的工具 operations 和多种外部隔离方案。问题在于它们解决的不是同一件事。Project Trust 防止未批准的仓库在启动时自动加载代码和指令;容器、VM 或 policy sandbox 才限制进程最终能访问的文件、网络和凭据。把两层合成一句“已经信任,所以安全”,会掩盖真正的权限边界。
Project Trust 管加载,不管系统调用
Pi 进入一个项目时,会检查项目目录中是否存在需要信任的资源。安全文档列出的范围包括 .pi/settings.json、.pi/extensions/、.pi/skills/、.pi/prompts/ 和 package sources。默认交互模式会询问;拒绝后不加载这些项目级资源。CLI override、extension 的 project_trust 事件、已保存决定和默认策略共同决定最终结果,无 UI 且没有已有决定时返回不信任。
一旦会话开始,Project Trust 不拦截模型对内置 read、write、edit、bash 的调用,也不会给 extension 包一层 capability filter。安全文档对此写得很明确:trust 是 input-loading guard,不是 sandbox;来自文件、注释、文档、context 或 build output 的 prompt injection 仍属于本地 agent 风险。
可以把这一层理解为“这个仓库能否改变 Pi 的组装输入”,而不是“组装后的 Pi 能否改这个仓库”。前者由 trust 决定;后者取决于 OS 权限、目录挂载和实际 tool implementation。
默认 bash 直接继承本地执行环境
BashOperations 把执行后端抽象成 exec(command, cwd, options),所以 extension 可以改成 SSH、VM 或其他远端实现。默认 createLocalBashOperations() 却没有隐含隔离:它解析本地 shell,以调用方 cwd 启动子进程,环境来自传入的 env 或 getShellEnv(),stdout/stderr 直接流回 callback。timeout 与 AbortSignal 会终止 process tree,但“能停止”不等于“权限受限”。
因此,Pi 若从拥有 SSH key、cloud token 和整个 home 目录访问权的开发 shell 启动,工具进程也处在这个权限上下文中。文件工具不经过 bash,却仍由同一 Node.js 进程执行;extension 更直接,它本身就是加载进 Pi 进程的 TypeScript/JavaScript。模型是否“应该”读取某个文件属于提示与产品约束,OS 是否“允许”读取才是安全边界。
flowchart TD
accTitle: Project Trust 与真正隔离边界的位置
accDescr: Project Trust 只决定项目级资源能否进入 Pi 组装;组装后的本地工具和 extension 默认继承 Pi 进程权限。只有容器、虚拟机或策略沙箱在操作系统层收缩文件、进程、网络与凭据能力。
PROJECT["project resources"] --> TRUST{"Project Trust"}
TRUST -->|approved| LOAD["settings / prompts / skills / extensions"]
TRUST -->|rejected| SKIP["skip project resources"]
LOAD --> PI["Pi process"]
SKIP --> PI
PI --> LOCAL["built-in tools + host extensions"]
LOCAL --> OS["current user permissions"]
SANDBOX["container / VM / policy sandbox"] -. "can enclose process or delegated tools" .-> OS
三种隔离方案,隔离对象不同
源码文档列出的方案可以按“Pi 进程在哪里、哪些工具在哪里、文件如何进入”比较:
| 方案 | Pi 与凭据 | 工具执行位置 | 仍需注意 |
|---|---|---|---|
| Gondolin extension | Pi 和模型凭据留在 host | 内置工具与用户 ! 命令进入 micro-VM | 其他 custom extension tool 默认仍在 host;/workspace 写回宿主 |
| Plain Docker | 整个 Pi 与传入凭据都在 container | 内置工具、! 与 extensions 都在 container | -v "$PWD:/workspace" 仍允许修改宿主项目;不要随意挂载 host agent home |
| OpenShell | 整个 Pi 在 policy-controlled sandbox | 工具与 extensions 都在 sandbox | 依赖 gateway 与 policy;本地 bind mount 和远端 upload/download 的数据路径不同 |
Gondolin 方案适合把 auth 和交互界面留在 macOS,同时将七个内置工具与 ! 命令换成 VM operations。示例把 host cwd 挂到 VM 的 /workspace,所以 VM 内的写入会反映到宿主项目。这提供进程、系统目录和未挂载资源的边界,却不是“项目只读”。如果一个自定义 extension 注册了第八个工具而没有主动委托,它仍随 host Pi 执行。
Plain Docker 把整个进程边界搬进去,extension 也随 Pi 进入 container。文档示例通过环境变量传 provider key,并把当前目录读写挂载到 /workspace。这样能隔离 host 的其他目录和进程,但 agent 对项目目录的写能力仍然完整;若再挂载 ~/.pi/agent,host session、settings 和 auth 也会暴露。更严格的场景应使用 read-only mount,或复制输入、审查 diff 后再把输出带回。
OpenShell 再增加 filesystem、process、network、credential 与 inference policy。远端 gateway 场景不把 host project 直接 bind mount,需 upload/download;credential routing 还可以让 sandbox 只看到内部 inference endpoint。它的强度来自外部 policy 和部署配置,不是 Pi 仓库内新增了一套 permission prompt。
用能力清单选择边界
“要不要上容器”不能只看仓库是否公开。至少应逐项决定:项目目录要读还是要写、home 是否可见、能否启动任意进程、是否需要外网、哪些 provider 凭据必须进入执行环境、是否允许无人值守。对可信代码的短时结对开发,本地权限可能是有意的产品选择;对未知仓库、自动修复任务或长时间 unattended run,则应把最小文件集和短期凭据放进外部边界。
下面的实验只读源码,不启动 Pi,也不执行未知项目命令。它能验证 trust gate 和默认 bash 后端解决的是两个不同问题:
repo="${PI_RELEASE_SOURCE_DIR:-/path/to/pi-0.83.0}"
cd "$repo"
sed -n '46,96p' packages/coding-agent/src/core/project-trust.ts
sed -n '82,148p' packages/coding-agent/src/core/tools/bash.ts
sed -n '1,53p' packages/coding-agent/docs/security.md
第一段只返回是否加载 project resources;第二段直接 spawn() 本地 shell;第三段用维护者自己的文字给出两者边界。三份证据合起来,比看到一次 trust prompt 后猜测权限模型可靠得多。
还有一个容易忽视的责任:外部隔离只能约束放进去的东西。host Pi + Gondolin 若仍加载第三方 extension,需检查 extension 是否只调用被替换的 built-ins,还是自己访问网络、文件或 child_process。Docker 若把整个 home 和长期 key 都挂进去,容器名称再像 sandbox 也没有形成最小权限。边界由实际数据流和执行位置决定,不由方案名称决定。
第 48 章把同样的克制带到全书验证:固定 tag 的普通 git checkout 甚至不含完整离线 model data,必须区分源码 commit、发布 source archive 和 main 分支设计文档。只有把版本、归档哈希、构建与测试结果都锁住,才能知道我们验证的究竟是哪一个 Pi。