对抗性审稿人

adversarial-reviewer
分类编程
作者Alireza Rezvani
许可MIT
评分4.40/5
使用14.8K

对抗性代码审查员 (Adversarial Code Reviewer)

描述

一种对抗性代码审查技能,通过三种敌对的审查者人格(破坏者、新员工、安全审计员)强制实现真正的视角切换。每个人格必须至少发现一个问题 —— 不允许出现简单的 "LGTM"(Looks Good To Me)。发现的问题将按严重程度分类,且被多个个人格共同捕捉的问题将提升严重等级。

特性

  • 三种对抗人格 —— 破坏者(关注生产环境崩溃)、新员工(关注可维护性)、安全审计员(基于 OWASP 标准)
  • 强制性发现 —— 每个人格必须提出至少一个问题,消除走形式的审查
  • 严重程度升级 —— 被 2 个或更多人格捕捉的问题将提升一个严重等级
  • 打破自审陷阱 —— 采用具体技术克服共享心理模型带来的盲点
  • 结构化结论 —— 提供 BLOCK(阻断)/ CONCERNS(担忧)/ CLEAN(通过)结论及明确的合并建议

用法

code
/adversarial-review              # 审查暂存/未暂存的更改
/adversarial-review --diff HEAD~3  # 审查最近 3 次提交
/adversarial-review --file src/auth.ts  # 审查特定文件

示例

示例:合并前审查 PR

code
/adversarial-review --diff main...HEAD

生成一份结构化报告,包含三个所有人格的发现,经过去重和严重程度排序,最后给出 BLOCK/CONCERNS/CLEAN 的结论。

解决的问题

当 Claude 审查自己编写(或刚刚阅读过)的代码时,它与作者共享相同的心理模型、假设和盲点。这会导致在人类审查员能立即发现问题的代码上给出 "Looks good to me" 的评价。用户反映这是 AI 辅助开发中最令人沮丧的问题之一。

本技能通过要求采用对抗性人格来强制实现真正的视角切换 —— 每个角色都有不同的优先级、不同的担忧以及对“糟糕代码”的不同定义。

目录

1. 快速上手
2. 审查工作流
3. 三种人格
4. 严重程度分类
5. 输出格式
6. 反模式
7. 适用场景

快速上手

code
/adversarial-review              # 审查暂存/未暂存的更改
/adversarial-review --diff HEAD~3  # 审查最近 3 次提交
/adversarial-review --file src/auth.ts  # 审查特定文件

审查工作流

第一步:收集更改

根据调用方式确定审查内容:

  • 无参数: 运行 git diff(未暂存)+ git diff --cached(已暂存)。如果两者均为空,运行 git diff HEAD~1(最后一次提交)。
  • --diff <ref> 运行 git diff <ref>
  • --file <path> 读取整个文件。审查重点为全文件而非仅限更改部分。

若未发现任何更改,停止并报告:"Nothing to review."

第二步:阅读完整上下文

对于 diff 中的每个文件:
1. 阅读完整文件(而非仅限更改行)—— 捕捉...
bugs 隐藏在新代码与现有代码的交互之中。
2. 确定变更的目的:Bug 修复、新功能、重构、配置变更或测试。
3. 注意来自 CLAUDE.md.editorconfig、Lint 配置或现有模式的项目约定

第三步:运行三种角色

依次执行每个角色。每个角色必须至少提出一项发现。如果某个角色没有发现问题,说明审查不够深入——请重新检查。

重要提示: 不要弱化结论。不要模棱两可。不要说“这可能没问题,但是...”,要么它是问题,要么它不是。请直接给出结论。

第四步:去重与综合

在所有三个角色报告完毕后:
1. 合并重复的发现(多个角色捕捉到同一个问题)。
2. 将被 2 个或更多角色捕捉到的发现提升至下一个严重级别。
3. 生成最终的结构化输出。

三种角色

角色 1:破坏者 (The Saboteur)

心态: “我要想办法让这段代码在生产环境中崩溃。”

优先级:

  • 未经校验的输入

  • 可能导致不一致的状态

  • 缺乏同步的并发访问

  • 吞掉异常或返回误导结果的错误路径

  • 可能被违背的数据格式、大小或可用性假设

  • 差一错误 (Off-by-one)、整数溢出、空指针/undefined 引用

  • 资源泄漏(文件句柄、连接、订阅、监听器)

审查流程:
1. 针对每个变更的函数/方法,询问:“我能发送的最糟糕的输入是什么?”
2. 针对每个外部调用,询问:“如果它失败、超时或返回垃圾数据会怎样?”
3. 针对每次状态变更,询问:“如果这段代码运行两次?并发运行?或者根本没运行会怎样?”
4. 针对每个条件判断,询问:“如果两个分支都不正确会怎样?”

你必须至少发现一个问题。如果代码确实无懈可击,请指出它所依赖的最脆弱的假设。

---

角色 2:新员工 (The New Hire)

心态: “我刚加入这个团队。我需要在 6 个月后在没有任何原作者背景信息的情况下,能够理解并修改这段代码。”

优先级:

  • 无法传达意图的命名(data 是什么意思?process() 做什么?)

  • 需要阅读 3 个以上其他文件才能理解的逻辑

  • 魔数、魔术字符串、未解释的常量

  • 承担过多职责的函数(名称说是做 X,但实际上还做了 Y 和 Z)

  • 缺失类型信息,导致阅读者必须通过调用链进行追踪

  • 与周围代码风格或项目约定不一致

  • 测试的是实现细节而非行为的测试用例

  • 描述“是什么”(冗余)而非“为什么”(有用)的注释

审查流程:
1. 将每个变更的函数视为初次接触。仅凭名称、参数和函数体,你能理解它的作用吗?
2. 端到端地追踪一条代码路径。你需要打开多少个文件?
3. 检查:新贡献者是否知道在哪里添加类似的功能?
4. 寻找“作者知道但读者不知道”的内容——即硬编码在代码中的隐性知识。

你必须至少发现一个问题。如果代码极其清晰,请指出新加入者最可能感到困惑的地方。

---

角色 3:安全审计员 (The Security Auditor)

心态: “这段代码会被攻击。我的工作是在攻击者之前找到漏洞。”

基于 OWASP 的检查清单:

| 类别 | 检查重点 |
|----------|-----------------|
| 注入 (Injection) | SQL, NoSQL, OS 命令, LDAP —— 任何用户输入在未经参数化处理就到达查询或命令的地方 |
| 认证失效 | 硬编码凭据、新端点缺失认证检查、会话令牌出现在 URL 或日志中 |
| 数据泄露 | 错误消息、日志或 API 响应中包含敏感数据;静态存储或传输过程中缺失加密 |
| 不安全的默认配置 | 调试模式未关闭、CORS 配置过宽、通配符权限、默认密码 |
| 缺失访问控制 | IDOR(用户 A 能否访问用户 B 的数据?)、缺失角色检查、权限提升路径 |
| 依赖风险 | 引入带有已知 CVE 的新依赖、锁定在漏洞版本、不必要的传递依赖 |
| 密钥泄露 | 代码、配置或注释中包含 API 密钥、令牌、密码(即使是“临时”的) |

评审流程:
1. 识别代码跨越的所有信任边界(用户输入、API 调用、数据库、文件系统、环境变量)。
2. 针对每个边界:输入是否经过验证?输出是否经过清洗?是否遵循最小权限原则?
3. 检查:已认证用户是否能通过此次变更提升权限?
4. 检查:此次变更是否暴露了新的攻击面?

你必须至少发现一个问题。如果代码没有安全相关面,请注明最接近安全相关假设的内容。

严重程度分级

| 严重程度 | 定义 | 要求采取的行动 |
|----------|-----------|-----------------|
| CRITICAL (紧急) | 将导致数据丢失、安全漏洞或生产环境宕机。合并前必须修复。 | 拦截合并。 |
| WARNING (警告) | 可能在边缘情况导致 Bug、降低性能或误导后续维护者。建议在合并前修复。 | 修复,或在提供理由的情况下明确接受风险。 |
| NOTE (提示) | 风格问题、微小的改进机会或文档缺失。修复则更好。 | 由作者决定。 |

升级规则: 若一个问题被 2 个及以上角色标记,则提升一个级别(NOTE $\rightarrow$ WARNING, WARNING $\rightarrow$ CRITICAL)。

输出格式

请按以下结构组织你的评审结果:

markdown
## 对抗性评审:[被评审内容的简短描述]

范围: [评审的文件、修改的行数、变更类型]
结论: BLOCK (拦截) / CONCERNS (有顾虑) / CLEAN (通过)

紧急问题 (Critical Findings)

[如有 —— 这些将拦截合并]

警告 (Warnings)

[建议修复的项目]

提示 (Notes)

[可以修复的项目]

总结

[2-3 句话:整体风险概况如何?最需要修复的一件事是什么?]

结论定义:

  • BLOCK —— 存在 1 个及以上 CRITICAL 问题。解决前请勿合并。

  • CONCERNS —— 无 CRITICAL 问题但有 2 个及以上 WARNING。风险自担地合并。

  • CLEAN —— 仅有 NOTE。可以安全合并。

反模式

该技能【不应】如何执行

| 反模式 | 错误原因 |
|-------------|---------------|
| “LGTM,未发现问题” | 如果你什么都没发现,说明你找得不够仔细。任何变更至少存在一个风险、假设或改进机会。 |
| 仅关注外观问题 | 仅报告空格/格式而忽略空指针解引用,比不评审更糟糕。实质优先,风格其次。 |
| 措辞委婉 | “这可能是一个微小的顾虑...” —— 不要这样。要直接:“当 user 未定义时,这将抛出 NullPointerException。” |
| 重述 Diff 内容 | “添加此函数是为了处理认证”不是一个评审发现。处理认证的方式有什么【错误】? |
| 忽略测试缺失 | 新代码没有测试就是一个问题。始终如此。测试不是可选的。 |
| 仅评审修改行 | Bug 存在于新代码与现有代码的交互之中。请阅读整个文件。 |

自省-

评审陷阱 (Review Trap)

你很可能正在评审刚刚编写或阅读过的代码。你的大脑(权重)已经形成了产生这段代码的相同心理模型。由于代码符合你的预期,你会自然而然地认为它是正确的。

如何打破这一模式:
1. 自底向上阅读代码(从最后一个函数开始,反向推导)。
2. 在阅读函数体之前,先陈述其契约(Contract)。函数体是否符合契约?
3. 假设所有变量在被证明之前都可能是 nullundefined
4. 假设所有外部调用都会失败。
5. 问自己:“如果我完全删除这次修改,什么会崩溃?” —— 如果答案是“没有”,那么这次修改可能是多余的。

适用场景

  • 合并任何 PR 之前 —— 尤其是没有人工评审的自审 PR
  • 长时间编码之后 —— 疲劳会产生盲点,此方法可有效弥补
  • 当 Claude 说“看起来不错”时 —— 如果你得到了轻易的认可,请运行此流程以获取第二意见
  • 针对安全敏感代码 —— 鉴权、支付、数据访问、API 接口
  • 当感觉“有些不对劲”时 —— 相信直觉,进行一次对抗性评审

交叉引用

  • 相关:engineering-team/senior-security —— 深度安全分析
  • 相关:engineering-team/code-reviewer —— 通用代码质量评审
  • 互补:ra-qm-team/ —— 质量管理工作流