前言¶
给 PR 做安全审查时,常见做法是打开 diff,扫一遍新增逻辑,再凭印象判断「改动小、影响面可控」。这一步很容易漏掉两件事:被删掉的那几行当初为什么存在;改动的函数在仓库里到底有多少调用方。
前者需要 git blame / git log -S,否则一次「重构删校验」可能把半年前的 CVE 修复一并抹掉。后者需要定量数调用方,否则一个校验函数的签名变化,会沿着调用链扩散到意料之外的模块。Trail of Bits 把这条审计习惯写成了 Agent Skill differential-review:不按 PR 行数决定认真程度,而按风险等级、git 历史和爆炸半径决定分析深度,并强制落一份带证据的 Markdown 报告。
这是什么¶
一句话定位:differential-review 对 PR、commit 和 diff 做安全导向的差异审查——按仓库规模自适应分析深度,用 git 历史补上下文,计算改动的爆炸半径,检查测试覆盖缺口,并生成完整的 Markdown 报告。
它由 Trail of Bits 维护,放在 trailofbits/skills 插件市场的 Code Auditing 分类里。插件作者署名为 Omar Inuwa,仓库中 .claude-plugin/plugin.json 当前版本号为 1.1.1。源码目录:
https://github.com/trailofbits/skills/tree/main/plugins/differential-review/skills/differential-review
官方描述与 GitHub 上的 SKILL.md、插件 README、文档站 Differential Review 一致:针对代码变更做安全审查,检测安全回归,量化爆炸半径,找出被改代码缺测试的地方。
它基于通用 SKILL.md 格式,因此在 Claude Code、Codex CLI,以及能发现 Agent Skill 目录的 Cursor 等工具里都可以加载。作为 Claude Code 插件时,还附带斜杠命令和 adversarial-modeler 子代理;只拷贝 SKILL.md 时,不一定带上这两项,以实际安装方式为准。
工作流按 git diff 走,并不绑定单一语言。配套的 patterns.md 和示例以智能合约(Solidity)居多;methodology.md 里评估仓库规模的命令会统计 .sol / .rs / .go / .ts 文件数。审查 Web 服务、Rust 或 Go 仓库时,风险分类和阶段流程仍然适用,漏洞模式清单则要按语言自行对照,不要把 Solidity 的 onlyOwner 检测原样套到别的栈上。
核心功能与亮点¶
根据 SKILL.md、methodology.md、adversarial.md、reporting.md 和插件 README,能力可以收成下面几条。文档采用渐进披露:入口文件只保留速查表和决策树,按阶段再加载对应长文,避免一次塞进全部方法论。
1. 按风险分流,不按 diff 大小¶
Skill 把「小 PR 可以快速过」列为必须拒绝的合理化借口,理由写在表里:Heartbleed 也只有两行。分类标准是风险,不是行数。
| 风险 | 触发条件 |
|---|---|
| HIGH | 鉴权、密码学、外部调用、价值转移、校验被删除 |
| MEDIUM | 业务逻辑、状态变更、新增公开 API |
| LOW | 注释、测试、UI、日志 |
文档另外写明:重构在被证明为 LOW 之前,按 HIGH 分析。 重构经常破坏不变量。
仓库规模决定分析策略,与风险分类是两套轴:
| 规模 | 策略 | 做法 |
|---|---|---|
| SMALL(少于 20 个文件) | DEEP | 读全部依赖,完整 git blame |
| MEDIUM(20–200) | FOCUSED | 一跳依赖,优先文件 |
| LARGE(200+) | SURGICAL | 只走关键路径 |
小仓库可以深挖;大仓库的鉴权重写则只切关键路径,并建议先跑同市场里的 audit-context-building 建基线。
2. 用 git 历史抓安全回归¶
Phase 1 要求对照基线版本和当前版本读每个改动区,并对被删除的代码做 git blame / git log -S:这段代码何时加入、提交说明写了什么、是不是安全修复。
立即升级的红旗包括:
- 删除来自带
security、CVE、fix的提交 - 访问控制修饰符被拿掉(例如
onlyOwner,或internal改成external) - 校验被删且没有替代
- 新增外部调用但没有检查
- 爆炸半径 50+ 调用方,同时属于 HIGH 风险改动
文档给过一类典型回归:注释写着「Security fix: validate length to prevent overflow (CVE-…)」的长度校验,在「为了性能重构」时被删掉。git blame 能把它连回当初的 CVE 修复提交;只看新代码,往往看不出这是回归。
检测思路来自 patterns.md,例如:
# 曾经因安全原因删掉、现在又出现的模式
git log -S "pattern" --all --grep="security\|fix\|CVE"
# diff 里被删掉的 require / assert / revert
git diff <range> | grep "^-" | grep -E "require|assert|revert"
3. 定量计算爆炸半径¶
Phase 3 要求按被改函数的调用次数量化影响,而不是口头说「影响面不大」:
| 调用次数 | 爆炸半径 |
|---|---|
| 1–5 | LOW |
| 6–20 | MEDIUM |
| 21–50 | HIGH |
| 50+ | CRITICAL |
优先级矩阵把「改动风险 × 爆炸半径」合成 P0/P1/P2。HIGH 风险且 CRITICAL 爆炸半径要深挖并读全依赖;MEDIUM 风险但调用方很多,也要把调用方纳入分析。methodology.md 里的计数示例是用 grep 统计函数名出现次数,智能合约场景按 .sol 过滤。
4. 缺测试会抬高风险评级¶
Phase 2 把测试缺口写成风险规则,而不是「测试是测试同学的事」:
- 新增函数且没有测试:MEDIUM 升为 HIGH
- 改了校验但测试没动:HIGH
- 复杂逻辑(超过 20 行)且没有测试:HIGH
报告里要列出未覆盖的函数,并据此决定是否建议卡住合并。
5. HIGH 风险要做对抗建模,并强制写报告¶
完整流程是 Pre-Analysis + Phase 0 到 Phase 6:
Pre-Analysis → Phase 0: Triage → Phase 1: Code Analysis → Phase 2: Test Coverage
↓ ↓ ↓
Phase 3: Blast Radius → Phase 4: Deep Context → Phase 5: Adversarial → Phase 6: Report
Phase 5 要求给出具体攻击者模型(谁、有什么权限、从哪个接口进来),而不是「可能存在风险」。可利用性按 EASY / MEDIUM / HARD 评级。插件里的 adversarial-modeler 代理专门做这一步,只应在 HIGH 风险改动上启用。
Phase 6 强制生成 Markdown 文件,禁止只在对话里口头说明。报告固定九段:执行摘要(含 APPROVE / REJECT / CONDITIONAL)、变更说明、高危发现、测试覆盖、爆炸半径、历史上下文、建议、方法论与局限、附录。每条高危发现要带文件行号、commit、爆炸半径、测试覆盖、攻击场景和建议修复。
输出文件名格式为 <项目>_DIFFERENTIAL_REVIEW_<日期>.md,文档示例是 VeChain_Stargate_DIFFERENTIAL_REVIEW_2025-12-26.md。写入优先级:当前仓库工作目录 → 用户 Desktop → ~/.claude/skills/differential-review/output/。写文件失败时才退回对话,并提示手工保存。
五条原则写在入口文件里:Risk-First、Evidence-Based、Adaptive、Honest(写明覆盖范围和置信度)、Output-Driven。
安装与启用¶
Claude Code¶
官方 marketplace 安装分两步。先加入 Trail of Bits 插件市场:
/plugin marketplace add trailofbits/skills
再安装本插件:
/plugin install trailofbits/skills/plugins/differential-review
也可以先执行 /plugin menu 浏览后再装。/plugin 是 Claude Code 里的命令,不是系统 shell。文档站提醒:没加 marketplace 之前,单个插件不会出现在菜单里。
Quick Start 里的调用示例是:
/diff-review
插件命令文件 commands/diff-review.md 的 name 为 trailofbits:diff-review,参数约定为:
/trailofbits:diff-review <pr-url|commit-sha|diff-path> [--baseline <ref>]
Target 必填,可以是 PR 地址、commit SHA 或 diff 路径;--baseline 可选,用来指定对比基线。两条写法指向同一条命令,以当前 Claude Code 插件菜单里显示的名称为准。
Codex CLI¶
仓库 README 写明 Codex 可以直接加载 Claude 的 marketplace,不需要额外的 sidecar 元数据:
codex plugin marketplace add trailofbits/skills
codex plugin list
codex plugin add differential-review@trailofbits
最后一条里的插件名与仓库中 plugins/differential-review 目录名一致。
通用 Skill 安装(Cursor 等)¶
officialskills.sh 与 skills.sh 上的命令是:
npx skills add https://github.com/trailofbits/skills --skill differential-review
这条命令按 Agent Skills 的通用目录约定,把 SKILL.md 装进当前工具使用的 skills 路径。装好后直接描述审查任务即可。第三方目录上的安装次数、扫描分数不是官方数据,安装命令以 GitHub README 和上述目录页为准。
Skill 声明的工具权限是 Read、Write、Grep、Glob、Bash,审查过程会跑 git / gh 和搜索命令。需要在有仓库历史的 git 工作副本里使用,并保证助手有执行这些工具的权限。
典型用法示例¶
下面提示词和命令均来自官方 SKILL.md、插件 README、methodology.md 和文档站,可按仓库现状复现。
1. 用自然语言触发,指向一段 diff
插件 README 的示例:
Review the security implications of this PR:
git diff main..feature/auth-changes
中文环境下可以说:
请用 differential-review,对 main..feature/auth-changes 做安全差异审查。
先按文件做风险分级,对 HIGH 风险文件跑完整流程(含 git blame 和爆炸半径),
最后把报告写到 Markdown 文件,不要只在对话里给结论。
2. 摄入阶段:把变更集摸清楚
methodology.md 要求先提取变更,再评估规模、给每个文件打风险分:
# commit 范围
git diff <base>..<head> --stat
git log <base>..<head> --oneline
git diff <base>..<head> --name-only
# PR
gh pr view <number> --json files,additions,deletions
3. 小 PR 快速分流(官方 Quick Triage)
输入:5 个文件的 PR,其中 2 个 HIGH、3 个 LOW。策略只用入口文件的 Quick Reference:
- 按文件分级
- 只深挖 2 个 HIGH 文件
- 对被删代码做 git blame
- 生成精简报告
文档给出的耗时大约是 30 分钟。即使走快速分流,碰到上文红旗仍要做对抗分析,不能因为「文件少」而跳过。
4. 中等仓库的标准审查
输入:约 80 个文件、12 处 HIGH 风险改动。策略为 FOCUSED:
- HIGH 文件走完整流程
- MEDIUM 做表面扫描
- LOW 跳过
- 按九段结构出完整报告
文档给出的耗时大约是 3–4 小时。自然语言可以写成:
Perform security review of PR #123 with full blast radius analysis
5. 大型关键改动:鉴权重写
输入:约 450 个文件、鉴权系统重写。策略为 SURGICAL,并串联 audit-context-building:
- 先在基线 commit 上建上下文(不变量、信任边界、校验模式、调用图)
- 只深挖鉴权相关改动
- 算爆炸半径
- 做对抗建模
- 出完整报告
文档给出的耗时大约是 6–8 小时。基线分析示例:
git checkout <baseline_commit>
# 若已安装 audit-context-building
# Solidity 示例:
# audit-context-building --scope packages/contracts/contracts --focus invariants,trust-boundaries,validation-patterns,call-graphs,state-flows
分析完基线后再切回 head 看 diff。没有 audit-context-building 时,文档要求用 Read / Grep 手工做同样的行级追踪,而不是跳过 Pre-Analysis。
6. 审查结束后转成审计报告
同市场的 issue-writer 可以把差异审查报告转成给非技术干系人看的审计文档:
issue-writer --input DIFFERENTIAL_REVIEW_REPORT.md --format audit-report
文档站还提到可用 fp-check 对审查中的疑似漏洞做误报核对。这些都是独立插件,需要另行安装。
适用场景与注意事项¶
适合
- 合并前对 PR / commit / diff 做安全审查
- 怀疑改动把旧的安全修复又加了回来
- 需要定量评估「这个函数一改,会波及多少调用方」
- 被改代码缺测试,要把缺口写进合并决策
- 鉴权、支付、外部调用、智能合约可见性这类高风险改动,需要对抗场景而不是笼统评论
官方文档举过的触发场景包括:鉴权系统重写合入 main 之前;被广泛调用的校验函数被删除;智能合约访问控制修饰符从 internal 改为 external;对 5 文件 PR 做分流,标出需要深挖的文件;生成绑定到具体行号和 commit 的证据报告。
明确不要用
- 从零开始的绿场代码(没有基线可对比)
- 纯文档改动
- 格式化 / lint 这类外观改动
- 用户只要口头摘要、并接受相应风险
这些情况文档要求改走普通代码审查。
使用上的限制
- 没有 git 历史就发挥不出主场。 浅克隆、squash 后丢失中间提交、或审查对象根本不在 git 里时,blame 和回归检测会明显变弱。Skill 把「git 历史太花时间」列为禁止跳过的借口。
- 诚实写覆盖范围。 原则里要求写明分析了哪些文件、LOW 是否被排除、置信度是 HIGH 还是 MEDIUM。时间不够时不要声称做了全文分析。
- 发现必须可定位。 要有行号、commit、具体攻击步骤;「输入校验可能被绕过」这类句子在质量清单里不算合格发现。
- 报告必须落盘。 只在聊天窗口里给结论,等于没有交付物。
- Solidity 模式不能当通用漏洞百科。
patterns.md覆盖回归、重入、访问控制、溢出、未检查返回值、时间戳依赖等,示例以合约为主。审查其他语言时沿用阶段流程,模式清单要换。 - 子代理和斜杠命令取决于安装路径。 marketplace / 插件安装会带上
commands/diff-review.md和agents/adversarial-modeler.md;只用npx skills add同步 Skill 目录时,通常只有SKILL.md和同目录的 methodology / adversarial / reporting / patterns。提示词里应写明「按 differential-review 的完整阶段出报告文件」。 - 许可是 CC BY-SA 4.0。 仓库根 README 声明整套 skills 按 Creative Commons Attribution-ShareAlike 4.0 授权。
小结¶
differential-review 做的事情很具体:把安全团队在差异审查里反复强调的步骤——风险分级、git blame、爆炸半径、测试缺口、对抗场景、落盘报告——写成 Agent 可执行的流程。它解决的不是「AI 会不会看 diff」,而是默认审查会跳过删除代码的历史、不会定量数调用方、也不留下可归档的证据。
官方地址:
- Skill 目录:https://github.com/trailofbits/skills/tree/main/plugins/differential-review/skills/differential-review
- 插件说明:https://github.com/trailofbits/skills/tree/main/plugins/differential-review
- Trail of Bits 技能页:https://trailofbits.com/skills/differential-review/
- 文档站:https://trailofbits-skills.mintlify.app/plugins/differential-review
- officialskills.sh:https://officialskills.sh/trailofbits/skills/differential-review
- 市场仓库:https://github.com/trailofbits/skills