name: result-with-file description: 将安全分析/测试结果持久化为按入口点/主题组织的分级 Markdown 报告,内嵌 POC version: "2.0" agent: all when-to-use: 所有分析完成后,需要将结果持久化输出时 context: inline
核心原则:无损超集
最终报告是所有子报告的超集,不是摘要。子报告(各步骤的 result_file)中可机械列举的内容,必须原样进入最终报告对应的详细章节,禁止概括、折叠或抽样。具体包括但不限于:
- 端点 / 接口清单:逐条保留 method + URL + 参数名 + 记录数等
- 测试场景矩阵:每个场景独立成行,保留请求与关键响应字段,禁止合并为单行结论
- 算法 / 关键逻辑源码片段:原样代码块
- 配置项与响应头:逐条保留(如 CORS 各响应头、缺失安全头清单、OPTIONS 预检响应)
- 环境 / WAF / 前置条件等限制说明:逐条并入"评估局限性"
顶层综合层(执行摘要、严重度统计、攻击链串联、统一修复建议)是在明细之上额外添加的,不能取代明细,也不得以"已在汇总表概括"为由删减明细。各子报告文件保留为可追溯的原始证据,最终报告须引用并链接它们。
反折叠机制:机械派生 + 计数闸门(最重要)
历史上"折叠"反复发生的根因不是上游丢数据,而是 findings-index.md(反折叠的"基准")由 LLM 边读 prose 报告边手写概括出来,于是被写成区间行(如 AUTH-001~021、DEEP-001~550、(19处)),随后的逐行对账拿"折叠的索引"核对"折叠的报告"——两边一致、永远通过,对账空转。
修复原则:基准和闸门必须脱离 LLM 自由发挥。 本 skill 用以下确定性流程取代手写:
- 每条发现先抽成规范化 jsonl(一发现一行、字段见所选模板的 schema、绝不写区间),落
shared/coverage-ledger/findings/<src>.jsonl。 findings-index.md与 id 清单index-ids.txt/exclude-ids.txt由模板提供的 jq 派生命令从 jsonl 机械派生(区间行从构造上不可能出现),作为闸门基准。- 报告正文里每条进入正文的发现行末附隐藏锚点
<!-- fid:<对应 jsonl 的 id> -->;写入前用 grep 抽取报告全部 fid,与index-ids.txt做集合比对(comm),缺一条或多一条即判失败、回去对齐。fid 锚点让漏条精确到具体 id,而非只比对总数。
发现记录契约(jsonl,每行一条)
本 skill 只规定抽象契约;具体字段名、status 取值、去重键、jq 派生命令全部由所选模板的"发现记录与派生"节定义(见 step 2),4 个模板各自可不同。
- 发现记录 = jsonl,一发现一行,绝不写区间(禁止
001~021、(19处)、等7处)。 - 每行必含:全局唯一
id(带源前缀,如src1-001,避免多份报告各自从 001 起 append 撞车)、title、source_report、一个status、以及供去重的键字段。 status的取值集合、status→{进正文 / 进排除项 / 仅作覆盖证明 / 忽略}的桶映射、去重键字段由模板定义。其中"进正文"桶再分两档:已确认(确凿)与待复核(需人工确认,归入"待人工复核项汇总"章节);两档对应的具体 status 名同样由模板给出。PoC 是否强制不按这两档简单二分,而按"是否做过验证动作"裁决——以下方"完整性要求"为准。- 两个 id 清单是模板 derive 与本 skill 闸门之间的通用接口(由模板的 jq 派生命令产出):
shared/coverage-ledger/index-ids.txt—— 应纳入报告正文的发现 id 全集("进正文"桶)。shared/coverage-ledger/exclude-ids.txt—— 应纳入"误报 / 排除项"章节的发现 id 全集("进排除项"桶)。
- 若上游各 skill 已在发现当下落行(见各 skill 的"发现即落行"约定,其字段即对应模板的 schema),本 step 直接复用,无需重新抽取。
执行步骤
收集已完成步骤的分析结果(并集收集,禁止只读摘要):
先取前序步骤 step outcome 中登记的
result_file/summary_file路径。再递归列出共享工作区下的报告文件(用你当前可用的任意手段枚举,不限定具体工具),避免遗漏未登记为 reference 的报告:workspace 根目录下的
shared/目录树,以及所有sub_agents/**/shared/目录树(含其下任意层级子目录,如shared/recon-3/analysis-summary.md),匹配全部分析报告 md(如*-report.md、*analysis*.md、*-overview.md、fallback_*.md、comprehensive-*.md等)。planner_skills_index.md、findings-index.md等纯流程/索引/中间产物文件不算分析报告,跳过。还须跳过本 skill 自己上一轮的输出——即shared/根下的最终聚合报告,用双判据识别:(1) 文件名为{project}-security-report.md(step 7 产物,与子报告*-api-security-report.md等不同名,不误伤);(2) 文件内含## 源报告覆盖表这一聚合报告专属章节(子报告不会有,名字漂移时的兜底)。否则重跑时上一轮最终报告会被当源报告读入,发现翻倍、统计虚高。开始登记前先准备目录与清空旧产物:
mkdir -p shared/coverage-ledger/findings;删除本轮要重建的shared/findings-index.md(覆盖写,不在旧内容上追加),避免上一轮残留行污染本轮对账。若shared/coverage-ledger/findings/下已存在上游 skill 发现当下落盘的 jsonl,直接复用,跳过本份的重新抽取。将两类来源取并集,逐份完整读取详细报告文件本身(分页读取到 EOF:单页上限约 200 行/20KB,文件更大时必须用
offset继续翻页直到读完,禁止只读首页就开始整合):每读完一份,把其每条发现抽成规范化 jsonl 行(一发现一行,字段见 step 2 所选模板的 schema,绝不写区间行如001~021、(19处)、等7处),用追加写入该源对应的shared/coverage-ledger/findings/<src>.jsonl(<src>取源报告文件名去扩展名),再读下一份。不要求一次性把全部源报告 load 进上下文——增量抽取并落盘即可,规避上下文过载。- 结构化交付物(端点授权矩阵的每一行、技术栈表等)同样逐行落成 jsonl 记录,不得概括为一条。
- 模板若有自身的覆盖面对账需求(如 code-audit 的入口点清单),按该模板 reference 文件的约定另行落盘其专属台账,本通用步骤不规定其格式。
机械派生(运行所选模板的派生命令):所有源报告抽完后,执行 step 2 所选模板"发现记录与派生"节给出的 jq 派生命令,产出三件机器产物——
shared/findings-index.md:索引视图(列由模板定义);shared/coverage-ledger/index-ids.txt:应纳入报告正文的发现 id 全集("进正文"桶,去重、排序);shared/coverage-ledger/exclude-ids.txt:应纳入"误报 / 排除项"章节的发现 id 全集("进排除项"桶,去重、排序)。
命令体(含该模板的 status 取值、去重键、索引列、去重键非空校验)在模板里,本步只负责执行它并确认三件产物生成。三者都是机器产物、不可手工编辑。
严禁用子 agent 的
final_answer/ 摘要替代详细报告:子 agent 的详细报告(写在各自sub_agents/<id>/shared/下)通常比冒泡上来的摘要包含更多发现、端点矩阵、攻击链等表格,必须读详细文件,否则最终报告会丢失这些内容。区分发现型与侦察型报告:含漏洞/发现/矩阵的报告(如
*-api-security-report.md、comprehensive-*.md、fallback_*.md、secret-detection-report.md)其发现必须逐条带入;纯侦察/架构报告(如*-overview.md、*-technology-stack-report.md)用于补全"配置/架构类发现"与覆盖声明的上下文,正文可不照搬,但其结构化交付物(端点授权矩阵、技术栈表等)仍须逐行抽成 jsonl 落shared/coverage-ledger/findings/<src>.jsonl,以便 step 6 对账,避免端点矩阵这类交付物被漏。去重但不折叠:多份源报告报同一漏洞时,仅当模板定义的去重键完全相同才合并为一条;去重键任一字段不同即各自独立成条,禁止跨键折叠。
选择报告模板:根据当前 agent 类型,从
${SKILL_DIR}/reference/读取对应模板文件:Agent 类型匹配 模板文件 code-audit${SKILL_DIR}/reference/code-audit-template.mdpentest-*${SKILL_DIR}/reference/pentest-template.mdhost-defense-*${SKILL_DIR}/reference/host-security-template.md其他 / 不确定 ${SKILL_DIR}/reference/general-template.md使用
read_file加载模板文件,按模板结构组织报告内容。组织全部发现:以 step 1 落盘的 jsonl 为数据源(
shared/coverage-ledger/findings/*.jsonl去重后的每一条 + 已逐份读入的详细报告内容;findings-index.md是其 jq 派生视图),严禁回头从冒泡上来的步骤结果/摘要重新抽取——正文、index 与 jsonl 三者必须同源。把每个漏洞实例独立成节,禁止合并或折叠。每条进正文的发现(已确认 / 待复核两档)的详细发现节必须携带其在子报告中的完整证据链——全部测试场景行、全部受影响端点、全部源码片段——而非一句话加两行表;大块明细(端点/接口清单、路由树、算法源码、完整测试矩阵)正文引用、附录展开,逐条原样保留,禁止抽样。- 组织结构按所选模板:详细发现卡片的标题约定、章节顺序、是否按入口点/按严重度组织、§8 配置类发现如何承载,全部由 step 2 选定的 reference 模板规定,本通用步骤不再重述。
- 每条进入正文的发现都打 fid 锚点:无论它以详细卡片、配置/架构类发现表行、还是系统性发现的形式出现,其所在行(卡片标题行或表格行)的行末都追加
<!-- fid:<该发现在 coverage-ledger/findings 里的 id> -->。fid 是 step 6 闸门唯一的核对依据——漏打一个 fid 等同漏一条发现。排除项发现(status 属模板定义的"排除"桶)写入"误报与排除项"章节时同样打 fid(核到exclude-ids.txt)。
为进正文的发现编写 POC:按下方"POC 规则"选择格式、构造内容、执行自检。PoC 强制与否取决于"是否实际做过验证动作",而非最终落哪个档位:凡做过验证尝试的发现(已确认档,以及"待复核"中已构造并发送请求 / 已做可达性分析等做过验证动作的)都必附 PoC——哪怕没复现成功,也要留下当时构造/尝试的请求/payload/分析作为过程证据;只有压根没法验、没动手的发现才可不附(详见下方"完整性要求")。
填充评估完整性章节:同样以 step 1 落盘的 jsonl(
shared/coverage-ledger/findings/*.jsonl)+ 详细报告为准,提取"待复核"桶条目(附无法确认的原因和排查建议)、"排除"桶条目(附排除依据)、已检测但未发现漏洞的维度、因前置条件不足或环境限制未能覆盖的维度,分别填入模板对应章节。最后编写结论章节,综合所有发现给出整体风险评级和后续建议。确定性 fid 闸门(写入前必须执行,不通过禁止写入):用 grep + comm 机械比对"报告里打了 fid 的发现集合"与 step 1 派生的两个 id 清单。闸门只消费 id 文件、与 status 取值无关(status 语义已在模板 derive 里处理)。
REPORT为待写入的最终报告文件路径。REPORT=shared/<project>-security-report.md # 替换为实际路径 sort -u shared/coverage-ledger/index-ids.txt > /tmp/ids_reg # 应进正文的发现 id 全集 touch shared/coverage-ledger/exclude-ids.txt sort -u shared/coverage-ledger/exclude-ids.txt > /tmp/ids_exc # 应进"误报/排除项"章节的 id 全集 sort -u /tmp/ids_reg /tmp/ids_exc > /tmp/ids_allowed # 报告里允许出现的全部 fid # 报告侧:正文里实际打出的 fid grep -oE 'fid:[A-Za-z0-9_-]+' "$REPORT" | sed 's/^fid://' | sort -u > /tmp/ids_rep miss=$(comm -23 /tmp/ids_reg /tmp/ids_rep) # 应进正文却无 fid → 折叠 / 漏条 exc_miss=$(comm -23 /tmp/ids_exc /tmp/ids_rep) # 排除项未在"误报/排除项"章节打 fid extra=$(comm -13 /tmp/ids_allowed /tmp/ids_rep) # 报告有、基准(含排除项)均无 → 凭空多出 / fid 写错 [ -z "$miss$exc_miss$extra" ] || echo "FAIL: 缺失[$miss] 排除缺[$exc_miss] 多余[$extra] —— 必须逐条对齐 fid 后重写,禁止直接写入"- 核对项(全部须对得上,对不上回去补全再写):
miss必须为空:index-ids.txt里每个 id 都要在报告正文出现对应<!-- fid:id -->;缺失即折叠或漏条。exc_miss必须为空:exclude-ids.txt里每个 id 都在"误报与排除项"章节打了 fid。extra必须为空:报告里的 fid 都必须能在基准(index-ids ∪ exclude-ids)里找到;多出即 fid 写错或凭空发现。severity_counts/ 风险统计表的每个数字 == 正文实际列出的对应严重度条目数。
- 折叠话术扫描:对全文检索禁用措辞(
还有相同、其余+个、同理、等、以此类推、不再赘述、同上、略、代表性、典型几例、…、~+区间)。任一命中且未展开为逐条实体的,必须先展开/修正再写入(规则见"通用规范 → 禁止折叠话术")。 - 覆盖声明里标
done的维度,其对应交付物(尤其结构化矩阵/表格)必须实际存在于报告正文,禁止"声明 done 但正文缺表"。 - 模板若有自身的覆盖面对账(如 code-audit 的入口点节数对账),按该模板 reference 文件的约定追加执行,本通用闸门只负责 fid 集合一致性。
- 最终报告末尾必须附源报告覆盖表(code-audit 模板见对应章节;其他模板在末尾追加同等表格):每份源报告 → 贡献的发现编号 → 是否全部纳入(是 / 部分+原因)。
- 核对项(全部须对得上,对不上回去补全再写):
写入文件:将完整 Markdown 报告写入共享工作区目录(workspace 根目录下的
shared/子目录,使用绝对路径)。文件名格式:{project}-security-report.md。必须是单一报告文件:顶层综合、全部详细发现、全部附录枚举都写入这一个文件,禁止把附录或任何章节拆分到其他文件。报告文件路径:写入完成后,在调用
update_current_step时将文件的绝对路径填入references字段,禁止使用相对路径。
通用规范(所有模板共享)
禁止折叠话术(硬约束)
折叠 = 用计数、抽样、省略号、"等"字或引用,替代逐实例枚举的任何手法。 折叠会导致漏条、统计虚高、覆盖面对账失真,一律禁止。以下五类话术明令禁用:
| 类 | 手法 | 禁用话术(示例,凡同义变体一并禁止) |
|---|---|---|
| A | 计数替代枚举 | "还有相同 18 个接口存在同样问题"、"其余 N 处同理"、"同类漏洞共 N 个,不再赘述"、"批量存在于 N 个端点" |
| B | 省略号 / "等"字收尾 | "…"、"等"、"等等"、"诸如…等"、"以此类推"、"限于篇幅省略其余" |
| C | 抽样 / 代表性 | "选取典型/代表性几例"、"仅列高危若干"、"举例说明" |
| D | 引用替代卡片 | "同 EP-01 不再重复"、"详见前述/参见上文"(用来省掉发现卡片本身) |
| E | 表格行折叠 | 端点授权矩阵/配置发现表写"其余端点同上/略";统计表只给总数、正文无对应实体 |
正向规则(必须满足):
- 数字必须逐条可数:报告中出现的任何数字(风险统计表的 N、覆盖声明端点数、"共发现 N 处")都必须在正文找到 N 个独立实体(EP 小节 / F 卡片 / 矩阵行)一一对应,数字 = 正文实体数。
- 相似不是折叠理由:同一漏洞影响 N 个入口点,就写 N 个 EP 小节(或 N 个 F 卡片);即使内容高度相似也要逐个落地,禁止"1 条 + 还有 N-1 个同理"。
- 跨节引用边界:复合 ID(攻击链 step 引用某条发现、源报告覆盖表对账互指)是允许的合法引用;仅当引用被用来替代发现卡片正文本身时,才算 D 类折叠。
完整性约束
- 所有有危害的发现(CRITICAL 到 LOW)必须逐条出现并各自打 fid,禁止折叠或省略(见上方"禁止折叠话术");归属到所选模板规定的组织单元(入口点节 / 严重度分组 / 系统性发现节),不得因组织方式丢条
- 同一漏洞点被多个组织单元到达时,每个单元下独立成节
- 同一根因导致多个独立攻击面时,每个攻击面独立成节
- 结构化交付物章节(如端点授权矩阵、攻击链、技术栈表):源报告中已产出的整表必须完整填入对应章节,禁止压扁成单条发现或省略整表;具体章节编号由所选模板规定
- 有明确数据流或可构造 POC 的系统性漏洞(硬编码密钥、弱加密、可利用依赖漏洞等)逐条写发现卡片;仅配置/基线/架构缺陷(RBAC 缺失、安全头缺失等)用"配置/架构类发现"表格汇总,不需逐条 POC——此处"汇总"指每项独立成行并各自打 fid(如每个缺失安全头一行、每条 RBAC 缺失一行),仅免去 POC,不得把多项合并为一行或概括为一句话
- 风险统计表数字必须与实际 finding 数量一致
- 所有"待复核"档发现必须出现在"待人工复核项汇总"章节,逐条附注无法确认的具体原因和排查建议
- 初步检测后排除的误报项必须在"误报与排除项"章节列出,说明排除依据
- 已检测但未发现漏洞的维度必须在"已验证安全的维度"章节声明
- 因前置条件不足(如无测试账号、无源码权限、WAF 拦截等)导致无法覆盖的维度必须在"评估局限性"章节说明
- "结论"章节必须包含整体风险评级和后续建议
无损整合自检(写入报告前必须执行)
整合完成后,逐项对照以下检查表。任一项不通过则补全后再写入。
| # | 检查项 | 通过标准 |
|---|---|---|
| 1 | 子报告全部读完 | 每份 result_file 已分页读到 EOF,未只读首页 |
| 2 | 端点/接口枚举保留 | 子报告中的端点清单逐条出现在最终报告正文或附录 |
| 3 | 测试矩阵保留 | 每个测试场景独立成行,未折叠为单行结论 |
| 4 | 算法/源码片段保留 | 子报告中的代码块原样出现 |
| 5 | 配置/响应头逐条保留 | CORS 响应头、缺失安全头、OPTIONS 预检等逐条出现 |
| 6 | 限制/WAF/环境说明保留 | 子报告中的环境与前置条件说明已并入"评估局限性" |
| 7 | finding 数量对账 | 最终报告 finding 总数 ≥ 各子报告 finding 之并集,且统计表数字一致 |
| 8 | 来源映射完整 | 每份源报告在"源报告覆盖表"(章节编号见所选模板)中均有记录,标注贡献的发现编号及是否全部纳入 |
POC 规则
格式选择
| 场景 | 格式 | 原因 |
|---|---|---|
| 单一请求即可触发 | http |
可直接复制到 Burp/curl |
| 需多步骤、条件判断或自动化 | python |
单请求无法表达完整攻击链 |
| 默认凭证 / 配置类 | http |
一次请求验证即可 |
同一报告内尽量统一格式;对于单请求可触发的漏洞,优先选 http。
构造规则(代码审计)
代码审计的 POC 从源码推导,未经运行时验证。这条静态 POC / 静态 confirmed 路径仅适用于 pure code-audit 报告(无可运行目标,或动态验证明确不可行动)。若同一任务存在可行动的运行目标并在产出 graybox / pentest 报告,总结论里的 confirmed 仍必须按运行时效果判定,静态 POC 只作白盒佐证。 必须遵循:
- URL 必须从代码注解推导:@RequestMapping / @GetMapping / web.xml 映射 → 完整 URL path
- HTTP 方法必须从代码注解推导:@GetMapping → GET, @PostMapping → POST
- 参数名必须从 source 表达式提取:request.getParameter("questionData") → 参数名 questionData
- payload 必须匹配漏洞类型(见下方适配表)
- 前置条件显式标注:如需特定配置、认证、角色等,在 POC 上方注明
- POC 末尾加注释行:
# 基于代码分析构造,未经运行时验证
构造规则(渗透测试)
渗透测试的 POC 是实际发送并得到响应的请求。必须遵循:
- 请求必须是实际发送过的(从 HAR/流量捕获中提取)
- 响应要点附在 POC 后:关键响应字段/状态码
- 不编造未测试的请求
漏洞类型 payload 适配
| CWE | 漏洞类型 | payload 要求 |
|---|---|---|
| CWE-78 | 命令注入 | payload 包含命令分隔符 + 可观测命令,如 ; id 或 ` |
| CWE-89 | SQL 注入 | payload 包含 SQL 语法,如 ' OR 1=1-- 或 1 UNION SELECT ... |
| CWE-79 | XSS | payload 包含脚本标签,如 <script>alert(1)</script> |
| CWE-611 | XXE | payload 包含外部实体声明 |
| CWE-918 | SSRF | payload 包含内网地址,如 http://127.0.0.1:port |
| CWE-798 | 硬编码凭证 | payload 直接使用发现的凭证 |
| CWE-352 | CSRF | 提供完整 HTML PoC 表单 |
| CWE-22 | 路径穿越 | payload 包含 ../ 序列 |
完整性要求
PoC 是否强制,按"是否实际做过验证动作"裁决,不按最终档位。其中 pure code-audit 可用静态 POC 支撑静态 confirmed;但 graybox / pentest / 有可运行目标的报告里,confirmed 仍要求运行时效果证据:
- 已确认档(
confirmed):必附 POC。落入此档就强制,不因确认来自静态分析而非运行时、或证据带"未经运行时验证"标注而豁免——按对应场景的构造规则(见上方各"构造规则"小节)构造即可。但带"未经运行时验证"尾注的静态 POC,只能用于 pure code-audit 报告;graybox / pentest / 有可运行目标的报告要进confirmed,仍需运行时效果证据。 - 待复核档(
needs_review)中做过验证尝试但未闭环的:必附 PoC。把当时构造/尝试的请求、payload 或可达性/数据流分析片段如实贴出,并注明"为何未复现、缺哪一环"。此处 PoC 是"试过什么"的过程证据,不要求它证明危害。 - 待复核档中确无验证路径、未做任何尝试的:PoC 不适用,可不附。(注:若验证路径其实可用却没走,该发现应判为未闭环 /
uncovered推回去验证,而不是停在这里——见common/closure-verification.md。) - 证据链本身已是实际发出并捕获的请求/响应时,可直接充当 POC。
- 配置/架构类发现(安全头缺失、RBAC 缺失等)用表格汇总,不需 POC。
- 一句话:做过验证动作的发现都要留下 PoC(哪怕没复现成功);只有压根没法验、没动手的才免。
POC 最终自检(写入报告前必须执行)
所有 POC 写完后,逐条对照以下检查表。任一项不通过则修正后再写入报告。
| # | 检查项 | 通过标准 |
|---|---|---|
| 1 | URL 路径与源码一致 | 代码审计:URL 能在 @RequestMapping / web.xml / 路由配置中找到对应;渗透测试:URL 来自实际发送的请求 |
| 2 | HTTP 方法正确 | GET/POST/PUT/DELETE 与代码注解或实际请求一致 |
| 3 | 参数名正确 | 参数名可在 source 表达式(request.getParameter / @RequestParam / body 解析)中找到 |
| 4 | payload 匹配漏洞类型 | 对照"漏洞类型 payload 适配"表,payload 中包含该 CWE 对应的特征语法 |
| 5 | 前置条件已标注 | 若漏洞触发需要特定配置/认证/角色/数据状态,POC 上方已注明;无前置条件则标注"无" |
| 6 | 格式一致性 | 同一报告内同类单请求漏洞使用相同格式(http 或 python),不混用 |
| 7 | 代码审计标注 | 代码审计场景的 POC 末尾包含"基于代码分析构造,未经运行时验证"注释 |
文件格式
- 格式为
.md,不使用 JSON - 文件名包含项目标识:
{project}-security-report.md