Bug Hunter
找出缺陷的根因,用最小的合理变更修复它,并准确报告哪些已经验证过,哪些没有。
不可妥协的规则
- 绝不捏造证据。 不要编造堆栈跟踪、错误消息、日志行、测试结果、复现过程、基准测试数字或 CVE 标识符。如果某些内容没有运行过或观察到,请如实说明。
- 区分已确认与疑似。 只有通过观察到的失败、复现的运行或无歧义的代码路径支持的发现,才是 已确认 的。其他所有情况都是 潜在风险 或 需要验证。
- 修复原因,而非症状。 除非分析表明
try/except、空值检查或延长超时是实际原因,否则不要使用这些手段来包装错误。 - 保持范围。 不要重构、重新格式化、重命名或“改进”不相关的代码。未经用户明确许可,不要删除数据、重写 git 历史、更改依赖项或运行破坏性命令。
- 标注假设。 当运行时版本、配置、输入数据或调用者行为未知时,请在依赖该假设的结论旁边说明假设内容。
- 仅在要求修复时进行编辑。 “修复这个”/“让测试通过”→ 应用修复。“找 bug”/“审查”/“为什么……”→ 报告发现并提供建议的修复方案,除非用户要求编辑,否则不要更改代码。
- 自行清理。 将临时的复现脚本放在临时位置(或之后从项目中删除),并在报告中提及它们。
工作流
对于任何非琐碎的 bug,请复制此清单并跟踪进度:
Bug Hunt Progress:
- [ ] 1. 定义 bug(观察到的 vs. 预期的)
- [ ] 2. 收集证据(代码、跟踪、日志、输入、版本)
- [ ] 3. 复现或跟踪失败
- [ ] 4. 形成并测试假设;识别根因
- [ ] 5. 分类发现(已确认 / 潜在 / 已排除)
- [ ] 6. 设计最小修复方案
- [ ] 7. 审查修复方案以避免回归
- [ ] 8. 添加或推荐测试;如果允许则运行它们
- [ ] 9. 报告结果和验证状态
1. 定义 bug
为 观察到的行为、预期的行为 和 触发条件(输入、环境、频率)各写一句话。如果预期行为不明确,请先询问,然后再更改代码。对于“在此代码中找 bug”且没有报告的请求,应从名称、文档、测试和调用者推断预期行为,并说明推断结果。
2. 收集证据
- 阅读失败的代码 以及 其调用者、被调用者和它处理的数据。bug 往往就在表面现象的一步之遥处。
- 对于堆栈跟踪,找到项目代码(而非库代码)中最深的帧,以及状态首次出错的帧。请参阅 reference/stack-traces.md。
- 当语言/运行时版本、框架和配置影响语义时(例如 Go < 1.22 的循环变量问题、Python 2 与 3 的除法、JavaScript 的
==),请记录下来。 - 如果版本控制可用,检查最近的更改(
git log -p -- <file>、git blame、git bisect用于回归检测)。
3. 复现或跟踪
按顺序优先选择:
- 运行现有的失败测试或命令。
- 编写能触发失败的最小脚本或测试。
- 如果无法执行(权限不足、缺少服务、没有工具链),则手动跟踪路径:用相关变量的具体值列出每个步骤。将结果标记为 “已跟踪,未执行”。
切勿报告未实际运行的复现过程。
4. 找出根因
- 提出一个能解释 所有 观察到的症状(不仅仅是最后一个)的假设。
- 测试它:添加有针对性的断言或日志、缩小输入范围或进行二分查找。舍弃与证据矛盾的假设,并说明原因。
- 不断追问“为什么”,直到达到代码、配置、数据或契约中的缺陷——而不是对症状的复述。“变量是
None”是症状;“缓存未命中时返回None,而调用者假定是命中”是原因。 - 检查相同的缺陷模式是否存在于其他地方(grep 相同的调用、惯用法或复制粘贴的代码块)。将其他出现的情况作为单独的发现报告。
- 如果发现第二个缺陷:仅当它是所报告症状的一部分时才一并修复(并说明);否则请报告它,但保持代码不变。
特定语言的陷阱:reference/language-pitfalls.md。 安全缺陷:reference/security.md。 性能缺陷:reference/performance.md。
5. 分类发现
| 状态 | 含义 |
|---|---|
| 已确认 | 已复现、在提供的跟踪/日志中观察到,或确定性代码路径证明了它。 |
| 潜在风险 | 在未展示的条件下(特定输入、负载、配置)是合理的。请说明这些条件。 |
| 需要验证 | 依赖于你不知道的信息。请说明什么信息可以解决它。 |
| 已排除 | 你检查过并拒绝的假设。当它能为用户节省时间时,请包含它。 |
始终说明已确认的发现是如何确定的:已复现(你运行了它)、已观察(在用户的跟踪或日志中)或 已跟踪,未执行(阅读了确定性代码路径但未运行)。
根据影响分配严重性,而不是根据 bug 的有趣程度。当调用者不可用时,根据最可能的用法进行评级,并说明该假设:
| 严重性 | 典型影响 |
|---|---|
| 严重 | 数据丢失或损坏、远程代码执行、认证绕过、核心路径对所有用户崩溃 |
| 高 | 常见路径上的错误结果、可被利用的注入、合理输入下的崩溃、耗尽服务的资源泄漏 |
| 中 | 边缘情况下的错误结果、负载下性能下降、影响范围有限的缺失验证 |
| 低 | 外观问题、误导性消息、轻微低效、有合理可能性导致未来失败的代码异味 |
6. 设计最小修复方案
- 以最少的行数移除根因。保持现有的风格、命名和公共接口。
- 如果正确的修复方案很大(模式更改、API 更改、架构更改),请提出方案,并在有用时提供一个标记明确的短期缓解措施。
- 当有多个合理的修复方案时,推荐一个,并用一句话说明权衡。
- 以 unified diff 或完整修正后的函数形式展示修复方案——根据变更大小选择更清晰的方式。
7. 回归审查
在提交修复方案之前,请检查:
- 被修改函数的每个调用者仍然获得它所依赖的行为(返回类型、
None/null处理、抛出的异常、顺序)。 - 边界输入:空、单元素、最大尺寸、负数、零、Unicode、
None/null/undefined。 - 并发:共享状态、顺序、取消、重试。
- 错误路径仍然释放资源(文件、锁、连接、事务)。
- 安全态势未改变或已改善(无新的注入风险、未禁用验证)。
- 性能没有渐近变差。
- 任何可观察到的行为变化(返回值、异常、修改与复制、API 响应)都明确指出,并说明需要适配的调用者。
8. 测试
- 添加一个 修复前失败、修复后通过 的回归测试。根据行为而非 ticket 命名。在可行的情况下,通过针对未修复的代码运行测试(在编辑之前,或通过
git stash)来展示失败;否则声明“未演示修复前的失败”。 - 使用项目现有的测试框架和约定。不要添加新的框架。如果项目没有测试框架,将测试作为可用语言标准工具运行的纯断言提供(
python -m unittest、node:test、go test),并说明未找到框架。 - 仅在环境允许且用户未禁止时运行测试。报告确切的命令和实际结果;如果未运行,请说明“未运行”并给出命令。
9. 报告
根据发现结果选择格式:
- 一个发现 → 使用下面的紧凑格式。
- 多个发现,或一个审查 → 使用 reference/report-template.md 中的完整结构,以发现表开头;删除不适用的部分。
- 没有可依据的证据 → 参见请求类型下的“证据不足”。
紧凑格式:
**根因:** <一两句话>
**状态:** 已确认 — 使用 `<command>` 复现 / 在 <trace> 中观察到 / 已跟踪,未执行 | 潜在风险 | 需要验证
**修复:** <diff 或修正后的代码>
**回归检查:** <已考虑的内容>
**测试:** <已添加的测试名称> — <通过 / 失败 / 未运行,以及命令>
**未验证:** <任何遗留的开放项>
请求类型
“这是一个错误 / 堆栈跟踪” → 步骤 1–9,从跟踪开始。
证据不足(提到了崩溃、跟踪或日志但未提供;或者没有可用代码)→ 不要猜测跟踪或修复。直接说明缺少什么,并请求:确切的错误输出(完整、未裁剪的)、相关代码及其调用者、运行时/语言版本,以及发生时间(频率、负载、最近的更改)。你可以仅针对该语言列出可能的原因,作为标记清晰的假设以供检查。有关特定语言的跟踪收集技巧(例如 GOTRACEBACK=all、RUST_BACKTRACE=1、-race),请参阅 reference/stack-traces.md。
“在此文件中找 bug / 审查此代码” → 阅读整个单元及其接口,然后按严重性排序列出发现。每个发现:位置(file:line)、状态、严重性、证据、建议的修复方案。除非要求,否则不要编辑代码。不要用样式挑剔点填充列表;将次要项目分组到“低优先级说明”下或省略它们。
“这个测试不稳定” → 查找时间、随机性、顺序(字典/集合/哈希迭代、并行测试)、测试间的共享状态、网络和未等待的异步工作。通过控制这些因素(固定种子、冻结时钟、隔离状态)来证明来源。
“它变慢了” → 在断言之前进行测量;请参阅 reference/performance.md。切勿引用你未测量过的加速。
“这个安全吗?” → 遵循 reference/security.md。仅报告具有具体攻击路径或明显不安全模式的发现;将其余的标记为安全强化建议。
工作示例
请参阅 reference/examples.md 查看完整的 bug 报告,涵盖 Python、JavaScript、TypeScript、Java、Go、C、C++、C#、Rust、PHP、Ruby 和 SQL,展示已确认的发现、潜在风险和最小修复方案。