跳转到内容

工具调用安全审查链(Inspector 模式)

工具调用安全审查链(Inspector 模式)

学习目标

读完本章后,你将能够:

  • 理解 Inspector 模式在工具执行前的安全审查架构
  • 设计多层 Inspector 的串行审查流水线
  • 实现只能升级不能降级的权限覆盖逻辑
  • 评估不同 Inspector 的安全/延迟权衡

前置知识


核心概念

1. 为什么需要审查链

Agent 在执行工具(如 Shell 命令、文件读写、网络请求)前,需要回答多个安全问题:

  • 用户是否授权此操作?(权限)
  • 工具参数中是否包含注入攻击?(安全)
  • 此操作是否会向外部发送敏感数据?(出口)
  • 是否是恶意的对抗性请求?(对抗)
  • 是否陷入重复调用循环?(重复)

将这些问题交给单一检查函数会导致:逻辑耦合、难以扩展、审计困难。Inspector 模式将每种检查独立为可插拔的模块。

2. Inspector 架构

3. 核心接口

trait ToolInspector {
fn name(&self) -> &str; // 审查器名称
fn is_enabled(&self) -> bool; // 是否启用
async fn inspect(&self, ctx) -> Vec<InspectionResult>; // 执行审查
}
enum InspectionAction {
Allow, // 允许执行
Deny, // 拒绝
RequireApproval(Option<String>), // 需要用户确认,附带警告信息
}
struct InspectionResult {
tool_request_id: String,
action: InspectionAction,
reason: String,
confidence: f32, // 审查置信度
inspector_name: String,
finding_id: String,
}

4. 五层 Inspector

Inspector检查内容方法默认行为
PermissionGoose 模式 + 用户权限规则(AlwaysAllow/NeverAllow/AskBefore)规则匹配根据模式决定
Security模式匹配的 Prompt 注入检测(如 curl | bash正则/模式匹配检测到 → RequireApproval
AdversaryLLM 驱动的对抗性审查调用 LLM 判断 ALLOW/BLOCK出错时 → Allow(fail-open)
Egress检测 Shell/Web 工具中的出站流量(URL、SSH、SCP、S3 推送等)正则提取仅信息标记,不阻断
Repetition检测同一工具 + 同参数的重复调用计数 + 窗口超过阈值 → 警告/阻断

设计模式详解

串行审查 + 结果合并

Inspector 按注册顺序串行执行,每个 Inspector 独立审查同一批工具调用,返回各自的 Vec<InspectionResult>

合并逻辑(只能升级不能降级)

关键安全原则:任何 Inspector 都可以阻止工具执行(escalate),但没有任何 Inspector 可以解除其他 Inspector 的阻止(de-escalate)。这保证了「最严格的 Inspector 决定上限」。

Permission Inspector:权限基线

Permission Inspector 是第一个执行的 Inspector,建立权限的初始状态:

  • Auto 模式:所有工具默认 approved
  • Approve 模式:所有工具需要用户确认
  • SmartApprove 模式:LLM 缓存辅助的权限判断
  • Chat 模式:跳过所有工具调用

此外还处理用户定义的 AlwaysAllow / NeverAllow / AskBefore 规则,以及对「一等扩展」的特殊权限(不受权限管理器约束)。

Security Inspector:Prompt 注入检测

Security Inspector 使用模式匹配检测常见的 Prompt 注入攻击:

  • curl | bash 类管道执行
  • 隐藏的危险命令
  • 试图绕过安全限制的指令

检测到可疑模式时,返回 RequireApproval 并附带格式化的安全告警消息,展示给用户确认。

Adversary Inspector:LLM 对抗审查

Adversary Inspector 是最高级的审查层,使用 LLM 本身来判断工具调用的安全性:

  1. 读取用户自定义规则文件 ~/.config/goose/adversary.md
  2. 将工具调用描述 + 原始任务 + 近期用户消息发送给 LLM
  3. LLM 返回 ALLOWBLOCK

Fail-Open 策略:当 LLM 调用失败时(网络错误、API 限流),Adversary Inspector 默认返回 Allow,而非阻断所有工具调用。这是可用性优先的设计——安全审查不应成为系统不可用的原因。

Egress Inspector:出口流量分析

Egress Inspector 扫描 Shell 命令和 Web 工具参数中的出站流量特征:

  • URL、HTTP 请求
  • SSH 连接
  • SCP/SFTP 文件传输
  • Docker push
  • npm/cargo/pip publish
  • S3 / GCS 上传

设计定位:Egress Inspector 始终返回 Allow,置信度为 0.0。它不做阻断,只做信息标记——将检测到的出站目标记录在 InspectionResult 中,供审计日志或 UI 展示使用。

用户确认与持久化

对于需要确认的工具:

  1. 发送 Message::assistant().with_action_required(...) 给前端,附带安全告警
  2. 等待 tool_confirmation_router.register() 返回用户决策
  3. 用户选择:
    • AllowOnce:本次允许
    • AlwaysAllow:本次允许 + 持久化为 AlwaysAllow 规则
    • AlwaysDeny:本次拒绝 + 持久化为 NeverAllow 规则

问题与规避

Fail-Open vs Fail-Closed

问题:当某个 Inspector 自身发生故障时(如 LLM 不可用),应该默认允许还是默认拒绝?

对策

  • Security Inspector:Fail-Open —— 注入检测是额外保护,不应阻塞正常使用
  • Adversary Inspector:Fail-Open —— LLM 审查失败时回退到其他 Inspector
  • Permission Inspector:Fail-Closed —— 权限是基线,无法判断时应要求确认
  • Egress Inspector:始终 Allow —— 仅用于审计

设计启示:安全链中的每一层都应该明确自己的故障模式。将「最关键的检查」(权限)放在最前面,「额外增强检查」(注入检测、对抗审查)放在后面且采用 fail-open。

审查延迟累积

问题:每层 Inspector 都增加延迟,特别是 Adversary Inspector 需要额外调用 LLM。

对策

  • Inspector 支持 is_enabled() 开关,可按需禁用
  • 轻量 Inspector(正则匹配)在前,重型 Inspector(LLM 调用)在后
  • 可以配置 Inspector 超时时间

审查规则的可解释性

问题:用户不知道某个工具为什么被阻止。

对策

  • 每个 InspectionResult 包含 reasoninspector_name
  • RequireApproval 附带格式化的告警消息
  • UI 展示是哪个 Inspector 触发的阻止及原因

设计取舍

串行审查 vs 并行审查

维度串行审查并行审查
延迟各层累加取最慢的一层
依赖关系后层可以看到前层结果各层独立
短路优化可以提前终止(如第一个就 Deny)必须等待全部完成
实现复杂度高(需要结果合并)

大多数 Agent 系统选择串行审查,因为:(1)Inspector 数量通常不多(3-5 个);(2)前层审查结果可以为后层提供上下文;(3)可以在遇到 Deny 时短路停止。

模式匹配 vs LLM 审查

维度模式匹配(Security Inspector)LLM 审查(Adversary Inspector)
速度极快(正则)慢(需要 LLM 调用)
覆盖率只能检测已知模式可以理解语义
误报率低(规则精确)较高(LLM 主观判断)
维护成本需要持续更新规则库依赖 LLM 能力

推荐策略:模式匹配作为第一道快速防线,LLM 审查作为深度补充。两者结合可以兼顾速度和覆盖率。

Inspector 模式 vs 单一权限检查

维度单一权限检查Inspector 链
实现复杂度
可扩展性差(改一处影响全局)好(独立插拔)
审计能力强(每层独立日志)
适用场景简单 Agent企业级安全需求

当 Agent 只需要区分「允许/拒绝」时,单一权限检查足够。当需要多维度安全审查(注入检测、对抗分析、出口流量)时,Inspector 链的可维护性和可审计性明显更优。


参考来源