对该代码库执行一次对抗性审计并完成它。先报告和交付;仅在带 --fix 参数或用户选定修复后才修改源代码。
你是对手。每个镜头从不同角度提出一个问题:什么输入、时机、用户或攻击者会导致此代码给出错误答案、丢失数据、泄露数据或使用户受阻?
此文件夹包含审计所需的一切。从该文件所在目录解析以下路径:
lenses/<lens>.md:每个镜头一个文件。其Fires:行说明镜头何时运行(always,或when <condition>)。每个探针要么是无条件的(始终回答),要么标记为(when <condition>)(当条件满足时回答,否则关闭为不适用)。然后是镜头产生的工件,以及它的完成行。scripts/scan.py:对仓库进行指纹识别,路由镜头,并列出项目自身的分析器来运行。scripts/signals.tsv:每个检测和种子模式;scripts/tools.tsv:它知道的每个分析器。项目在docs/agents/audit-signals.tsv中添加自己的行。scripts/verify.py:对照镜头文件检查发现文件。scripts/cochange.sh:一起变更的文件。DELIVER.md:交接说明、审计地图及其问题单。UNTANGLE.md:结构重构,用于--untangle和结构性问题单。
1. 范围
- 参数:路径(默认:仓库根目录);
--lens a,b仅运行这些镜头;--since REF审计自某个引用以来的变更(PR 基础,或上次审计的提交);--fix;--untangle [area|ticket]直接跳转到UNTANGLE.md;--out DIR(默认.audit/)。 - 当无法获得答案时(CI,
claude -p),未回答的问题留在HANDOFF.md中作为开放问题,交付仅进入本地跟踪器,并且除非问题单在approved:行上,否则源代码保持不变。 - 创建输出目录,其中包含
.gitignore和*,因此审计不会触及任何已跟踪的文件。启动新运行时(绝不在恢复时),首先将之前运行的RUN.md、REPORT.md和findings/移动到<out>/history/<其日期>/,以便新运行可以与之比较。 - 恢复:如果
<out>/HANDOFF.md存在,则从其next阶段继续,运行的文件保留在原处。其approved:行上的问题单被清除,无需再次询问即可进行源代码更改;按照DELIVER.md的说法,处理边界上的第一个问题单,每个会话一个问题单。任何其他问题单首先返回给用户。如果在history/中存在更早的运行,则提供--since <其提交>并向前携带refuted.md。
每个阶段结束时按照 DELIVER.md 的描述重写 <out>/HANDOFF.md。
2. 扫描与侦查
运行 python3 scripts/scan.py <path> [--since REF] [--lens a,b] > <out>/SCAN.md,因此 SCAN.md 是脚本的原始输出:技术栈、包、哪些镜头触发及原因、热点、执行计划、需要运行的分析器、每个探针的候选位置(最热的文件优先),以及特殊情况计数。
运行工具下列出的每个可用命令的分析器,将其输出保存到 <out>/tools/<tool>.txt。它们的警告是它们命名的镜头行的线索,与扫描位置相同;缺失或失败的工具在 RECON.md 中记录,绝不安装。
然后编写 <out>/RECON.md,完成脚本无法看到的内容。每个标题都有答案或 none 时才完成:
- 边界:每个外部数据进入的地方(HTTP、webhooks、数据库读取、文件、环境变量、深层链接、推送负载、模型输出、用户输入)。
- 持久化:本地存储、服务器数据库、缓存、队列,以及每个事实归谁所有。
- 关键路径:金钱、认证、数据删除、迁移、任何与安全相关的内容、主要用户循环。
- 命令:构建、测试、lint、运行;测试当前是否通过。
- 内部规则:
CLAUDE.md/AGENTS.md;GLOSSARY.md(或GLOSSARY-MAP.md)中的领域语言,用于发现和修复中的所有名称;ADR,其中已接受的风险在风险未改变时保持已解决;docs/agents/中的问题跟踪器。 - 条件:对于每个触发的镜头,在此处哪些条件性探针成立,并附有证据。
- 镜头覆盖:扫描跳过但侦查显示需要的镜头,或由错误信号触发的一个,并附有理由。
编写 <out>/RUN.md:提交、日期、参数、最终镜头列表。
3. 搜寻
遵循扫描的计划:顺序执行此处每个镜头,一个接一个;并行 为每个镜头分配一个子代理,分片镜头为每个列出的包分配一个子代理。限制并发的主机分批次运行,将镜头与站点最少的配对;没有子代理的主机按顺序运行所有内容。
子代理启动时不携带任何技能和此对话的任何部分,因此每个简报独立。它包含:镜头文件、此文件(用于方法和发现格式)、RECON.md、SCAN.md 中镜头对应的部分、范围以及输出路径 <out>/findings/<lens>[-<package>].md。搜寻者只读:他们阅读代码、运行测试、脚本和应用,仅写入其发现文件和重现步骤,然后返回主上下文,主上下文单独分发。
方法
- 事前分析:发布三个月后,第一条差评是关于这个领域的。写下来,然后找到导致它的代码。
- 逆向思维:你如何使这个功能尽可能严重地失败?检查每种方式的代码块。
- 假设清单:对于关键路径上的每个函数,列出它的假设。哪些假设通过类型或约束得到强化,哪些靠运气?
- 切斯特顿栅栏:在将守卫、特殊情况或奇怪的一行称为错误之前,先找到它存在的原因(
git log -L、blame、PR)。有活生生理由的栅栏不是发现。 - 候选是线索:扫描站点是开始阅读的地方。零站点的探针仍要回答,站点只有在有具体场景时才成为发现。
发现格式
### <LENS>-<n> <60 字符以内的声明>
- where: 路径:行号
- probe: <镜头文件中的探针ID>
- scenario: <具体的输入、状态或时机> → <用户或系统看到的错误结果>
- severity: critical | high | medium | low
- detectability: loud(崩溃、告警、测试失败)| quiet(人们可能注意到的错误答案)| silent(没人会注意到)
- verdict: CONFIRMED(重现它的测试、脚本、查询或截图)| PLAUSIBLE(有推理,未运行)
- fix family: <变更的类型,而非补丁>
严重性:critical 导致数据丢失或损坏、违反安全或隐私、金钱计算错误或危及安全;high 在常见路径上显示错误答案或使用户受阻;medium 在罕见路径上如此或降低质量;low 是卫生。严重性由谁在已交付的配置中今天可触发它决定:critical 或 high 场景命名该人(任何访客、任何登录用户、持有 X 的攻击者);仅操作员或死配置可达的路径降低一级。
编辑:发现、重现、报告和问题单会共享。在任何秘密、令牌或个人数据的位置写入 <REDACTED>,并指向其存储位置而非引用它。
发现文件以镜头的工件开头。每个探针以至少一个发现或一个 clean: 行结束:clean: <probe> — <evidence>,或者对于条件不成立的条件性探针:clean: <probe> — n/a: <why>。当探针的完成行成立且 python3 scripts/verify.py <out> --root <path> --lens <lens> 通过时,搜寻者完成。
4. 验证
- 合并重复:跨镜头,当两个发现共享位置和机制时,保留更严重的那块,添加
- also: <the other ids>,并删除另一块,使每个问题只出现一次。 - 反驳:每个 critical 或 high 发现获得一个新的反驳者;其余按镜头分批。反驳者仅获取发现块,绝不包括搜寻者的推理。对于
PLAUSIBLE发现,它试图驳斥声明:上游守卫、数据库中的约束、不可达路径、有理由的栅栏。对于CONFIRMED发现,它检查重现:它是否因所述原因变红,且通过真实用户或攻击者可达的代码?当主机提供不同的模型时,反驳者使用它,因为共享偏差使同模型一致性成为弱证据。 - 按证据提升:存活并已重现 →
CONFIRMED;存活但仅凭推理 →PLAUSIBLE;被驳斥 →<out>/refuted.md并附理由。 - 覆盖率审查:一个全新的子代理获取
RECON.md的关键路径和每个镜头的clean:行,并命名每个证据薄弱的关键路径(需要跟踪或测试的句子)或没有镜头触及的路径。每个路径在被报告之前,返回到匹配的镜头进行二次查看。
重现是一个紧凑的反馈循环,对此发现变红而不是其他:在最接近的接缝处编写一个失败的测试、脚本或查询。只要成本低于十分钟,就构建一个,放在 <out>/repro/ 中,仅针对本地、分支、沙箱或测试模式的系统。被一个下一季度仍会成立的理由(已接受的风险、有意的权衡)驳斥的发现属于 ADR,在交付时提供,以便后续审计在侦查中读取。
5. 报告
编写 <out>/REPORT.md:
- 摘要:按严重性和镜头统计,以及优先修复的前三个问题及原因。
- 修复:按根本原因聚类发现,每个修复写在其解决的发现之上;寻找一个修复背后的多个发现,这通常看起来与它们完全不同。标记每个为
structural(耦合、重复、缺失类型、浅层模块、放错的接缝)或local(一个地方、一次更改),并用深层模块词汇描述结构性修复:模块、接口、深度、接缝、适配器、杠杆、局部性、删除测试。 - 未聚类的发现,按严重性排序,然后按可检测性(先静默),再按可达性。
- 覆盖率:每个镜头的
clean:行、跳过的镜头及其原因、运行或缺失的分析器、以及覆盖率审查返回的内容。 - 自上次审计以来,当
history/包含更早运行时:已修复、仍开放、新出现、以及回归(之前修复过,现在又回来)。
- 让服务器成为 `scan_count` 的唯一写入者,使用 `where scan_count > 0` 原子递减,这样客户端不能漂移、双重消费或重置它。[structural, critical]
- MONEY-2 `ScanQuota.swift:40` 在上传前本地递减,并在重试时再次递减。
- MONEY-3 重新安装应用将本地存储中的 `scansUsed` 重置为 0。
- STATE-5 每周配额在设备的本地周一重置,因此向西飞行可获额外一周。
运行 python3 scripts/verify.py <out> --root <path>;当它通过时报告完成。当主机渲染结构化发现(ReportFindings 工具)时,也要报告每个存活的发现,最严重的优先。
调优此项目的漏斗:一个种子行其所有站点最终被驳斥或干净,或者一个指向它的发现没有种子,被视为项目 docs/agents/audit-signals.tsv 的建议行。在报告中列出建议;仅在用户接受时才写入。
6. 交付与交接
阅读 DELIVER.md。将报告绘制为一张审计地图,每个修复一个票据,有类型,有阻塞边,放在仓库的问题跟踪器上(未配置时放在本地 .scratch/;在发布到远程跟踪器之前确认)。然后询问要开始哪些修复作为一个烧烤轮:将每个编号为一个问题,并附上你推荐的答案,措辞使得“是”接受它。如果使用 --fix,则采纳所有建议。在 HANDOFF.md 的 approved: 行记录已批准的票据编号。
根据票据的形状路由每个票据。命名的技能是 mattpocock-skills;当可用时使用 Skill 工具调用一个,否则内联执行其步骤:
| 票据形状 | 路由 |
|---|---|
| 需要先决策:接缝的位置、与之矛盾的 ADR、模糊的领域术语、要接受的风险 | grill-with-docs,将答案作为 ADR 和术语记录 |
| 结构性,一个模块,值得在提交前与用户探讨 | improve-codebase-architecture 针对该区域 |
| 结构性,有竞争的接口设计 | codebase-design,其双重设计模式 |
| 结构性,方法明确且适合一个会话 | UNTANGLE.md,附带票据和 REPORT.md 作为其起始列表 |
| 结构性,大于一个会话 | wayfinder,绘制决策票据以到达目的地 |
| 许多修复需要切片以便代理无人值守工作 | to-tickets(曳光弹切片、阻塞边、大型重构作为展开-收缩),然后 implement |
| 本地修复 | tdd:重现是第一个红测试,在与用户商定的接缝处;整个测试套件保持绿色 |
PLAUSIBLE 且 critical,尚无重现 | diagnosing-bugs 阶段 1:先构建紧凑循环 |
当边界为空时,使用 --since <审计提交> 重新审计。审计的目标是通过一个重新审计来验证没有存活的 critical 或 high 发现。
完成条件
- 每个触发的镜头都有一个满足其完成行的发现文件,并且
verify.py对整个输出通过。 - 每个存活的发现都有位置、探针、场景、严重性、可检测性和判定;每个被驳斥的都位于
refuted.md中。 REPORT.md将每个发现置于一个修复下或未聚类列表中,地图每个修复对应一个票据,并且HANDOFF.md指明下一个票据。