跳转到内容

沙箱与策略引擎

Gemini CLI — 沙箱与策略引擎

学习目标

  • 理解 Policy Engine 的三层决策架构(规则 → 安全检查器 → 钩子检查器)
  • 掌握 Shell 命令解析和路径提取的实现
  • 分析文件夹信任机制和通配符策略规则

前置知识

本章涉及安全沙箱的通用原理,建议先阅读:

下文假设你已理解多层防御的概念,直接聚焦 Gemini CLI 的具体实现。


项目实践

PolicyDecision 三值

Policy Engine 对每个工具调用返回三种决策之一:

决策含义后续行为
ALLOW允许执行Scheduler 将工具标记为 Scheduled
DENY拒绝执行Scheduler 创建 ErroredToolCall
ASK_USER需要用户确认等待用户同意或拒绝

三层检查器架构

PolicyEngine.check(toolCall):
├── 1. PolicyRule(规则匹配)
│ ├── 工具名称匹配(精确 + 通配符)
│ ├── 审批模式过滤
│ └── Shell 命令解析 + 路径匹配
├── 2. SafetyCheckerRule(安全检查器)
│ ├── 动态安全评估
│ └── 上下文依赖决策
└── 3. HookCheckerRule(钩子检查器)
├── 运行时动态决策
└── 可扩展策略

决策合并:如果多个规则匹配,取最严格的决策(DENY > ASK_USER > ALLOW)。

Shell 命令解析

Shell 工具的策略检查需要解析命令内容:

parseShellCommand(command):
1. shellQuote.parse(command) // 使用 shell-quote 库
2. 提取命令名称(如 cat, grep, rm)
3. 提取文件路径参数
4. 检测重定向(>, >>, |, 2>&1)
5. 剥离外壳包装(如 cd dir && cmd)

Shell 工具分类SHELL_TOOL_NAMES):

  • shell:执行任意 Shell 命令
  • bash:执行 Bash 脚本
  • 重定向工具:write_file 等间接通过 Shell 操作文件的工具

重定向检测

hasRedirection(parsedCommand):
return parsedCommand.some(entry =>
entry.op === 'redirect' || entry.op === 'pipe'
)

重定向命令即使操作的是安全路径,也可能写入其他位置,因此策略更严格。

通配符策略规则

{
"rules": [
{ "name": "read_file", "action": "allow" },
{ "name": "mcp_github_*", "action": "allow" },
{ "name": "shell", "action": "ask_user" },
{ "name": "*", "action": "ask_user" }
]
}

匹配逻辑

模式匹配范围
*所有工具
mcp_*所有 MCP 工具
mcp_{server}_*指定 MCP 服务器的所有工具
精确名称单个工具

文件夹信任机制

checkPathTrust() 检查路径是否可信:

信任来源优先级:
1. 环境变量: GEMINI_CLI_TRUST_WORKSPACE=true → 全部信任
2. IDE 信任: VS Code 等 IDE 标记为信任 → 信任
3. 配置文件: .gemini/trusted.json → 按配置信任
4. 默认: 不信任

TrustLevel 三级

  • TRUST_FOLDER:信任当前文件夹及其子目录
  • TRUST_PARENT:信任父文件夹(允许读取但不允许写入)
  • DO_NOT_TRUST:不信任,所有操作需用户确认

信任配置文件.gemini/trusted.json):

{
"trustedFolders": ["/path/to/project"],
"trustLevel": "TRUST_FOLDER"
}

沙箱管理器

SandboxManager 接口抽象:

interface SandboxManager {
prepareCommand(cmd: { command, args, cwd, env }): Promise<PreparedCommand>;
isSandboxed(): boolean;
}

实现类型

实现平台说明
NoopSandboxManager全平台无沙箱,直接执行
MacSeatbeltmacOS使用 Seatbelt 沙箱策略
Container 沙箱Linux使用容器隔离(如 gVisor)

命令准备

SandboxManager.prepareCommand():
1. 规范化命令和路径
2. 检查路径是否在允许的范围内
3. 注入沙箱环境变量
4. 返回 { finalCommand, finalArgs, finalEnv, cleanupFunc }

陷阱与对策

Shell 命令解析的局限性

问题shell-quote 库可能无法处理所有 Shell 语法(如进程替换 <()、复杂的引号嵌套)。

对策

  • 对无法解析的命令使用保守策略(ASK_USER 或 DENY)
  • parseCommandDetails 提取已知模式,未知模式降级处理
  • 策略规则支持按命令名称精确匹配,不依赖完整解析

路径遍历攻击

问题:工具调用中的路径参数可能使用 ../ 遍历到允许目录外。

对策

  • isSubpath() 验证路径是否在允许的根目录下
  • 路径规范化(resolve + normalize)消除 ../ 后再检查
  • 沙箱管理器在 prepareCommand 中再次验证

策略规则的隐性冲突

问题:多条规则可能匹配同一工具调用,决策可能不一致。

对策

  • 决策合并取最严格的结果(DENY > ASK_USER > ALLOW)
  • 规则按优先级排序,高优先级先评估
  • 调试日志记录所有匹配的规则和最终决策

设计取舍

三层检查器 vs 单一检查器

方案优势代价
三层检查器静态规则快速匹配、动态检查器处理上下文、钩子检查器可扩展实现复杂、需要合并多层决策
单一检查器简单直接难以兼顾性能和灵活性

Gemini CLI 的选择:三层检查器。静态规则处理 80% 的常见情况(快速),安全检查器处理 Shell 命令等动态场景,钩子检查器允许运行时扩展策略。

文件级信任 vs 全局信任

方案优势代价
文件级信任不同项目不同策略、恶意工作区不影响其他项目配置复杂、用户需要管理信任列表
全局信任简单、无需配置一个恶意项目可能影响全局

Gemini CLI 的选择:文件级信任。安全敏感的工具(如 Shell、文件写入)需要在可信环境中执行,文件夹级信任是最小化的信任范围。


参考来源