对 CNVD(国家信息安全漏洞共享平台) 网站页面与接口的 Go 封装库,支持漏洞列表、漏洞详情、厂商补丁三类页面的抓取与解析,内置代理 IP 轮换、重试、超时、去重,以及加速乐(JSL)三层解密与图片验证码挑战的自动处理。
- 漏洞列表
/flaw/list——RequestVulListByOffset+ParseVulList,解析当前页码、总页数、总记录数与条目 - 漏洞详情
/flaw/show/CNVD-xxx——RequestVulDetailByID/RequestVulDetailByURL+ParseVulDetail,解析 CNVD/CVE/危害级别/影响产品/描述/参考链接/补丁/附件等,时间字段同时提供字符串与*time.Time - 厂商补丁
/patchInfo/show/:id——RequestVulPatchByID/RequestVulPatchByURL+ParseVulPatch(含RequestVulPatchByIDWithConfig/RequestVulPatchByURLWithConfig变体以透传验证码识别器) - 列表检索
/flaw/list?keyword=...——RequestVulListByQuery/RequestVulListByQueryWithConfig+VulListWithQuery,按关键词/日期范围/厂商/危害级别/类别定向检索 - 单条抓取
FetchVulDetail(cnvd)—— 不落盘,返回结构化结果 - 主流程
VulList(ctx, proxyProvider, config)—— 翻页抓取 + 逐条详情 + JSONL 落盘,按总页数停止、按 CNVD 去重 - 反爬穿透 —— 自研加速乐三层解密客户端 + 可插拔验证码识别器(
CaptchaSolver),自动通过 CNVD 图片验证码挑战
go get github.com/scagogogo/cnvd-skills库已移除原先对私有仓库
github.com/JSREP/go-jsl-sdk的依赖,改为自研加速乐客户端(见cnvd_skills/cnvd_jsl_client.go),所有依赖均为公开模块,无需配置GOPRIVATE。
package main
import (
"context"
"fmt"
"github.com/scagogogo/cnvd-skills/cnvd_skills"
)
func main() {
ctx := context.Background()
err := cnvd_skills.NewCnvdSkills().VulList(
ctx,
cnvd_skills.FixedProxyProvider(""), // 空串=直连;或填 http://ip:port
cnvd_skills.DefaultConfig(),
)
if err != nil {
fmt.Println("抓取出错: " + err.Error())
}
}cfg := &cnvd_skills.Config{
MaxRetry: 3,
RequestTimeoutSeconds: 30,
CaptchaSolver: cnvd_skills.CommandCaptchaSolver{
Command: "python3",
Args: []string{"scripts/ddddocr_solver.py"},
},
}
detail, err := cnvd_skills.NewCnvdSkills().FetchVulDetailWithConfig(
context.Background(),
"CNVD-2021-67823",
cnvd_skills.FixedProxyProvider(""),
cfg,
)
if err == nil {
fmt.Println(detail.CNVD, detail.CVE, detail.HazardLevel.Level)
}q := cnvd_skills.VulListQuery{
Keyword: "XStream",
StartDate: "2024-01-01",
Endate: "2024-12-31",
}
list, err := cnvd_skills.NewCnvdSkills().RequestVulListByQueryWithConfig(
context.Background(),
q,
0,
cnvd_skills.FixedProxyProvider(""),
cfg, // 含 CaptchaSolver
)说明:VulListQuery 字段对应 CNVD 真实表单字段(keyword/startDate/endDate/categoryId/manufacturerId/serverity/referenceScope 等),零值不拼入查询。Endate 字段对应表单 endDate。全量定向抓取用 VulListWithQuery(ctx, q, proxyProvider, config),按 query 过滤翻页并逐条详情落盘。
CNVD 由加速乐(JSL)保护,访问需经过三层加密解密,且三层通过后对部分 IP 会再触发图片验证码挑战(创宇盾 captcha)。本库自动完成:
- 三层解密:第一层
document.cookie=XXX混淆 JS(goja 求值 + 兼容正则提取 cookie);第二层go({...})参数 + md5/sha1/sha256 暴力匹配算出__jsl_clearance_s;第三层带 cookie GET 目标页。 - 验证码挑战:检测到验证码页(
本站开启了验证码保护//cdn-cgi/js/captcha.js)后,自动 GET 取图(base64 PNG + sec token)→ 调用CaptchaSolver识别 → POST 提交答案 → 放行后重新 GET 拿真实页面。识别失败自动换图重试(最多 6 次)。
验证码识别("图→答案")是库的责任边界外,故抽成 CaptchaSolver 接口由调用方注入。内置实现:
| 实现 | 说明 |
|---|---|
NoopCaptchaSolver |
永不识别,遇验证码直接返回 ErrCaptchaRequired |
StaticCaptchaSolver |
返回固定答案/错误,仅供单测 |
InteractiveCaptchaSolver |
把图写到临时目录 + 轮询 CNVD_CAPTCHA_ANSWER 环境变量读答案(人工/外部脚本配合) |
CommandCaptchaSolver |
调外部命令识别:stdin 传 base64 PNG,stdout 读答案 |
CommandCaptchaSolver 配合 ddddocr 可全自动通过 CNVD 验证码。仓库自带 scripts/ddddocr_solver.py:从 stdin 读 base64 PNG,用 ddddocr 识别后输出答案。安装:
pip3 install ddddocr # 受 PEP668 限制的系统加 --break-system-packages未配置 CaptchaSolver 时遇验证码返回 ErrCaptchaRequired,调用方可用 errors.Is(err, cnvd_skills.ErrCaptchaRequired) 判断。
加速乐逆向已剥离为独立模块 go-jsl(github.com/scagogogo/go-jsl,package jsl),可脱离 CNVD 业务直接访问任意被加速乐保护的站点,作为通用加速乐绕过库独立 go get:
import "github.com/scagogogo/go-jsl"
client := jsl.NewJslClient("", 30, jsl.CommandCaptchaSolver{
Command: "python3",
Args: []string{"scripts/ddddocr_solver.py"},
})
html, err := client.Get(context.Background(), "https://www.cnvd.org.cn/flaw/show/CNVD-2021-67823")本库通过 go.mod require + 本地 replace ./gojsl 依赖该模块。CnvdSkills 持有一个默认 jsl.JslClient 实例(直连、不限时、不配识别器),可通过 JslClient() 获取;带 Config 的请求在 requestWithRetry 内按请求派生独立客户端,并发安全。验证码识别器配置类型为 jsl.CaptchaSolver,内置实现见 go-jsl 包。
Config 字段(DefaultConfig() 提供默认值):
| 字段 | 默认 | 说明 |
|---|---|---|
| OutputPath | data/test.jsonl |
抓取结果输出路径 |
| NumPerPage | 10 | 每页条数 |
| ListPageIntervalSeconds | 3 | 翻页间隔(秒) |
| DetailIntervalSeconds | 3 | 详情请求间隔(秒) |
| ProxyRetryIntervalSeconds | 3 | 代理失效重试间隔(秒) |
| MaxRetry | 3 | 单次请求最大重试次数 |
| RequestTimeoutSeconds | 30 | 单次请求超时(秒,0=不限) |
| EnableDedup | true | 是否按 CNVD-ID 去重输出 |
| Jitter | 0.3 | 翻页/详情/代理重试间隔的随机抖动幅度(0=关闭用固定间隔,0.5=±50%),模拟人类节奏 |
| CaptchaSolver | nil | 验证码识别器,不配则遇验证码返回 ErrCaptchaRequired |
需要传入 CaptchaSolver 等配置时,用 *WithConfig 变体(普通版本等价于传 nil 配置):
RequestVulDetailByIDWithConfig/RequestVulDetailByURLWithConfigRequestVulListByOffsetWithConfigRequestVulPatchByIDWithConfig/RequestVulPatchByURLWithConfigRequestVulListByQueryWithConfigFetchVulDetailWithConfig
实现 ProxyProvider func() (string, error) 即可接入任意代理源。内置:
FixedProxyProvider(proxy)—— 固定 IP(传空串直连,测试用)PinYiProxyProvider()—— 品易代理 API(注:其源已下线,DNS 无法解析,仅供兼容)
# 离线测试(解析逻辑,不依赖网络与代理)
go test ./cnvd_skills/ -short -v
# 真实集成测试(直连 CNVD,需可访问外网;含验证码自动识别,需 ddddocr)
go test ./cnvd_skills/ -run "_Real" -v -timeout 400s真实集成测试 _Real 系列:直连 CNVD 抓取真实数据,遇验证码用 CommandCaptchaSolver 调 scripts/ddddocr_solver.py 自动识别。验证码图为中文词组、OCR 有概率性,库内最多重试 6 次,偶发失败重跑即可。
- 解析与请求分离:
ParseXxx接受纯字符串入参、返回结构体与 error,可用本地 HTML fixture 离线测试,无需网络与代理。 - 自研加速乐客户端:
jsl_client.go复刻并修复了原 jsl_sdk 的三层解密(first 层正则兼容; Max-age大写带空格格式),接入 context、超时、代理与验证码挑战流程,移除了对私有 jsl_sdk 的依赖。已剥离为独立模块 go-jsl(github.com/scagogogo/go-jsl),本库通过 go.mod require + 本地 replace 依赖;CnvdSkills持有默认jsl.JslClient实例,三处请求统一走requestWithRetry方法派生的独立客户端(并发安全)。 - 请求隐蔽性强化:go-jsl 内部经统一
HttpClient收发所有请求(三层解密每一跳、验证码取图/提交、放行刷新),而非每次新建连接——复用 TCP/TLS 连接、cookie jar 自动管理会话、浏览器级 Header 全套(Client Hintssec-ch-ua与 UA 大版本联动、Fetch MetadataSec-Fetch-*)、UA 从真实 Chrome 121/122 池随机、翻页/详情/验证码间隔按Jitter随机抖动,降低被反爬识别为机器的概率。详见 go-jsl README。 - 验证码可插拔:识别环节抽成
CaptchaSolver接口,库负责取图/提交/放行刷新/重试,调用方注入"图→答案"实现。 - 请求层重试:
requestWithRetry统一封装,代理类错误自动换 IP、非代理错误按MaxRetry重试,验证码类错误(ErrCaptchaRequired)不重试直接上抛,全程支持context.Context取消。 - 去重:
EnableDedup开启时,写文件前读取已抓 CNVD 集合,跳过重复条目,支持断点续抓。 - 不 panic:所有错误返回 error,库代码无
panic调用。