2026年5月30日 2 分钟阅读

如何发现 AI Agent 中未经检查的危险函数调用?Diplomat 静态扫描实战

tinyash 0 条评论

你交付了一个 TypeScript AI Agent。它能够读写数据库、发送邮件、调用支付接口、删除文件、执行系统命令。每一个能力都是精心设计的功能——但你知道哪些函数调用没有任何安全检查吗?

这并非危言耸听。在传统 Web 应用中,每次危险操作都有人类点击确认。但在 AI Agent 中,LLM 自主决定调用哪些函数、以什么参数调用、调用多少次——它可能循环调用、幻觉出错误参数、甚至被 prompt injection 利用。

Diplomat Agent 是一个开源的静态 AST 扫描器,专门分析你的 TypeScript AI Agent 代码,找出所有未经安全检查的工具调用。它只有两个运行时依赖,在 7,874 个 TypeScript 文件的代码库上只需约 9 秒就能完成全量扫描。

为什么 AI Agent 需要安全检查?

在传统应用中,函数调用有天然的安全缓冲:用户通过 UI 操作触发、有确认弹窗、有速率限制。

但在 AI Agent 架构中:

  • LLM 自主决定调用 — Agent 根据上下文自行选择调用哪些工具函数
  • 无人工确认链 — 没有每次操作的确认对话框
  • 循环风险 — LLM 可能重复调用同一个函数数千次
  • 参数幻觉 — Agent 可能使用错误参数调用危险函数
  • Prompt Injection — 攻击者可能通过注入操控 Agent 调用未授权的函数

Diplomat 的团队扫描了 OpenClaw Agent 代码库(7,874 个 TypeScript 文件),发现了 419 个有真实副作用的工具调用,其中 332 个(79%)没有任何安全检查

快速开始

通过 npm 安装并运行扫描:

npm install -D @diplomat-ai/diplomat-agent-ts
npx diplomat-agent-ts scan .        # 扫描项目根目录
npx diplomat-agent-ts scan ./src    # 扫描指定子目录

输出示例:

Found 419 tool calls with real side effects
  332 (79%) have NO checks
  87 (21%) have partial checks
  ⚠️  chargeCustomer (src/payments.ts:42) — no bounds, no rate limit, no approval
  ⚠️  deleteUserData (src/users.ts:156) — no confirmation, no batch protection
  ✅  sendWelcomeEmail (src/notifications.ts:23) — checked:ok

检测范围

Diplomat 支持 40+ 种模式,覆盖 12 类副作用:

类别示例调用所需守卫
支付(payment)stripe.charges.create()bounds、rate limit、approval
数据库写入(database_write)Prisma .create(), Mongoose .save()输入验证、rate limit
数据库删除(database_delete)Prisma .delete(), raw DELETE批量保护、确认
HTTP 写入(http_write)axios.post(), fetch(POST)rate limit、重试上限
邮件(email)nodemailer.sendMail()rate limit
消息(messaging)twilio.messages.create()rate limit
Agent 调用(agent_invocation)agent.run(), graph.invoke()输入验证、approval
动态代码(dynamic_code)eval(), new Function()确认
文件删除(file_delete)fs.rm(), fs.unlink()确认
破坏性操作(destructive)execSync(), spawnSync()确认

守卫检查项包括:输入验证(Zod、Yup)、速率限制(NestJS @Throttle)、权限检查(NestJS Guards)、确认步骤、幂等性键、重试上限。

CI 集成:阻止未经检查的代码合入

在 GitHub Actions 中配置 Diplomat 扫描,让任何包含未检查危险调用的 PR 无法通过:

- name: Diplomat governance scan
  run: npx -y @diplomat-ai/diplomat-agent-ts scan . --fail-on-unchecked

--fail-on-unchecked 参数使扫描器在发现任何 no_checks 状态的调用时返回 exit code 1,直接阻断 CI 流水线。partial_checks 状态仅作为警告,不会阻断。

也可以在 pre-commit hook 中使用:

repos:
  - repo: local
    hooks:
      - id: diplomat-agent-ts
        name: diplomat governance scan
        entry: npx -y @diplomat-ai/diplomat-agent-ts scan . --fail-on-unchecked
        language: system
        pass_filenames: false

工具调用 SBOM:构建可见性

除了终端输出,Diplomat 可以生成 toolcalls.yaml——一个类似 package-lock.json 的行为清单(SBOM),记录每个工具调用及其安全状态:

npx diplomat-agent-ts scan . --output-registry toolcalls.yaml

生成的文件可 commit 到仓库,在 PR 中 diff——新增危险函数调用时,reviewer 可以直观看到变化:

spec_version: "1.0"
language: typescript
summary:
  total: 12
  no_checks: 8
  partial_checks: 3
  confirmed: 1
tool_calls:
  - function: chargeCustomer
    file: src/payments.ts
    line: 42
    checks: []
    missing:
      - no bounds on amount
      - no rate limit
      - no idempotency key
    owasp: [ASI-01, ASI-02, ASI-03, ASI-06]

标记已确认的安全调用

对于故意不设守卫或通过外部中间件保护的函数,可以用 inline annotation 标记:

// checked:ok — protected by middleware/approval.ts
export async function chargeCustomer(amount: number, customerId: string) {
  return stripe.charges.create({ amount, currency: "usd", customer: customerId });
}

下次扫描时,该调用会被归为 confirmed 状态,不再报警。diplomat:okcanary:ok 也被接受。

实践技巧

  1. 先扫描后开发 — 在新项目中先运行扫描建立基线,后续每次新增工具调用时审查安全状态
  2. CI 强制闸门 — 将 --fail-on-unchecked 加入 CI,防止未经检查的工具调用进入 main 分支
  3. SBOM 版本控制 — 将 toolcalls.yaml 纳入代码审查流程,每次 Agent 能力变更时都有记录
  4. 结合运行时防护 — Diplomat 的静态扫描与 diplomat-gate 运行时强制形成互补(后者可以在 <1ms 内对每次调用做出 CONTINUE / REVIEW / STOP 决策)
  5. 告诉你的 AI Agent — 可以要求 Claude Code、Copilot 或 Cursor 在生成工具调用代码后自动运行扫描:”Run diplomat-agent-ts scan . and fix any unguarded tool calls.”

结语

AI Agent 的信任问题本质上是可见性问题。当你不知道自己的 Agent 能做什么危险操作时,你根本无法信任它。Diplomat Agent 用静态分析的方式,帮你回答一个最基本的问题:「我的 AI Agent 在真实世界中到底能干什么?」——以及更重要的,「哪些操作没有任何防护?」

对于正在构建 TypeScript AI Agent 的团队来说,Diplomat 应该成为开发流程中的最小安全基线。

发表评论

你的邮箱地址不会被公开,带 * 的为必填项。