第 7 章:权限、Workspace 与沙箱
“工具能执行”与“工具被允许执行”是两件事;“策略允许”与“操作系统确实放行”也不是一件事。本章把 Grok Build 的副作用边界拆成四层:工具 gate、权限决策、Workspace 执行、OS 沙箱。
7.1 四层安全模型
每层失败的语义不同:
- gate 失败:当前 agent/mode 不具备能力;
- permission 失败:能力存在,但这次动作不获授权;
- workspace 失败:动作获授权,但执行环境返回错误;
- sandbox 失败:即使上层允许,OS 仍拒绝越界。
7.2 AccessKind:权限判断的统一语言
权限系统把动作归类为 AccessKind,例如:
- Read;
- Grep;
- Edit;
- Bash;
- MCPTool;
- WebFetch;
- WebSearch。
统一分类允许 native tool、MCP、shell 和 managed policy 使用相同决策结构,而不用为每个工具名写特判。
7.3 Decision 不只是 Allow/Deny
权限结果至少包括:
| Decision | 语义 |
|---|---|
Allow | 当前动作可继续 |
Ask | 需要交互确认 |
FollowupMessage | 把额外信息返回到对话 |
Reject | 用户或普通策略拒绝 |
PolicyDeny | 托管/强策略拒绝,优先级更高 |
Cancelled | 请求在决策期间被取消 |
区分 Reject 与 PolicyDeny 很重要:前者可能允许模型换方案或用户改变授权,后者代表更高层边界,不应被普通 session grant 覆盖。
7.4 配置从哪里来
最终策略可能合并:
- 原生
.grok/config.toml; - 用户级配置;
- 企业/managed settings;
- 兼容读取的 Claude settings;
- 当前 session 的临时 grants;
- 启动参数中的 auto/yolo 模式。
解析阶段需要保留来源信息,否则 UI 只能告诉用户“被拒绝”,却无法解释是项目规则、企业策略还是当前模式导致。
7.5 决策优先级
源码中的 permission manager 体现了大致顺序:
- managed/policy deny 优先;
- 某些 shell/保护场景强制 prompt;
- 非强制场景才考虑 yolo 快速放行;
- 已存在的 session grant;
- auto policy、静态规则、分类器;
- 安全 read/grep/web-search 快速路径;
- persisted/static/session scope grants;
- 用户 prompt;
- 成功后按允许范围持久化 grant。
准确分支会随 AccessKind 和策略变化,但不变量是:
高优先级限制先于便利模式;“always approve”不是跳过 managed policy 的超级权限。
7.6 Yolo 与 Auto 的边界
Yolo 和 auto 在配置上互斥。即使启用 yolo,以下边界仍可能要求确认或直接拒绝:
- managed pin/clamp;
- protected edit;
- 不安全环境变量;
- 解析不透明的 shell;
- exec/嵌套 shell 等强制 floor;
- Plan mode gate。
Plan gate 甚至在 yolo 下仍然生效,因为它表达当前 Agent 的能力模式,而非一次普通权限偏好。
7.7 Bash 为什么需要解析而不是字符串匹配
判断 rm -rf、重定向、管道、wrapper、sh -c 等命令,仅用前缀匹配很容易被绕过:
env X=1 sh -c "..."
command sudo ...
foo | dangerous-command
$(constructed command)2
3
4
权限系统使用 tree-sitter 等结构化解析,并执行有限的 wrapper/env/shell -c peeling。遇到含义不明确的 shell 结构会 fail closed 或提升到强制 prompt。
核心原则:
- 解析深度必须有界,防止恶意嵌套耗尽资源;
- 无法可靠理解不应默认为安全;
- 权限展示应基于 canonical command;
- 实际执行的命令必须与被审批的命令一致。
7.8 Hook 与 Permission 的先后
工具 preflight 中,server/client pre-tool Hook 与 permission 都能阻断动作。二者职责不同:
| 机制 | 典型用途 |
|---|---|
| Hook | 项目自动化、审计、约定检查、注入上下文 |
| Permission | 用户/企业授权与安全策略 |
Hook 失败通常按配置与威胁模型 fail-open,显式 deny 才阻断;managed permission policy 则可 fail-closed。不能把 Hook 当成唯一安全边界。
7.9 Workspace 的双模式
xai-grok-workspace 把调用表达为 typed WorkspaceOp,执行有两种实现:
Local
直接绑定本地 session、toolset、文件系统、terminal 和 VCS。
Proxy
xai-grok-workspace-client 使用与 xai-grok-workspace-types 共享的 request/response shape,通过 RPC 执行动作。
这种设计允许主 Agent 在一个进程,实际 workspace 在另一个受控进程或环境中,而上层工具不需要改变语义。
7.10 Workspace 为什么不等于文件系统封装
Workspace 同时负责:
- 文件读取、编辑与搜索;
- terminal/process;
- VCS/git/worktree;
- permission;
- session storage/recovery;
- workspace server/hub;
- tool configuration;
- 文件状态跟踪与 rewind。
它更接近“代码执行环境的领域边界”,而非 std::fs 的抽象。
7.11 FileStateTracker 与 prompt checkpoint
每个 prompt 位置都可以有一个文件检查点。FileStateTracker 记录本轮相关文件:
- 首次操作前的 before snapshot;
- 操作后的 after snapshot;
- 路径、版本/内容等恢复信息。
路径使用可移植的 FlexiblePath:优先存相对路径,同时兼容旧的绝对路径。历史可延迟加载,picker 扫描先读 metadata,避免为列表展示加载大量文件内容。
7.12 Rewind 不是简单 git checkout
Session rewind 支持:
All:对话与文件一起回退;ConversationOnly;FilesOnly。
文件回退过程:
- 找到目标 prompt 之后每个文件最早的 before;
- 比较当前内容与记录的 latest after;
- 没有外部变化的文件可干净恢复;
- 有差异的文件标记 conflict;
- preview 模式不写文件;
- force commit 才真正回退并截断检查点。
prompt 3: a.rs before=A0 after=A1
prompt 4: a.rs before=A1 after=A2
rewind to prompt 2 => restore earliest before A0
current != latest after A2 => conflict, do not silently overwrite2
3
4
这比“恢复最后一个 diff”更能处理同一文件跨多轮修改。
7.13 对话回退与 compaction
若历史从未压缩,内存中的 prompt index 与对话项可直接截断。但只要发生过 compaction,旧的多个 user turn 可能已被一个 summary 替换,简单按 User message 计数就会错位。
因此源码在存在 compaction 时强制通过 persisted updates 重放并重建 conversation。恢复系统必须尊重其事实源,而不能仅凭当前内存形状逆推历史。
7.14 OS 沙箱
xai-grok-sandbox 使用 nono 等平台能力施加进程级限制,并在支持的平台对子进程网络使用 seccomp 等机制。Profile 在进程早期应用,具有不可逆性质。
沙箱的工程边界:
- 支持的平台执行硬限制;
- 不支持的平台应明确告警/降级;
- 子进程继承与网络规则需单独考虑;
- permission 允许不代表 sandbox 必须放行;
- sandbox 无法解释用户意图,只负责硬资源边界。
不要把沙箱文档描述成跨平台完全等价。具体 profile、feature 与平台支持需结合构建配置验证。
7.15 Hub/HITL 与失败策略
权限还可通过 hub 进行 human-in-the-loop。远程授权 transport 出错时应 fail closed,否则“审批服务不可用”会意外变成“全部放行”。
PermissionEvent 记录:
- 规则/来源;
- 决策原因;
- latency;
- 解析/嵌套深度;
- 最终 decision。
这些字段既服务安全审计,也帮助诊断“为什么这个命令每次都弹框”。
7.16 威胁边界表
| 风险 | 首要防线 | 次要防线 |
|---|---|---|
| 模型尝试未提供的能力 | toolset/mode gate | prompt |
| 危险 Bash | parser + permission | sandbox |
| 修改受保护文件 | protected edit policy | file conflict/rewind |
| 项目插件执行任意命令 | folder trust + executable gate | permission/sandbox |
| MCP 调用外部系统 | MCP access kind + approval | server credentials scope |
| 客户端重复审批 | Leader first-answer-wins | permission request id |
| 远程审批服务失联 | fail closed | 本地告警 |
| 已批准动作执行失败 | Workspace typed error | ToolResult/model recovery |
7.17 源码阅读卡
| 目的 | 文件 |
|---|---|
| Workspace 总入口 | workspace/src/lib.rs |
| Local/Proxy operation | workspace_ops.rs |
| 权限实现 | xai-grok-workspace/src/permission/* |
| 文件状态 | xai-grok-workspace/src/session/file_state.rs |
| Session rewind | rewind.rs |
| 沙箱 | xai-grok-sandbox/src/lib.rs |
| 工具 preflight | tool_calls.rs |
7.18 本章结论
安全不是一个布尔开关,而是一组互补边界:
Agent/mode 决定有没有能力,Permission 决定这次是否获授权,Workspace 决定在哪里执行,Sandbox 决定进程硬上限,FileStateTracker 决定事后能否安全回退。
下一章回到模型输入,拆解系统提示、消息、规则、技能和上下文修剪如何共同构成一次请求。