扫描计划与结果协议¶
任务计划¶
创建扫描页与域名任务 API 使用以下四种用户模式:
- “全部”/
vulnerability_scan:端口发现、可选服务识别和必需漏洞检测; - “仅端口”/
port_only:只执行端口发现; - “服务识别”/
service_inventory:端口发现后执行必需的服务识别; - “仅 DNS”/
dns_records:只查询并归档 DNS 记录,只允许域名目标。
IP/CIDR 使用 POST /api/task/create;域名使用 POST /api/task/create-dns。域名选择前三种
模式时,服务端先保存一个 dns_records 父任务,再从其 A/AAAA 结果中筛选授权地址,按
IPv4/IPv6 和任务上限拆分为对应 profile 的子任务。“仅 DNS”不会创建子任务,也不接受
port。两条创建接口都会执行最终目标/profile 校验,不能依靠绕过前端提交非法组合。
两个创建接口都接受 dispatch_mode=automatic|selected|all。selected 必须同时提供非空
scanner_ids,另外两种模式不能附带 scanner_ids。域名接口按 DNS 父任务能力及显式解析器
能力校验所选节点,并为每台指定或符合“全部扫描器”的节点创建独立执行记录。
协议层还保留以下兼容 profile:
legacy:兼容旧扫描器,由节点本地配置决定是否识别服务;dns_ports:对显式 domain 查询 A/AAAA 后扫描解析地址的端口;dns_full:归档 DNS 记录,并扫描解析地址的端口。
未传入时默认 legacy。服务端将 profile 展开为版本 1 scan_plan 并保存,客户端不能传入
任意命令、二进制路径或 CLI 参数。任务领取响应包含完整计划。
漏洞扫描计划固定为:
{
"version": 1,
"profile": "vulnerability_scan",
"stages": [
{ "type": "port_discovery", "required": true },
{ "type": "service_detection", "required": false },
{ "type": "vulnerability_detection", "required": true }
]
}
服务识别是可选阶段:节点没有启用 zgrab2 或识别失败时仍可继续 Nuclei;漏洞检测是必需阶段, Nuclei 启动失败、非零退出、输出无效、超时或超过本地安全上限都会使执行失败。
创建 IP 漏洞扫描任务的请求示例:
{
"ip": "192.0.2.10",
"port": "80,443,8080,8443",
"level": 1,
"scan_profile": "vulnerability_scan",
"dispatch_mode": "automatic"
}
创建域名“全部”任务的请求示例:
{
"domains": ["api.example.com", "www.example.com"],
"port": "80,443,8080,8443",
"level": 1,
"scan_profile": "vulnerability_scan",
"dispatch_mode": "selected",
"scanner_ids": ["019c0000-0000-7000-8000-000000000001"]
}
创建“仅 DNS”任务时必须使用域名接口,且省略 port:
{
"domains": ["example.com"],
"level": 1,
"scan_profile": "dns_records",
"dispatch_mode": "automatic"
}
域名解析后扫描受运行时 dns_follow_up 策略控制。只有 enabled=true 且解析地址落入
authorized_cidrs、同时未命中 excluded_cidrs 时才会生成子任务。策略未启用时创建请求
返回冲突响应,不会退化为不受限扫描。
任务目标必须属于调用方已获授权的范围。服务端只负责调度和结果校验,不会代替组织完成扫描 授权审批。
能力调度¶
扫描器心跳的 scan_capabilities 包含 plan_versions、profiles 和 stages。服务端保存该
结构,并仅把非 legacy 任务分配给声明支持相应 profile 的扫描器。旧扫描器按仅支持
legacy 处理。
可领取漏洞任务的节点必须同时满足:
- 心跳
scan_capabilities.plan_versions包含1; scan_capabilities.profiles包含vulnerability_scan;scan_capabilities.stages包含vulnerability_detection;- 目标地址族当前就绪;
- 节点在线、启用且没有被其他执行占用。
扫描器只有在本地启用 Nuclei,并通过二进制和模板目录启动检查后才声明这些能力。指定扫描器 和全部扫描器模式仍逐个应用相同的能力条件。
类型化结果协议¶
结果协议版本 3 在版本 2 的 port 和 service observations 基础上增加
vulnerability observation。漏洞记录保存模板 ID、名称、严重级别、匹配位置、修复建议和
引用,不接收 Nuclei 的原始请求/响应,避免将凭据、Cookie 或响应正文写入结果库。每个请求
最多 500 条,并携带:
upload_id:一个任务缓存周期内保持稳定;batch_index:从 0 开始的批次序号;is_final:是否为最后一批。observed_at:观察产生时的 Unix epoch 毫秒时间,重试时保持不变。
漏洞 observation¶
{
"kind": "vulnerability",
"ip": "192.0.2.10",
"port": 443,
"protocol": "https",
"template_id": "cve-example",
"name": "Example vulnerability",
"severity": "high",
"matcher_name": "body",
"matched_at": "https://192.0.2.10:443/",
"description": "Structured template description",
"remediation": "Apply the vendor update.",
"tags": ["cve"],
"references": ["https://example.invalid/advisory"],
"classification": {},
"extracted_results": [],
"source": "nuclei",
"observed_at": 1788393600000
}
| 字段 | 约束 |
|---|---|
template_id |
必填,最多 256 字节 |
name |
必填,最多 512 字节 |
severity |
info、low、medium、high、critical 之一 |
matcher_name |
可选,最多 256 字节 |
matched_at |
必填,最多 4096 字节 |
description / remediation |
可选,各最多 8192 字节 |
tags |
最多 128 项,每项最多 128 字节 |
references |
最多 64 项,每项最多 2048 字节,且仅允许 HTTP(S) URL |
extracted_results |
最多 64 项,每项最多 2048 字节 |
单条 observation 的 data 序列化后不得超过 1 MiB。字符串中的 NUL 会被清理或拒绝。服务端
不接收 Nuclei 原始 request/response;这些字段也不会出现在扫描器缓存、PostgreSQL 或
Elasticsearch observation 中。
扫描器将任务、稳定上传身份、观察和已确认批次序号持久化到本地状态文件,重启后先恢复
未完成上传。服务端通过 (execution_id, upload_id, batch_index) 回执和 payload SHA-256
实现批次幂等,并要求最终批形成从 0 开始的连续序列。
扫描结果仅支持 elasticsearch.scan_storage=elasticsearch,PostgreSQL 保存控制面、上传回执
和派生分析快照;原始扫描结果及端点投影存储在 ES。PG 历史结果只通过独立 PG → ES 迁移命令
导入,不支持正常运行时的 PG 写入、查询或双写。参见 ES 存储说明。
漏洞 observation 使用端点、模板 ID、matcher 和匹配位置生成稳定键。同一执行内的上传重试 不会重复入库;不同执行仍分别保留,用于追踪扫描历史。
DNS task 使用同一批次/回执机制,但一个批次只能包含 dns observations,不能与端口、服务或漏洞 observation 混合。DNS query 的 observed_at、question、rcode、answer sections 和解析器元数据写入 Elasticsearch Data Stream;详情见 DNS_MAPPING.md。
PostgreSQL 模式下 scan_observation 是事实来源,ips、ports 是资产与端口索引;ES
权威模式下对应角色分别由月度 observation 索引和 execution/current endpoint 索引承担。
读取接口按
scan_execution_endpoint_latest 定义的顺序选择一次执行内的端点展示结果:服务观察优先于端口观察,
再按观察时间和记录 ID 排序。端点列表使用参数化查询,仅检查当前端点的观察记录,避免为分页
结果计算全表窗口;漏洞 observation 单独展示,不参与端点当前状态投影。任务详情、搜索、导出和分析查询均不依赖 scaninfo。协议版本
1 的结果会直接转换为 observation,并绑定到 execution 对应的合成批次。
任务详情响应¶
GET /api/task/{task_id} 返回所选执行的漏洞摘要和明细:
{
"success": true,
"data": {
"summary": {
"ip_count": 1,
"port_count": 2,
"result_count": 5,
"vulnerability_count": 1
},
"vulnerabilities": [
{
"template_id": "cve-example",
"name": "Example vulnerability",
"severity": "high",
"ip": "192.0.2.10",
"port": 443,
"protocol": "https",
"matched_at": "https://192.0.2.10:443/",
"observed_at": 1788393600000
}
]
}
}
vulnerability_count 是该执行的总数;vulnerabilities 最多返回最近 500 条。普通端点结果仍
通过既有分页参数读取,漏洞列表目前没有独立分页接口,也不会出现在任务导出文件中。
上线、兼容与回滚¶
跨仓库部署顺序必须为:
- 部署服务端并执行
source/database/init.sql中的幂等初始化,使约束接受新 profile、协议 3 和 vulnerability kind; - 部署前端,使任务创建和详情页识别新字段;
- 在少量扫描节点安装 Nuclei 和审核模板,启用
[nuclei]并重启; - 完成隔离测试目标验收后再逐步扩大节点范围。
服务端必须先于扫描器升级,因为旧服务端会拒绝协议版本 3。协议 1、2 客户端可以继续与新 服务端工作,因此服务端先升级不会中断旧扫描器。
新版扫描器对所有类型化结果都使用协议 3,而不只对漏洞结果使用。回滚服务端前,必须先停止 新版扫描器领取任务,在新服务端上排空协议 3 待上传批次,再回滚扫描器二进制并确认其恢复 协议 2,最后才能回滚服务端。扫描器会把任务和批次持久化到本地 SQLite;直接降级服务端或 删除节点状态可能造成持续上传失败或结果丢失。