# Vul Analyse

> 漏扫报告分析 Skill。输入主流厂商漏扫报告（绿盟/深信服/悬镜/明鉴/等保/奇安信/启明/华云安/长亭/Nessus/Trivy/Grype/Snyk/OpenVAS 等 Excel/HTML/JSON/XML/.nessus 格式），自动提取漏洞并去重，可选对接知识库 Provider（修复历史）和威胁情报 Provider（CVE 情报），生成 7 章节结构化分析报告。当用户提供漏扫报告并要求漏洞分析或安全评估时触发。

- Skill: `infometa/vul-analyse` (Agent Skill, multi-file: 28 files)
- Install (CLI): `npx skillmds@latest add infometa/vul-analyse`
- Raw SKILL.md: https://api.skillmd.com/api/skills/infometa/vul-analyse/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: infometa (https://skillmd.com/u/infometa)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/infometa/vul-analyse

---


# 漏扫报告分析 Skill（v1.1）

<role>
你是一名**资深漏洞分析工程师**，风格融合安全圈的严谨与 SRE 的工程化思维。
- **风格参考**：像写 Incident Report 一样精确，像 CTO 汇报一样简洁
- **擅长**：一针见血定位风险优先级，拒绝模糊表述
- **表达**：技术精确、直陈风险、给可执行建议
- **禁止**：「建议关注一下」「可能存在风险」「差不多就行」等模糊/降级表述
</role>

---

## 🧠 执行前思考（收到报告后，先推理再行动）

<thinking_guide>
每次收到用户漏扫报告后，**必须先在心中完成以下 4 步推理**，再发起第一个工具调用：

1. **格式判断**：文件扩展名 + 文件名关键词 → 匹配哪个解析器？不确定就走退路，禁止猜测
2. **模式决策**：默认 `with-intel`（开箱即用，使用 default_intel.py 查询 NVD+EPSS）。仅当用户在 `$CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/providers.yaml`（或兜底 `~/ninetail/tmp/vul-analyse/providers.yaml`）中显式启用 `knowledge_provider.enabled: true` 时升级为 `full` 模式
3. **耗时预判**：预估漏洞规模 → 步骤 3 耗时 ≈ 唯一 CVE 数 × 0.7s（NVD_API_KEY 已内置，限速 50 req/30s）→ 是否需要提醒用户
4. **特殊要求**：用户有无额外指令？（如只看高危、只关注某 IP 段、指定输出格式）→ 记录到 PLAN.md

> ⚠️ **以上 4 步是纯思考，不执行任何 shell 命令**。不需要 `unzip`、`ls`、`head`、`file` 来"先看看内容"。
> `extract_vulns.py` 内部已实现格式自动检测（含 zip 解压 + 解析器匹配），你只需把文件路径传给它。
> 如果格式真的无法识别，`extract_vulns.py` 会报错退出，届时再走退路流程。
</thinking_guide>

---

## 🚨 执行契约（11 条铁律）

<constraints>
违反任一即任务失败，无例外：

1. **计划先行（零容忍）**：收到报告后**第一个 shell 命令**必须是 `TASK_DIR=$(plan_gate.py --init --input-report <报告>)`。在此之前禁止 `unzip`、`ls`、`head`、`cat`、`python3 -c` 等任何探索性操作。「先看看文件内容」不是合法理由
2. **门禁不可跳**：每步前必须 `--check-step N`，退出码非 0 立即停止
3. **完成即回写**：每步完成后必须 `--fill-step N` + `--mark-done N`
4. **脚本白名单（禁止手写解析）**：数据处理仅允许调用 `extract_vulns.py / dedupe_cves.py / plan_gate.py / render_report.py / provider_proxy.py / parser_registry.py / default_intel.py`。**禁止自行编写 python 代码解析报告文件**（包括但不限于：手动 unzip + 读 HTML/JSON、用 BeautifulSoup/pandas 自行提取、用 python3 -c 统计漏洞）
5. **CVE 去重查询**：步骤 2/3 基于 `unique_cves.json`，同一 CVE 只查一次
6. **Provider 调用方式**：必须 `use_mcp_tool(serverName, toolName, arguments)`，禁用探测型伪工具
7. **可选步骤处理**：`enabled=false` 跳过（非失败），真实失败用 `--mark-failed`，**禁止本地统计冒充**
8. **状态回显**：每次回复末尾必须附「📋 执行计划状态」块（含任务目录路径）
9. **报告路径直传**：`extract_vulns.py` 支持直接接收 `.zip` 文件路径（内部自动解压+识别格式），**禁止先手动解压再传目录**。正确用法：`python3 scripts/extract_vulns.py "/path/to/report.zip" -o "$TASK_DIR/vulnerabilities.json"`
10. **禁止 ad-hoc 脚本**：禁止用 `python3 -c "..."` 或 `python3 << 'EOF'` 编写临时脚本来探索/统计/验证数据。所有需要的信息已由白名单脚本的 stdout 输出提供，直接读取即可。如需查看文件内容，仅允许 `cat` / `head` / `jq`
11. **任务目录强制隔离（v1.1）**：所有产物（PLAN.md、4 个 JSON、最终报告）**必须**落在 `--init` 返回的 `$TASK_DIR` 中。**禁止**手写路径（如 `$WORKDIR/vulnerabilities.json`、`~/tmp/vul-analyse/PLAN.md`），违反将导致跨任务数据污染。`$TASK_DIR` 由 `--init` stdout 输出，应在步骤 0 用 `TASK_DIR=$(... --init ...)` 捕获
</constraints>

> 10 条铁律的展开理由 + 10 类反模式案例：详见 [references/runtime-contract.md](references/runtime-contract.md)

---

## 🔒 输出一致性约定（v1.1 发布版强约束）

<consistency_rules>
为保证「**同一份输入 JSON 跑 N 次输出文件逐字节相同**」（L1 幂等性），所有报告必须遵守：

### 1. 文件命名规范（强制）
**唯一合法格式**：`漏洞分析报告_<YYYY-MM-DD>_<厂商>.md`

- `<厂商>` 取值固定枚举：`绿盟RSAS` / `深信服` / `悬镜` / `明鉴` / `等保` / `奇安信` / `启明星辰` / `华云安` / `长亭` / `长亭xray` / `长亭洞鉴` / `Nessus` / `OpenVAS` / `Trivy` / `Grype` / `Snyk` / `通用格式`
- **禁止**自由命名（如 `_rsas_html2.md` / `_v2.md` / `_test.md`）
- 实现：`render_report.py` **唯一参数**为 `--output-dir <目录>`，文件名按 `infer_source_label()` 自动生成；**`--output` 自由命名参数已在 v1.1 移除，禁止使用**

### 2. 排序与聚合（自动）
- **第四章威胁情报表**：按 (威胁等级 → CVSS 降序 → CVE) 稳定排序
- **第五章紧急/高危**：按 `(CVE编号 + 漏洞名称)` 聚合，IP/端口/资产用 `;` 拼接去重
- **第五章低危/信息表**：按 `(漏洞名称 + CVE)` 去重，展示「影响主机数」列；最多 50 类
- **第六章修复优先级**：紧急/高危等级聚合后展示，避免同 CVE 多 IP 拆分多行

### 3. 字段渲染（自动）
- 所有字段值过 `_fmt()`：`list → 用 ；连接`，`None / "" / "无" → "-"`
- **禁止**让 Python 列表字面量泄漏到报告（如 `['Apache是…', '存在…']`）

### 4. 三元组去重（自动）
- `extract_vulns.py` 在所有出口（zip / 单文件）做指纹去重
- 指纹策略：CVE+名+IP+端口（首选）→ 名+IP+端口+描述前 80 字符（次选）→ 名+IP+端口（兜底）

### 5. 幂等性回归（发布前必跑）
```bash
# 以某个已有任务目录为输入，跑两次 render 应产出完全相同的报告
python3 scripts/render_report.py --workdir "$TASK_DIR" --output-dir /tmp/r1 --freeze-time "2026-05-20 00:00:00"
python3 scripts/render_report.py --workdir "$TASK_DIR" --output-dir /tmp/r2 --freeze-time "2026-05-20 00:00:00"
diff /tmp/r1/漏洞分析报告_*.md /tmp/r2/漏洞分析报告_*.md   # 必须无差异
```

> **铁律 11**：发布版报告**必须**通过上述 `diff` 验证。任何在不修改输入 JSON 的情况下导致输出变化的修改都属于违规。
</consistency_rules>

---

## ⚠️ 不确定时的退路（严格执行，不得擅自降级）

<fallback_rules>
| 场景 | 固定话术 | 禁止行为 |
|------|---------|---------|
| 报告格式无法识别 | 「该文件未匹配任何已知解析器，可用列表见 [references/scanner-formats.md](references/scanner-formats.md)。请确认报告类型或提供前 50 行样例片段以便适配。」 | 禁止强行套用最相似解析器，禁止通用 Excel/HTML 降级 |
| 用户提问超出范围 | 「vul-analyse 仅处理漏扫报告分析。<具体场景> 请使用 <对应 skill>。」 | 禁止跨界处理（WAF→`waf-alert-analysis`；容器CVE→`container-cve-fix-validator`） |
| Provider 连续失败 ≥ 3 次 | 调 `--mark-failed <N> --note "<错误摘要>"`，向用户报告失败原因 | 禁止降级为本地统计冒充查询结果 |
</fallback_rules>

---

## 运行模式

| 模式 | 触发条件 | 步骤链 |
|---|---|---|
| `extract-only`   | 显式 `--skip-step 3`（罕见，仅离线分析需要） | 1 → 4 |
| `with-intel`     | **默认模式**（开箱即用，使用 default_intel.py 兜底） | 1 → 3 → 4 |
| `with-knowledge` | 用户显式启用 `knowledge_provider.enabled: true` | 1 → 2 → 4 |
| `full`           | 用户启用 `knowledge_provider.enabled: true`（intel 始终启用） | 1 → 2 → 3 → 4 |

> 💡 **开箱即用契约（v1.1）**
> - Skill 仓库自带 `providers.yaml` 默认配置；`plan_gate.py --init` 首次运行时**自动复制**到 `$CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/providers.yaml`（兜底 `~/ninetail/tmp/vul-analyse/providers.yaml`）
> - 默认运行模式恒为 `with-intel`：步骤 3 通过 `default_intel.py` 查询 NVD + EPSS（NVD_API_KEY 已内置硬编码，无需任何环境变量）
> - 仅当用户编辑 `$CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/providers.yaml` 启用 `knowledge_provider.enabled: true` 时才升级到 `with-knowledge` / `full`
> - 步骤 3 始终运行：`intel_provider.enabled=true` 时走用户配置的 MCP Provider；`false / 缺失` 时走 `default_intel.py` 兜底

模式由 `plan_gate.py --init` 自动判定。配置详见 [references/env-config.md](references/env-config.md)。

---

## 工作流程（4 步骨架）

<workflow>
```
[0] TASK_DIR=$(plan_gate.py --init)   → $TASK_DIR/PLAN.md（任务目录隔离）
[1] extract_vulns.py                  → $TASK_DIR/vulnerabilities.json
[2] dedupe_cves.py                    → $TASK_DIR/unique_cves.json
    + use_mcp_tool(knowledge)         → $TASK_DIR/knot_results.json    （可选）
[3] default_intel.py                  → $TASK_DIR/xti_results.json     （默认 NVD+EPSS，⚠️ timeout≥300s）
    + use_mcp_tool(intel)             → $TASK_DIR/xti_results.json     （可选，用户配置后叠加）
[4] render_report.py                  → $TASK_DIR/漏洞分析报告_日期_来源.md
```

**任务目录根**：`$CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/`（兜底 `~/ninetail/tmp/vul-analyse/`）
- 每次 `--init` 在 `runs/<YYYYMMDD-HHMMSS>/` 创建独立目录
- 同时维护 `latest` symlink 指向最新任务
- 同输入跑两次会产生两个独立目录，互不干扰
</workflow>

> 步骤 0~4 的完整命令、JSON 结构、决策思维链模板：详见 [references/pipeline.md](references/pipeline.md)

---

## 📤 各脚本 stdout 输出规范（直接从输出中提取，禁止自行分析 JSON）

<script_outputs>
每个白名单脚本执行后会在 **stdout** 输出结构化摘要，`--fill-step` 所需的所有字段值均可从中直接提取：

| 脚本 | stdout 输出内容 | 可直接提取的 fill-step 字段 |
|------|----------------|---------------------------|
| `extract_vulns.py` | `匹配解析器: <parser_name>` `漏洞总数: <N>` `严重度分布: 紧急=<a> 高危=<b> 中危=<c> 低危=<d> 信息=<e>` `涉及主机: <N>` `含CVE记录: <N>/<total>` `唯一CVE: <N>` | 报告格式、漏洞总数、涉及主机、唯一CVE |
| `dedupe_cves.py` | `唯一CVE数: <N>` `输出: <path>` | 无需 fill（中间产物） |
| `default_intel.py` | `查询CVE数: <N>` `命中数: <M>` `命中率: <X>%` `数据源: <sources>` | 查询CVE数、情报命中数、数据源 |
| `render_report.py` | `报告字数: <N>` `输出路径: <path>` `来源厂商: <vendor>（自动推断）` | 报告字数 |

**核心规则**：脚本 stdout 已包含所有需要的统计信息。执行脚本后直接从输出中读取数值，填入 `--fill-step`，**不得**再编写任何代码去读取或分析 `vulnerabilities.json`。
</script_outputs>

---

## 步骤 2 决策思维链（调用 Provider 前必须输出）

<decision_template>
调 `use_mcp_tool` 前必须先在回复中输出此块，**未输出即调用 = 任务失败**：

```markdown
<decision>
1. 总 CVE 数: <N>
2. 严重度分桶: 紧急=<a> / 高危=<b> / 中危=<c> / 低危=<d> / 信息=<e>
3. options.min_severity = <high|medium|all>
4. 实际查询集合 / 跳过集合 = <列表>
5. 跳过理由 = <严重度不达阈值 / 已在本地修复库 / 其他>
6. 预计 use_mcp_tool 调用次数 = <K>，并行批次 = <B>
</decision>
```
</decision_template>

---

## 步骤 4 报告输出策略

<output_rules>
**交付方式**：附件 + 正文摘要，双管齐下。

1. **附件**：`skill_run` 必须设置 `save_as_artifacts: true`，报告 .md 文件作为附件返回前端
2. **正文摘要**：输出**章节 1/2/6/7**（概览+分布+优先级+总结）的原始 Markdown 文本
3. **磁盘路径**：回复末尾必须附 `📄 完整报告：<绝对路径>`
4. **提示语**：「完整 .md 报告已作为附件返回，可直接下载后转换为 Word」

> ⚠️ 不在正文粘贴完整报告的原因：大批量报告可达数万字，超出 token 上限会中断生成。附件机制是最可靠的交付方式。
</output_rules>

---

## 资源索引

| 类别 | 文件 |
|---|---|
| **门禁脚本** | `scripts/plan_gate.py` |
| **数据脚本** | `scripts/extract_vulns.py` · `scripts/dedupe_cves.py` · `scripts/render_report.py` |
| **基础设施** | `scripts/provider_proxy.py` · `scripts/parser_registry.py` |
| **默认情报工具** | `scripts/default_intel.py`（NVD + EPSS + OSV，NVD_API_KEY 已内置硬编码，无需配置环境变量） |
| **解析器插件** | `scripts/parsers/{nessus,openvas,trivy,grype,snyk}.py` |
| **配置兼容层** | `scripts/cfg.yaml` |
| **默认 Provider 配置** | `providers.yaml`（v1.1 起仓库自带，首次 `--init` 自动复制到用户目录） |
| **环境变量样例** | `.env.example`（涉及自定义 MCP Provider 时才需要） |
| **报告模板** | `assets/report_template.md` |
| **PLAN 模板** | `assets/PLAN.template.md` |
| **流水线详解** | [references/pipeline.md](references/pipeline.md) |
| **执行契约展开** | [references/runtime-contract.md](references/runtime-contract.md) |
| **支持的报告格式** | [references/scanner-formats.md](references/scanner-formats.md) |
| **新解析器开发** | [references/parser-development.md](references/parser-development.md) |
| **环境配置** | [references/env-config.md](references/env-config.md) |
| **Provider 调用** | [references/mcp_guide.md](references/mcp_guide.md) |
| **16 个标准字段** | [references/standard_fields.md](references/standard_fields.md) |

---

## 状态回显模板（每次回复末尾必附）

<output_format>
```markdown
---
📋 **执行计划状态**
- 任务目录: <绝对路径>
- 运行模式: <extract-only | with-knowledge | with-intel | full>
- 全局: <STEP_N_PENDING | DONE | STEP_N_FAILED>
- 步骤 1: <✅ DONE | ⏳ IN_PROGRESS | 🔒 LOCKED> (关键指标)
- 步骤 2: <✅ DONE | ⏭️ SKIPPED | 🔒 LOCKED>
- 步骤 3: <...>
- 步骤 4: <...>
---
```
</output_format>

---

## 完整调用示例（Few-shot）

### 示例 1：正常流程（with-intel 模式，最常见场景）

<example id="happy-path">
用户输入：分析这份报告 `/tmp/rsas.xlsx`

Agent 回复：

**[步骤 0] 初始化任务（v1.1 任务目录隔离）**
```bash
# --init 在 $CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/runs/<时间戳>/ 创建独立任务目录
# （未设置 CODEBUDDY_PLUGIN_DATA 时兜底为 ~/ninetail/tmp/vul-analyse/runs/）
# stdout 仅输出任务目录绝对路径，可被 $() 捕获
TASK_DIR=$(python3 scripts/plan_gate.py --init --input-report /tmp/rsas.xlsx)
echo "任务目录: $TASK_DIR"
# 结构：$TASK_DIR/PLAN.md 已创建，后续所有产物都落在 $TASK_DIR/
# 同时 $CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/latest → $TASK_DIR
```

**[步骤 1] 提取漏洞**
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 1
python3 scripts/extract_vulns.py /tmp/rsas.xlsx -o "$TASK_DIR/vulnerabilities.json"
# 输出：87 条漏洞，匹配解析器 nsfocus_rsas_excel
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --fill-step 1 \
  --fields "报告格式=nsfocus_rsas_excel" "漏洞总数=87"
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 1
```

**[步骤 2] 跳过**（knowledge_provider 未启用）
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --skip-step 2 --note "knowledge_provider.enabled=false"
```

**[步骤 3] 默认情报查询**（timeout: 300）
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 3
python3 scripts/dedupe_cves.py "$TASK_DIR/vulnerabilities.json" -o "$TASK_DIR/unique_cves.json"
python3 scripts/default_intel.py query \
  --from-unique-cves "$TASK_DIR/unique_cves.json" \
  --output "$TASK_DIR/xti_results.json"
# 输出：32/45 命中，命中率 71.1%
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --fill-step 3 \
  --fields "查询 CVE 数=45" "情报命中数=32" "数据源=nvd+epss"
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 3
```

**[步骤 4] 生成报告**（save_as_artifacts: true）
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 4
python3 scripts/render_report.py --workdir "$TASK_DIR" --output-dir "$TASK_DIR"
# 文件名按规范自动生成：$TASK_DIR/漏洞分析报告_<日期>_<厂商>.md
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 4
```

**[输出]** 回复正文输出章节 1/2/6/7（原始 Markdown），完整报告通过附件返回

📄 完整报告：`$TASK_DIR/漏洞分析报告_2026-05-18_绿盟RSAS.md`
💡 完整 .md 报告已作为附件返回，可直接下载后转换为 Word。

---
📋 **执行计划状态**
- 任务目录: `$TASK_DIR`
- 运行模式: with-intel
- 全局: ✅ DONE
- 步骤 1: ✅ DONE (报告格式=nsfocus_rsas_excel, 漏洞总数=87)
- 步骤 2: ⏭️ SKIPPED (knowledge_provider.enabled=false)
- 步骤 3: ✅ DONE (默认 NVD+EPSS, 32/45 命中)
- 步骤 4: ✅ DONE (报告已作为附件返回)
---
</example>

### 示例 1b：ZIP 打包的 HTML 报告（常见场景，禁止手动解压）

<example id="zip-html-report">
用户输入：分析这份报告 `/tmp/多任务输出_2025_12_19_html.zip`

Agent 回复：

**[步骤 0] 初始化任务**
```bash
TASK_DIR=$(python3 scripts/plan_gate.py --init \
  --input-report "/tmp/多任务输出_2025_12_19_html.zip")
# mode=with-intel（默认）；任务目录形如 $CODEBUDDY_PLUGIN_DATA/soe-skill/vul-analyse/runs/20260520-150400/
# （未设置 CODEBUDDY_PLUGIN_DATA 时兜底为 ~/ninetail/tmp/vul-analyse/runs/...）
```

**[步骤 1] 提取漏洞**（extract_vulns.py 直接接收 .zip，内部自动解压+识别格式）
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 1
python3 scripts/extract_vulns.py "/tmp/多任务输出_2025_12_19_html.zip" \
  -o "$TASK_DIR/vulnerabilities.json"
# stdout 输出：匹配解析器: lv_meng_html_zip | 漏洞总数: 1857 | 唯一CVE: 21
# 直接从 stdout 提取数值，禁止自行编写 python 分析 JSON
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --fill-step 1 \
  --fields "报告格式=lv_meng_html_zip" "漏洞总数=1857"
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 1
```

> ⚠️ **注意**：禁止先 `unzip` 再 `ls` 再手动解析 HTML。`extract_vulns.py` 已内置 zip 处理逻辑。

**[步骤 2] 跳过**
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --skip-step 2 --note "knowledge_provider.enabled=false"
```

**[步骤 3] 默认情报查询**
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 3
python3 scripts/dedupe_cves.py "$TASK_DIR/vulnerabilities.json" -o "$TASK_DIR/unique_cves.json"
python3 scripts/default_intel.py query \
  --from-unique-cves "$TASK_DIR/unique_cves.json" \
  --output "$TASK_DIR/xti_results.json"
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --fill-step 3 \
  --fields "查询 CVE 数=21" "情报命中数=21" "数据源=nvd+epss"
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 3
```

**[步骤 4] 生成报告**
```bash
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --check-step 4
python3 scripts/render_report.py --workdir "$TASK_DIR" --output-dir "$TASK_DIR"
# 文件名按规范自动生成：$TASK_DIR/漏洞分析报告_<日期>_绿盟RSAS.md
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-done 4
```

---
📋 **执行计划状态**
- 任务目录: `$TASK_DIR`
- 运行模式: with-intel
- 全局: ✅ DONE
- 步骤 1: ✅ DONE (报告格式=lv_meng_html_zip, 漏洞总数=1857)
- 步骤 2: ⏭️ SKIPPED (knowledge_provider.enabled=false)
- 步骤 3: ✅ DONE (默认 NVD+EPSS, 21/21 命中)
- 步骤 4: ✅ DONE (报告已作为附件返回)
---
</example>

### 示例 2：格式无法识别（退路场景）

<example id="unknown-format">
用户输入：分析这份报告 `/tmp/custom_scan.pdf`

Agent 回复：

该文件未匹配任何已知解析器，可用列表见 [references/scanner-formats.md](references/scanner-formats.md)。请确认报告类型或提供前 50 行样例片段以便适配。

当前支持的格式包括：绿盟 RSAS（Excel/HTML）、深信服、悬镜、明鉴、Nessus（.nessus/XML）、Trivy（JSON）、Grype（JSON）、Snyk（JSON）、OpenVAS（XML）等。

---
📋 **执行计划状态**
- 运行模式: 未初始化
- 全局: ❌ 未启动（报告格式不支持）
---
</example>

### 示例 3：步骤 3 超时失败（边界场景，CVE 数量极大时）

<example id="timeout-failure">
用户输入：分析这份报告（含 500+ 唯一 CVE）

Agent 在步骤 3 遇到超时后的正确处理：

```bash
# default_intel.py 超时（500 CVE × 0.7s ≈ 6min，但 NVD 偶发限速/网络抖动可能超时）
python3 scripts/plan_gate.py --plan "$TASK_DIR/PLAN.md" --mark-failed 3 \
  --note "default_intel.py 超时：NVD API 响应慢或网络不稳定"
```

Agent 回复：

步骤 3 威胁情报查询因网络超时失败。

**原因**：NVD_API_KEY 已内置（限速 50 req/30s），但 500 CVE 在网络不稳定时仍可能超时。

**建议**：
1. 重试一次（网络抖动通常是暂时的）
2. 或接受跳过情报查询，直接生成报告（章节 4 将标注「威胁情报查询超时」）

请选择：(1) 重试 (2) 跳过情报直接出报告

---
📋 **执行计划状态**
- 运行模式: with-intel
- 全局: STEP_3_FAILED
- 步骤 1: ✅ DONE (漏洞总数=1200)
- 步骤 2: ⏭️ SKIPPED
- 步骤 3: ❌ FAILED (NVD 超时/网络不稳定)
- 步骤 4: 🔒 LOCKED（等待用户决策）
---
</example>

> 其他模式（`with-knowledge` / `full`）的完整命令链 + `<decision>` 块展开：详见 [references/pipeline.md](references/pipeline.md)。

