自动漏洞验证SKILL设计
面向企业测试内网的白盒漏洞动态验证SKILL。核心不是重新发现漏洞,而是把上游报告中的
verification_handoff编译成独立、可调度、可恢复的单case任务,执行真实PoC并保存证据,最后形成可复核的三态验证报告。
通用SKILL组织原则参考Agent SKILL设计。
当前版本:2.1.1。
定位
这个SKILL主要处理:
- 消费白盒报告中的
verification_handoff; - 校验测试目标、身份和运行前置条件;
- 为每个漏洞编译独立的单case task;
- 在授权测试内网中通过宿主可用的HTTP客户端执行PoC;
- 根据实际响应调整报告允许调整的PoC细节;
- 保存逐次网络交互、实际客户端、oracle判断和结论依据;
- 支持最多3个无冲突单case执行器并发;
- 在没有子Agent/Worker时由主Agent完整顺序执行;
- 在结构化HTTP工具不可用或不能表达请求时回退到已有
curl等命令行客户端; - 汇总所有case并生成
VULNERABILITY_VERIFICATION_REPORT.md。
明确不处理:
- 漏洞发现;
- 源码审计;
- 重新判断漏洞类型;
- 根据漏洞知识库设计新的攻击路径;
- 自动修复漏洞;
- 修改业务源码;
- 把一个case扩展成报告未定义的额外测试范围。
这里需要始终保持一个边界:
验证阶段执行既定flow并判断oracle,不重新承担漏洞挖掘阶段的职责。
上游报告决定要验证什么;这个SKILL负责把它可靠地执行、记录和汇总。
核心设计
整个设计拆成三层:
flowchart TD A[控制层<br/>SKILL / 主Agent] --> B[任务协议层<br/>tasks/case-id.json] B --> C[执行层<br/>单case子Agent / Worker] B --> D[执行层<br/>主Agent顺序回退] C --> E[Evidence] D --> E E --> F[Result] F --> A A --> G[run.json] A --> H[最终报告]
各层职责:
| 层 | 内容 | 定位 |
|---|---|---|
| 控制层 | SKILL.md与主Agent | 输入校验、任务编译、调度、验收、恢复、汇总、用户交互和最终报告 |
| 任务协议层 | .auto-verification/tasks/<case-id>.json | 固化一个case的全部执行输入、策略和读写边界 |
| 执行层 | 单case子Agent/Worker或主Agent | 只执行一个task,写该case的evidence和result;Pi优先使用vulnerability-verifier |
| 数据契约 | references/context-schema.md | 定义target、run、task、result和evidence结构 |
| 报告契约 | assets/VERIFICATION_REPORT_TEMPLATE.md | 定义最终人类可读报告格式 |
子Agent不是第四层业务逻辑,也不是另一个SKILL。它只是任务协议的一个执行后端。
为什么使用单case task
直接把完整报告交给子Agent会产生三个问题:
- 子Agent会重新读取大量与当前case无关的上下文;
- workflow、身份、oracle和输出路径仍然需要它再次解释;
- 并发执行时很难证明每个case只被一个执行者拥有。
单case task把这些不确定性提前消解:
flowchart LR A[白盒报告中的一个case] --> B[主Agent选择variant] B --> C[绑定身份和资源] C --> D[编译workflow与oracle] D --> E[单case task] E --> F[执行器] F --> G[evidence + result]
task是已编译协议,不是给执行器的研究材料。执行器不再决定:
- 验证哪个漏洞;
- 选择哪个variant;
- 读取哪些源码或漏洞Reference;
- 使用哪个身份;
- 写入哪个结果文件;
- 是否扩展攻击路径。
为什么不向执行器提供漏洞References
这个SKILL位于漏洞发现之后。上游已经提供:
- 身份槽位和认证类型;
- required capabilities;
- workflow;
- oracle;
- evidence level;
- negative control;
- prerequisites;
- allowed adjustments或variants。
漏洞Reference主要帮助发现和分析漏洞类型,而不是执行既定验证flow。把这些Reference交给执行器会重新引入:
- 类型分析;
- 攻击路径扩展;
- 与当前case无关的上下文;
- 对已编译task的二次解释。
因此执行器只消费task及其显式引用:
flowchart TD A[Task] --> B[target_ref] A --> C[read_scope.input_files] A --> D[当前case已有write_scope] X[漏洞Reference] -.禁止读取.-> A Y[源报告] -.禁止读取.-> A Z[run.json] -.禁止读取.-> A
若task不足以执行或判断,结论是inconclusive,而不是让执行器回到漏洞发现阶段。
完整Workflow
笔记中的执行结构严格对应SKILL.md定义的五步workflow,不额外引入正式阶段或门禁:
flowchart LR P1[1 初始化与门禁] --> P2[2 编译单case任务] P2 --> P3[3 调度与通用回退] P3 --> P4[4 单case执行与判定] P4 --> P5[5 汇总与报告] P5 --> DONE[completed]
可达性检查属于“初始化与门禁”;子Agent发现、Child验收和顺序降级属于“调度与通用回退”;Evidence、Result和三态判断属于“单case执行与判定”。它们是所属步骤的子流程,不是独立阶段。
运行状态
run.json.status记录运行状态,不表示另一套workflow阶段:
| 状态 | 含义 |
|---|---|
initialized | 已创建运行上下文,尚未开始PoC |
testing | 可达性通过,正在执行或验收case |
awaiting_retry | 基础设施阻断,等待主Agent统一询问重试 |
completed | 全部case都有终态且最终报告已生成 |
case状态:
| 状态 | 含义 |
|---|---|
pending | 已编译但尚未执行 |
running | 当前有且只有一个执行者拥有该case |
verified | 动态证据满足全部必要oracle |
not_verified | 完成有效验证,但oracle未满足 |
inconclusive | 环境、凭证、依赖或能力不足以评估oracle |
没有skipped。报告中的每个漏洞ID都必须进入一个最终三态。
完整性优先
子Agent、HTTP扩展、命令行客户端和并发都属于执行手段,不是完整性条件。
以下问题不能导致case丢失:
- 当前宿主没有子Agent能力;
- 子执行器未发现或不可用;
- child启动失败;
- 并发执行中断;
- child结果验收失败;
- 某个HTTP客户端无法表达当前flow。
这些情况只改变执行路径:
flowchart TD A[全部已编译case] --> B{子Agent路径可用?} B -->|是| C[委派执行器可表达且无冲突的case] B -->|否| D[主Agent顺序队列] C --> E{结果验收通过?} E -->|是| F[Canonical Result] E -->|否| D D --> F F --> G{仍有pending case?} G -->|是| B G -->|否| H[完整性检查]
并行失败不能转化为:
- 漏洞验证失败;
inconclusive;- case遗漏;
- 提前结束workflow。
只有目标、凭证、依赖、协议或宿主实际能力使oracle无法评估时,case才是inconclusive。
实现结构
| 路径 | 作用 |
|---|---|
profile_sys/skills/auto-vulnerability-verification/SKILL.md | 主workflow、门禁、调度、回退、判定和报告 |
profile_sys/skills/auto-vulnerability-verification/references/context-schema.md | 所有运行文件的数据契约 |
profile_sys/skills/auto-vulnerability-verification/assets/VERIFICATION_REPORT_TEMPLATE.md | 最终验证报告模板 |
profile_sys/skills/auto-vulnerability-verification/agents/openai.yaml | 界面名称、简介和默认Prompt |
profile_sys/agents/vulnerability-verifier.md | Pi环境下的薄单case执行器 |
当前不增加脚本。执行本身包含基于真实响应的有限判断;只有当重复出现确定性结构校验失败时,才值得补充专用validator。
.auto-verification是Canonical Context
聊天上下文只是临时缓存,.auto-verification/才是真正的运行上下文。
| 路径 | Source of Truth |
|---|---|
target.json | 测试地址、身份和认证信息 |
run.json | 运行身份、来源指纹、状态、队列、执行模式和计数 |
tasks/<case-id>.json | 单case执行协议 |
results/<case-id>.json | case结论、attempt和oracle checks |
evidence/<case-id>-attempt-<n>.json | 实际网络交互、客户端和可见事实 |
VULNERABILITY_VERIFICATION_REPORT.md | 面向人的最终汇总 |
数据关系:
flowchart TD R[白盒报告] --> T[tasks] X[target.json] --> T T --> E[evidence] E --> Y[results] Y --> S[run.json] Y --> P[VULNERABILITY_VERIFICATION_REPORT.md] E --> P
run.json不复制:
- 完整白盒报告;
- workflow;
- 请求;
- 响应;
- 漏洞详情。
它只保存恢复和协调所需的最小状态。
Canonical Writer
只有主Agent可以写全局Canonical State:
flowchart TD A[Executor A] --> X[result/evidence A] B[Executor B] --> Y[result/evidence B] C[主Agent顺序执行] --> Z[result/evidence C] X --> P[主Agent验收] Y --> P Z --> P P --> R[run.json] P --> F[最终报告]
单case执行器可以写:
- task声明的
result_file; - 匹配
evidence_prefix的文件; - 所选客户端产生且仍位于当前evidence prefix的响应、Header、元数据或下载文件。
单case执行器不能写:
run.json;- 最终报告;
- 其他case的result或evidence;
- task未声明的工作区文件。
主Agent同时是唯一可以:
- 向用户提问;
- 记录重试决定;
- 修改执行模式;
- 调度下一批case;
- 合并最终计数。
写入顺序
每次网络交互和case状态都有固定写入顺序:
flowchart TD A[执行一次网络交互] --> B[立即更新当前attempt evidence] B --> C{attempt结束?} C -->|否| A C -->|是| D[更新result attempts与oracle checks] D --> E{case得到终态?} E -->|否| F[确认允许调整和max_attempts] F --> A E -->|是| G[完成final result] G --> H[主Agent更新run.json] H --> I[重新读取并验收]
核心顺序:
- network interaction:先写evidence;
- attempt:先完成evidence,再更新result;
- case:先完成final result,再更新
run.json; - 下一case:前一个case重新读取并验收后才能开始。
如果把队列状态先更新、Artifact后写入,中断时就会出现“case已经完成但结果不存在”的不可恢复状态。
Snapshot与恢复
一次运行由以下信息绑定:
run_id;source.path;source.sha256;target_ref。
恢复过程:
flowchart TD A[发现已有.auto-verification] --> B[读取run.json] B --> C[重新计算source.sha256] C --> D{报告是否一致?} D -->|否| E[停止合并旧结果并要求新运行] D -->|是| F[检查running case] F --> G[读取task/result/evidence] G --> H{最后检查点完整?} H -->|是| I[按replay策略继续] H -->|否| J[根据可确认状态决定inconclusive或恢复]
replay控制中断后的处理:
| 值 | 恢复动作 |
|---|---|
safe | 可以从最后完整检查点继续 |
check_state | 先确认目标状态,再决定是否重放 |
never | 不重放;无法确认时为inconclusive |
不能根据对话记忆推断请求是否发送成功,也不能为了恢复方便重放状态未知的有副作用请求。
1. 初始化与门禁
这一步确定本轮验证到底是什么。
必须取得:
- 用户明确给出的白盒报告路径;
- 完整测试地址,包括scheme、host和必要base path;
- 报告要求的测试身份;
- 身份标签、角色和已知主体ID;
- 跨身份case需要的对象归属或权限基线。
报告路径未提供时直接询问,不能扫描工作区、猜测文件名或自动选择候选报告。
报告契约
报告必须包含:
report;summary;- 统一漏洞详情;
verification_handoff;coverage。
正式运行前检查:
| 检查项 | 要求 |
|---|---|
| 总数 | summary.cases_total等于漏洞详情数量 |
| ID | 汇总与详情ID集合完全一致 |
| 元数据 | 标题、严重性、静态状态一致 |
| 步骤数 | 汇总steps与实际workflow一致 |
| Schema | 新报告使用report.schema_version=1和verification_handoff.schema_version=1;无版本旧报告仍须满足完整handoff,兼容转换不得补造攻击路径 |
| handoff | 身份需求、required capabilities、结构化send、oracle、negative control和allowed adjustments完整 |
| workflow | 步骤连续,或至少一个variant前置条件满足 |
契约错误不静默修补,也不在验证阶段重新设计flow。
测试目标和身份
target.json是地址与凭证的Canonical来源。Evidence和报告可以记录实际发送的认证信息,但不能反过来作为后续请求的凭证来源。
认证类型只支持:
- Cookie;
- JWT;
- 自定义HTTP Header。
身份规则:
| 场景 | 绑定要求 |
|---|---|
| 匿名 | identity_id=anonymous,移除所有配置认证Header |
| 普通认证 | 使用一个稳定身份ID |
| 水平权限边界 | actor与resource_owner是两个真实不同身份,并先建立对象归属基线 |
| 垂直权限边界 | low_privilege与high_privilege角色真实不同,并先建立正常权限差异 |
| 多阶段flow | 每个step单独标记identity_id |
不得把同一凭证复制为两个身份,也不得仅根据名称推断角色、租户或对象归属。
task与run.json只引用身份ID,不复制凭证;执行evidence和最终报告保留完整请求及认证信息。
企业测试环境策略
当前运行环境是可快速快照恢复的企业测试内网,因此策略明确为:
| 策略 | 值 |
|---|---|
allow_destructive_poc | true,允许已编译flow中的破坏性PoC |
preserve_unredacted_outputs | true,Evidence、Result和Report保留完整信息 |
follow_workflow_without_scope_expansion | true,禁止引入flow外动作或扩大范围 |
| cleanup | 不由执行器设计,依赖外部快照恢复 |
限制的对象不是PoC的破坏性,而是执行器的主观扩张。
主Agent和所有执行器都不得:
- 引入workflow之外的动作;
- 引入
allowed_adjustments之外的变体; - 增加目标范围;
- 增加无依据的请求数量;
- 扩大载荷规模;
- 延长持续时间;
- 扩大既定flow之外的影响范围。
可达性门禁
发送PoC前先执行无副作用可达性检查。
入口选择顺序:
- 报告明确提供的健康检查;
- 只读公开入口;
- base URL。
判断:
| 观察 | 结论 |
|---|---|
| 任意HTTP响应 | 网络层可达 |
| 5xx或持续API异常 | 网络可达,但仍需判断服务是否可验证 |
| DNS、连接、TLS、代理或超时错误 | 基础设施问题 |
基础设施问题不能记为漏洞not_verified。
主Agent负责:
- 保存可达性状态;
- 将运行切换到
awaiting_retry; - 统一询问用户是否重试;
- 用户同意后重新执行门禁;
- 避免无限自动重试。
工具没有提供时间、延迟或其他字段时写null,不能根据模型等待时间估算。
2. 编译单case任务
每个漏洞都编译为:
.auto-verification/tasks/<case-id>.json
这一步由主Agent完成,且所有执行后端使用同一文件。
task结构:
| 部分 | 关键字段 | 作用 |
|---|---|---|
| 运行引用 | schema_version、run_id、target_ref | 绑定运行与凭证来源 |
| 读取范围 | read_scope.input_files | 只允许workflow明确需要的上传或输入文件 |
| 写入范围 | result_file、evidence_prefix | 限定当前case输出 |
| Policy | 破坏性PoC、完整输出、禁止范围扩展、重试和TLS策略 | 固化企业执行边界 |
| Case身份 | ID、标题、prerequisites、identity bindings | 固化当前验证对象和身份 |
| Variant | selected_variant | 由主Agent预先选择,执行器不再路由 |
| 并发 | parallel_safe、resource_claims | 判断能否与其他case同时执行 |
| 恢复 | side_effect、replay | 控制隔离与中断重放 |
| 验证协议 | workflow、oracle、evidence level、negative control | 定义如何执行和判断 |
| 调整 | allowed_adjustments、max_attempts | 限定PoC调整空间和停止条件 |
Variant选择
主Agent选择第一个:
- 前置条件满足;
- 当前部署可执行;
- 当前宿主有足够观察能力;
- 能形成报告要求证据等级;
的workflow或variant。
task只保存选中的实际flow。执行器不能重新选择variant。
PoC调整
allowed_adjustments优先直接来自handoff。兼容旧报告时只允许来自:
- 报告
alternatives中明确的可执行差异; - 主Agent根据报告协议做出的等价规范化。
不得从漏洞描述、Reference或通用payload经验补充调整。
执行器只能在初始PoC未满足oracle后,根据实际响应选择允许调整。
max_attempts不能超过:
默认尝试 + 可解释的允许调整数量
不能把调整机制变成无边界枚举。
读取范围
执行器只能读取:
- 当前task;
- task中的
target_ref; read_scope.input_files;- 恢复当前case所需的
write_scope文件。
不得加入:
- 漏洞Reference;
- 源报告;
- SKILL正文;
run.json;- 其他case;
- workflow没有明确使用的工作区文件。
并发和资源声明
并发不是根据“请求看起来像GET”判断,而是根据case是否访问同一目标状态。
每个task声明:
parallel_safe;resource_claims;- 每个资源的
read|write访问。
冲突规则:
| Case A | Case B | 是否可并发 |
|---|---|---|
同一资源read | 同一资源read | 可以 |
同一资源read | 同一资源write | 不可以 |
同一资源write | 同一资源write | 不可以 |
| 无法可靠声明资源 | 任意case | parallel_safe=false,独占执行 |
parallel_safe=false不等于不能委派,只表示它执行期间不能存在其他active case。
side_effect和replay只用于:
- 判断并发隔离;
- 中断恢复;
- 防止未知状态下重复请求。
它们不改变handoff内容,也不阻止task已经声明的破坏性PoC。
3. 调度与通用回退
通用宿主能力发现
不能通过settings、package目录、磁盘上的agent定义或工具文档推断能力已经可用。正式调度前检查当前会话真实暴露的:
- 子Agent/Worker创建、隔离、并发与完成通知能力;
- 子执行器可用的文件读取和写入能力;
- 主Agent与子执行器各自可用的结构化HTTP工具;
- 命令执行能力,以及本地已经存在的
curl或等价客户端; - 当前case要求的Browser、Callback、WebSocket、Race或其他观察能力。
只要某个子执行器能读取单个task和target_ref、使用至少一种合格客户端并写自己的Result/Evidence,就可以作为单case执行后端,不要求固定agent名、工具名或调度API。
执行模式:
| 条件 | execution.mode |
|---|---|
| 存在满足单case协议的子Agent/Worker | subagents |
| 开始时没有合格子执行器 | sequential |
| 运行中并行能力失效 | degraded-sequential |
run.json.execution记录实际host、agent和按优先级发现的http_clients。并发上限为3,同时受宿主上限、依赖、资源声明和副作用约束。
HTTP客户端选择
HTTP执行按case选择实现,不要求整次运行固定一种客户端:
flowchart TD A[已编译单case task] --> B{结构化HTTP工具可准确表达并观测?} B -->|是| C[使用http或宿主等价工具] B -->|否| D{允许命令执行且已有curl?} D -->|是| E[使用curl] D -->|否| F{存在其他合格客户端?} F -->|是| G[使用宿主已有客户端] F -->|否| H[检查其他执行器或记录能力阻断] C --> I[记录实际客户端与可见性] E --> I G --> I
选择原则:
- 优先结构化HTTP工具。Pi中优先
@evalexp/pi-http提供的http,其他宿主使用等价结构化工具。 - 结构化工具不存在或不能表达当前请求时,回退到已有
curl;仍不满足时才考虑宿主已有的其他命令行客户端。 - 不为本SKILL安装客户端,不自动重试,也不把不支持的协议动作静默近似成普通请求。
- Header、Body、跳转、计时、TLS、代理、二进制、上传和下载必须以所选客户端真实能力为准。oracle依赖不可见事实时继续选择其他客户端;所有允许路径都不足时才是宿主能力阻断。
- 每个interaction记录实际客户端、
structured_tool|command_line模式和可见性边界。命令行客户端产生的响应、Header、write-out元数据、下载和临时文件只能写入当前case的evidence范围。
http不可用不等于case阻断,curl不可用也不等于case阻断。只有子执行器和主Agent的全部允许执行路径都无法完成必要动作或采集充分证据时,case才是inconclusive。
Pi首选适配与vulnerability-verifier
Pi是主要维护和使用的宿主。在Pi中仍要先检查当前会话实际暴露的子Agent能力,并确认vulnerability-verifier可执行;安装了pi-subagents或磁盘上存在agent文件都不能代替运行时发现。
Pi专用agent定义位于profile_sys/agents/vulnerability-verifier.md。它保持为薄执行器:
tools:
- read
- write
- http
systemPromptMode: replace
inheritProjectContext: false
inheritGlobalContext: false
inheritSkills: false
defaultContext: fresh
acceptanceRole: writer关键设计:
| 配置 | 目的 |
|---|---|
tools: read, write, http | 只保留Pi首选单case验证所需工具 |
不声明extensions | 继承主Agent已加载的@evalexp/pi-http扩展 |
inheritProjectContext=false | 不把项目说明注入执行器 |
inheritGlobalContext=false | 不继承全局上下文 |
inheritSkills=false | 不把完整SKILL或其他Skill交给执行器 |
defaultContext=fresh | 每个case使用独立上下文 |
acceptanceRole=writer | 明确它负责写task声明的Artifact |
这里不能把agent再写成一个SKILL。这个定义是Pi适配器,不是通用SKILL的运行前提。它的system prompt只保留角色协议:
- 一次执行一个task;
- 严格按task执行;
- 每次网络交互后写evidence;
- attempt结束后写result;
- 阻断为
inconclusive; - 不询问用户;
- 不写全局状态;
- 不调度下一层Agent。
http来自主profile中的@evalexp/pi-http。agent只在tools中选择http,不重复配置扩展。task能被read、write、http准确完成时优先委派给它;扩展不存在或当前flow超出该工具能力时,交给具备所需客户端的其他执行器,或由主Agent按同一task顺序执行。
当前http@0.1.1适合:
- 常规HTTP方法;
- 字符串Header映射;
- raw文本、JSON、URL编码表单和multipart请求体;
- 文件上传;
- 响应下载;
- 最终响应状态、URL、Header和body观察。
当前版本不能准确表达或观察:
| 需求 | 限制 |
|---|---|
| 重复请求Header | 请求Header输入是字符串映射 |
| 原始HTTP framing | 不暴露线级原始报文 |
| 畸形请求 | 普通HTTP客户端会规范化请求 |
| 重定向逐跳证据 | 只暴露最终响应 |
| 精确计时 | 工具未提供时不能估算 |
| WebSocket | 不属于当前HTTP工具能力 |
| 浏览器DOM | 不属于当前HTTP工具能力 |
| 独立回调通道 | 需要其他能力 |
| 竞态协调 | 需要专门并发控制 |
这些限制只决定能否使用当前Pi verifier,不定义整个SKILL的能力上限。例如逐跳重定向或重复Header可以尝试由主Agent使用curl准确执行;Browser、Callback或Race则需要宿主真实提供相应能力。
无论使用哪种客户端,大响应和辅助文件都必须位于当前case的evidence_prefix。TLS校验只能在用户已接受风险且task设置allow_insecure_tls=true时,通过http.insecure=true、curl -k或等价选项关闭。
调度
调度顺序优先考虑依赖和状态冲突:
- 只读、匿名、响应型case;
- 认证和权限边界case;
- 写入、上传、命令、SSRF、反序列化和跨步骤case。
这个顺序是队列组织方式,不改变报告原有case集合和最终展示顺序。
并发调度:
flowchart TD Q[Pending Tasks] --> S[检查依赖、resource claims和工具能力] S --> A[Executor 1] S --> B[Executor 2] S --> C[Executor 3] A --> X[Result/Evidence A] B --> Y[Result/Evidence B] C --> Z[Result/Evidence C] X --> P[主Agent验收] Y --> P Z --> P P --> Q
每个case同时只能有一个执行者。一次子Agent调用只传一个task路径,不传:
- 完整报告;
- SKILL;
run.json;- 其他case;
- 漏洞References。
Child结果验收
子执行器返回后,主Agent不能只接受自然语言摘要。
验收内容:
flowchart TD A[Child返回] --> B[解析result JSON] B --> C[检查case ID和路径] C --> D[读取全部evidence引用] D --> E[检查attempt数量] E --> F[检查interactions顺序] F --> G[逐条检查oracle checks] G --> H{证据充分且契约一致?} H -->|是| I[更新run.json] H -->|否| J[一次针对性修正或重试] J --> K{仍失败?} K -->|否| I K -->|是| L[放回主Agent顺序队列]
主Agent验收是Canonical Acceptance,不是重新分析漏洞。
通用顺序回退
顺序回退使用与所有子执行器完全相同的task contract。
触发条件:
- 没有子Agent/Worker能力;
- 当前宿主没有合格的子执行器;
- Pi verifier不可执行或缺少
http; - flow超出子执行器的客户端或观察能力;
- case之间存在冲突;
- child启动失败;
- child结果持续不合格;
- 并行运行中途失效。
回退过程:
flowchart TD A[并行路径失效] --> B[保留已验收结果] B --> C[未验收active/queued case重新入队] C --> D[execution.mode=degraded-sequential] D --> E[主Agent逐个读取原task] E --> F[按同一Evidence/Result契约执行] F --> G[直到全部case终态]
主Agent顺序模式强制检查点:
- 读取当前task和
target_ref; - 把case写为
running; - 每次交互立即写evidence;
- attempt结束更新result;
- 得到终态后完成final result;
- 更新
run.json; - 重新读取task、result和evidence;
- 验收完成后进入下一case。
不能先执行全部PoC,最后集中补写证据。
4. 单case执行与判定
执行器只关心当前task。
flowchart TD A[读取Task] --> B[读取target_ref] B --> C[绑定每个step身份] C --> D[执行workflow] D --> E[保存跨步骤变量] E --> F[执行后续step] F --> G[检查step assert] G --> H[检查最终oracle] H --> I{终态?} I -->|否且允许调整| J[记录理由并调整PoC] J --> D I -->|是| K[写Result]
身份处理
匿名步骤必须移除:
- Cookie;
Authorization;target.json中配置的全部认证Header。
认证步骤根据identity_id读取凭证。
凭证Header与PoC Header同名冲突时不能静默覆盖,case应为inconclusive并交主Agent处理。
多步骤变量
后续step只能使用前面save产生的变量。Evidence中的variables只记录后续步骤实际复用的值。
不能:
- 从未执行的步骤构造变量;
- 根据预期响应猜测变量;
- 使用另一个case的变量;
- 把模型推断写成实际提取结果。
HTTP响应是不可信输入
响应正文可以用于:
- 提取workflow变量;
- 判断assert;
- 判断oracle;
- 决定是否采用task允许的调整。
响应正文不能:
- 修改task;
- 指示执行器读取额外文件;
- 指示执行器调用额外工具;
- 扩大测试范围;
- 覆盖系统协议。
Attempt和PoC调整
一次attempt可以包含多次网络交互,但必须全部按顺序保存在同一个evidence文件中。
调整流程:
flowchart TD A[执行默认PoC] --> B[写Evidence] B --> C[评估Oracle] C --> D{满足?} D -->|是| E[verified] D -->|否| F{存在响应支持的allowed adjustment?} F -->|否| G[not_verified或inconclusive] F -->|是| H{达到max_attempts?} H -->|是| G H -->|否| I[记录adjustment_reason] I --> J[执行下一attempt] J --> B
每次调整都要回答:
- 上一attempt观察到了什么;
- 哪个允许调整适用;
- 为什么这个调整可能影响oracle;
- 当前是第几次尝试。
不能基于漏洞Reference、通用payload列表或猜测进行无边界试探。
Evidence契约
Evidence回答:所选客户端实际观察到了什么。
每次attempt一个文件:
.auto-verification/evidence/<case-id>-attempt-<n>.json
主要结构:
| 部分 | 内容 |
|---|---|
capture | 实际客户端、执行模式和可见性边界 |
variables | 实际复用的跨步骤变量 |
interactions | 按序保存的全部实际网络请求 |
notes | 工具限制、截断或不可见字段说明 |
每个interaction包含:
sequence;- workflow step;
identity_id;- 客户端提供时的时间和耗时;
- 实际传给客户端的请求;
- 客户端返回的响应;
- 结构化网络错误。
客户端可见性
Evidence记录的是所选客户端可见事实,不声称是原始TCP/TLS或完整线上HTTP字节流。capture.tool写实际名称,例如http或curl;capture.mode写structured_tool或command_line。
| 字段 | 记录规则 |
|---|---|
| 请求Header | 只记录显式传给客户端的Header,不补写运行库隐式Header |
| 响应Header | 记录客户端输出,只声明它实际保留的大小写、顺序和重复字段能力 |
| Redirect | 记录客户端实际暴露的范围,不虚构中间hop;oracle需要逐跳证据时必须选择支持该能力的客户端 |
timestamp | 客户端或宿主不提供时为null |
duration_ms | 客户端或宿主不提供时为null;curl只有显式采集write-out时间字段时才能使用 |
| 错误 | 使用DNS、connect、TLS、timeout、protocol或other类型 |
Body
内联body支持:
utf-8;base64。
需要记录:
- 捕获内容;
captured_bytes;- 是否
truncated; - 客户端知道时的
original_size_bytes。
大响应可以写文件,编码使用file,路径必须位于当前case的evidence prefix。
如果截断部分可能影响oracle,则当前attempt不能据此形成verified或not_verified。应改用允许的完整证据方式,或者判定为inconclusive。
完整信息策略
企业内部验证不对以下内容做额外脱敏:
- 认证Header;
- 请求body;
- 响应body;
- Result中的请求和观察;
- 最终报告中的测试凭证和验证细节。
target.json、evidence和报告因此可能包含内部敏感信息,运行目录权限应限制在当前用户。
Result契约
Result回答:Evidence对oracle意味着什么。
每个case一个文件:
.auto-verification/results/<case-id>.json
主要内容:
| 字段 | 作用 |
|---|---|
status | 三态终态 |
environment | 目标和实际身份信息 |
attempts | 每次目的、请求、调整理由、观察和evidence引用 |
oracle_checks | 每条oracle条件是否满足及对应证据 |
conclusion_basis | 为什么证据足够或不足 |
limitations | 平台、能力或证据限制 |
infrastructure_issue | 基础设施阻断 |
retry | 用户重试询问和决定 |
执行器没有可靠时钟时,started_at和completed_at写null,不能生成看似精确的时间。
三态判定
| 状态 | 判定标准 |
|---|---|
verified | 所有必要oracle满足,且证据足以证明报告声明的影响 |
not_verified | 已完成有效验证,但oracle未满足 |
inconclusive | 网络、凭证、依赖、前置状态或宿主能力使oracle无法评估 |
需要始终区分:
not_verified不等于漏洞不存在;inconclusive不等于not_verified;- 静态
confirmed不等于动态verified; - HTTP 200不等于漏洞已验证;
- 接口接受请求不等于高阶影响已经发生。
Attempt保留
| 终态 | 保留规则 |
|---|---|
verified | Result和最终报告只保留成功attempt |
not_verified | 保留全部有效attempt和调整理由 |
inconclusive | 保留已执行attempt、阻断和重试信息 |
保留策略服务于结论可读性:成功项展示最终可复现路径,未成功项展示完整探索边界,无法判定项展示真实阻断。
Evidence Level
Evidence level来自上游报告,执行阶段不重新选择漏洞影响。
| Level | 需要观察的事实 |
|---|---|
response | 响应状态、业务字段、Header或body直接满足判据 |
differential | 主探针与对照请求形成稳定差异 |
time | 工具真实提供且可重复的时间差 |
state | 后续查询观察到状态变化 |
effect | 文件、命令、渲染或其他真实效果 |
cross_identity | 不同身份之间形成因果和权限边界证据 |
cross_channel | 独立HTTP、DNS、消息或其他通道收到关联marker |
执行器必须达到task声明的证据等级,不能用更弱观察替代更强影响声明。
5. 汇总与报告
报告前先做集合完整性检查:
flowchart TD A[白盒报告ID集合] --> E[集合一致性] B[Task ID集合] --> E C[Result ID集合] --> E D[run.json.cases键集合] --> E E --> F{完全相等?} F -->|否| G[停止生成报告并修复状态] F -->|是| H[检查终态、attempt和evidence] H --> I[生成最终报告]
还要确认:
- 每个ID只有一个终态;
attempt_count与Result最终保留attempt一致;- Result引用的evidence存在且可解析;
result_counts可以从case状态重新计算;- 所有
inconclusive都有阻断原因; - 重试决定只由主Agent记录。
最终报告写在工作区根目录:
VULNERABILITY_VERIFICATION_REPORT.md
不能放在.auto-verification/中。
报告内容:
- 测试目标和身份;
- 白盒报告来源;
- 可达性结果;
- 三态统计;
- 全部漏洞汇总;
- 每个case的最终flow、请求、观察和oracle判断;
- evidence相对路径;
inconclusive阻断和重试决定;- 方法和结论边界。
动态结果与白盒静态状态分开呈现,不能相互覆盖。
完成条件
这个SKILL真正完成需要满足:
- 输入报告契约有效;
- source hash与当前运行一致;
- 可达性门禁已处理;
- 每个漏洞都有一个task;
- 每个case都有唯一终态;
- 没有遗留
pending或running; - Result和Evidence全部通过验收;
- ID集合完全一致;
- 最终报告已经生成;
run.json.status=completed;report_output指向根目录最终报告。
不能因为某项未验证成功而省略它,也不能把inconclusive排除在总数之外。
agents/openai.yaml
这个文件只负责界面层:
| 字段 | 内容 |
|---|---|
display_name | 自动漏洞验证 |
short_description | 按单case验证白盒漏洞并汇总生成报告 |
default_prompt | 根据白盒漏洞报告验证测试站点并生成验证报告 |
真正的workflow、策略和Agent协议分别位于SKILL.md、context-schema.md和vulnerability-verifier.md。
设计取舍
主Agent编译task,执行器不读报告
主Agent本来就负责:
- 校验完整报告;
- 解析身份需求;
- 选择variant;
- 汇总所有结果。
让它逐case编译task不会增加新的业务职责。编译完成后,执行阶段只需要读取task和Artifact,避免把完整报告反复注入每个child。
Task引用凭证,不复制凭证
凭证集中保存在target.json,task只保存target_ref和身份ID。
这样可以:
- 更新凭证而不重写全部task;
- 避免同一凭证在并发任务中重复出现;
- 保持身份绑定稳定。
Evidence和报告仍按企业内网策略保存实际认证信息。
子Agent保持薄
单case执行器只拥有当前case需要的工具和角色协议;Pi的首选实现是薄vulnerability-verifier。
如果把编译、调度、报告或漏洞知识加入agent,它就会与SKILL重复,并破坏单case执行器边界。
不给执行器漏洞Reference
References对漏洞发现有价值,但对执行已编译flow没有必要。执行器只需要知道:
- 发什么;
- 使用哪个身份;
- 保存什么变量;
- 如何判定;
- 失败后允许怎么调整。
子Agent只是加速层
主Agent拥有完整顺序执行能力,因此并行插件失败不会改变验证语义和最终完整性。
一个case一个task
这个粒度使:
- 所有权清晰;
- 写入范围明确;
- 恢复简单;
- Result可独立验收;
- 冲突容易声明;
- 主Agent和子Agent可以共用协议。
主Agent唯一写全局状态
牺牲并行写run.json的便利,换取:
- 无Lost Update;
- 计数一致;
- 恢复点唯一;
- 重试决定集中;
- 最终报告稳定。
Evidence只记录客户端可见事实
不要求客户端无法提供的原始报文、重定向hop或精确时间,可以避免为了满足schema而编造证据;但在另一现有客户端可以提供oracle所需事实时,应先切换客户端,而不是直接降级结论。
破坏性与脱敏策略由环境决定
当前企业测试内网有快速快照恢复能力,因此允许task中的破坏性PoC,并保留完整验证信息。真正需要约束的是执行范围,而不是用通用“低影响”规则覆盖本地授权模型。
维护风险
verification_handoff耦合
这个SKILL依赖上游白盒报告字段:
report.schema_version与verification_handoff.schema_version;identity_requirements;required_capabilities;- workflow;
- variants;
- oracle;
- evidence level;
- negative control;
allowed_adjustments;- 旧报告中的alternatives兼容输入。
这些字段变化需要同步修改:
- task compiler规则;
context-schema.md;- Result验收;
- 最终报告模板;
- 设计笔记。
应按Schema Migration处理,而不是只改文案。
通用宿主适配变化
调度协议不能绑定某个宿主的工具名或子Agent API。宿主升级时重新发现:
- 是否能创建隔离的单case执行器;
- 子执行器的最小文件与网络能力;
- 主Agent是否可以完整顺序回退;
- 结构化HTTP工具与命令行客户端的真实可用性;
- 并发上限、完成通知和运行引用。
只要task、result和evidence契约不变,宿主适配器可以替换;不能把Pi专有字段提升为通用协议必填项。
Pi pi-subagents API变化
Agent发现和调度必须以当前会话实际暴露的schema为准。
维护时优先检查:
- agent list;
- executable状态;
- fresh context;
- async或并发调用;
- run reference;
- wait或完成通知;
- diagnostics。
不要把某个版本的调用示例固化为永远不变的API。
@evalexp/pi-http能力变化
如果HTTP扩展新增或修改:
- Header输入模型;
- Redirect行为;
- 时间字段;
- 响应大小限制;
downloadPath;- 文件上传;
- TLS选项;
必须同步检查evidence契约和Pi verifier可调度case范围。该扩展是Pi首选结构化客户端,不是SKILL运行前提;升级或缺失都不能移除curl及其他已有客户端的通用回退路径。
Agent工具继承语义
Pi专用verifier依赖:
- 主profile加载
@evalexp/pi-http; - agent不声明
extensions; toolsallowlist选择已经注册的http工具。
Pi或pi-subagents升级后,需要重新执行实际agent discovery,不能只解析Markdown推断。其他宿主不需要实现这些Pi字段,只需满足单case任务和读写边界。
完整信息保存
当前策略不脱敏Evidence、Result和Report。维护时不能擅自加入通用脱敏逻辑,但应保持:
target.json尽可能使用0600;- 运行目录仅对当前用户可读;
- 最终报告的存放位置符合企业内部使用边界。
Snapshot恢复假设
Skill不设计cleanup,依赖外部快照恢复。
如果未来运行环境不再提供快照,需要重新设计:
- cleanup字段;
- 可逆性;
- 环境重置检查点;
- 并发隔离。
不能只删除“快照”文案而保留当前副作用模型。
Verified历史保留
当前verified只保留成功attempt。如果以后需要完整研究轨迹,应明确区分:
- 机器运行日志;
- Canonical Result;
- 最终报告展示。
不能简单改变attempt retention而不调整attempt_count和报告语义。
后续增强方向
后续增强优先考虑:
- 为
context-schema增加显式迁移规则; - 在出现真实重复错误后增加确定性JSON validator;
- 给不同执行能力定义清晰的executor capability描述;
- 为Callback、Browser、WebSocket和Race case定义独立但兼容的执行后端;
- 增加中断恢复的隔离前向测试;
- 增加子Agent失败后顺序回退的行为测试;
- 验证大响应下载、截断和oracle关系;
- 为资源声明建立更稳定的key约定。
不优先增加:
- 漏洞知识库;
- 漏洞类型Reference;
- 通用payload大全;
- 让执行器自行发现攻击路径。
这些属于上游漏洞挖掘或审计阶段。
修改时需要保持的核心约束
以后修改这个SKILL时,至少保持:
verification_handoff是唯一执行模型;- 不从漏洞描述重新设计攻击路径;
- 一个case一个task;
- variant由主Agent选择;
- task不复制凭证;
- task不包含漏洞Reference;
- 子执行器一次只执行一个case,并使用宿主等价的隔离上下文;
- 核心task、result和evidence契约不依赖Pi、固定agent名或固定HTTP工具;
- 结构化HTTP工具优先,不能表达时回退到已有
curl或等价命令行客户端; - 客户端不可用或能力不足时先检查其他执行器和主Agent路径,不能直接漏掉case;
- Pi verifier使用fresh context,不继承Project、Global或Skill上下文;
- Pi verifier工具只包含
read、write、http,且不重复声明extensions; - 最多3个无冲突的单case执行器;
- 主Agent是
run.json和最终报告唯一写入者; - 子Agent失败必须回退顺序模式;
- 子Agent失败不能成为漏洞结论;
- 主Agent回退使用同一个task contract;
- 每次网络交互立即写Evidence;
- 写入顺序为Evidence → Result →
run.json; - Evidence记录实际客户端和真实可见事实;
- 客户端不可见字段不能推断;
- 匿名步骤必须移除全部认证Header;
- 跨身份结论必须有真实不同身份和基线;
- PoC调整只能来自
allowed_adjustments和实际响应; - 达到
max_attempts必须停止; - 允许task内破坏性PoC;
- Evidence、Result和Report保留完整信息;
- 不引入task外动作或扩大执行范围;
- cleanup依赖外部快照,不由执行器设计;
- 所有case必须有唯一三态终态;
not_verified不能表述为漏洞不存在;- source hash变化不能复用旧结果;
- Final Report只消费已验收Result并引用Evidence。
快速定位
| 问题 | 先看 |
|---|---|
| 整体workflow | SKILL.md |
| 运行状态不一致 | references/context-schema.md中的run.json |
| 身份或凭证问题 | target.json契约 |
| Task编译错误 | references/context-schema.md中的tasks/<case-id>.json |
| Child读取了额外上下文 | 当前宿主的单case隔离配置;Pi看profile_sys/agents/vulnerability-verifier.md |
| HTTP客户端选择错误 | SKILL.md中的“宿主与HTTP客户端适配”及run.json.execution.http_clients |
Pi verifier没有http | Agent的tools与主profile扩展加载状态;不可用时走通用回退 |
| Child没有被发现 | 当前宿主实际worker discovery;Pi看pi-subagents的agent list与diagnostics |
| 并发状态冲突 | parallel_safe与resource_claims |
| 中断后是否可以重放 | side_effect与replay |
| Evidence缺字段 | 客户端可见性和capture.tool/mode规则 |
| 大响应被截断 | 所选客户端的下载能力和evidence body规则 |
| Oracle判断不充分 | Result中的oracle_checks与Evidence引用 |
| Child结果不可信 | 主AgentResult验收流程 |
| 子Agent失败后case丢失 | execution.mode与顺序回退队列 |
| 三态计数不一致 | run.json.cases与result_counts |
| 最终报告格式 | assets/VERIFICATION_REPORT_TEMPLATE.md |
| 报告遗漏case | 报告、task、result和run.json.cases的ID集合检查 |
| 输入报告发生变化 | source.sha256恢复门禁 |