第三章 数据集构建:开源漏洞库、真实审计报告与人工标注规范

好,咱们进入第三章。这一章我打算聊聊数据集构建。说白了,就是喂给模型吃的“教材”从哪来、怎么洗、怎么标。

你想想看,一个智能合约审查模型,如果没见过真正的漏洞长什么样,它怎么学得会?我刚开始做这个方向时,踩过不少坑。最惨的一次,模型在测试集上准确率95%,一上真实审计项目,直接漏掉一个重入漏洞。后来复盘发现,训练数据里全是教科书级别的例子,太“干净”了。

所以,数据集构建不是简单的“找一堆代码扔进去”。它是个手艺活。今天我就把这三条路——开源漏洞库、真实审计报告、人工标注规范——掰开揉碎了讲。

3.1 开源漏洞库:SWC Registry 与 SmartContractVulnerabilityDB

先说说开源漏洞库。这是最省力的起点。我个人习惯把这两个库当作“基础教材”。

3.1.1 SWC Registry

SWC Registry 是 ConsenSys Diligence 维护的智能合约漏洞分类标准。它有点像 OWASP Top 10,但专攻以太坊。

它把漏洞分成了 100 多个类别,每个都有编号、描述、示例代码和修复建议。比如 SWC-101 是重入攻击,SWC-102 是整数溢出。

我在项目中遇到过,有些团队拿 SWC 当“检查清单”用。但说实话,它更适合做“分类标签”。你训练模型时,可以用 SWC ID 作为输出类别。

关键点: SWC 的示例代码偏短,通常只有几十行。适合做“概念学习”,但不够覆盖真实场景的复杂性。

3.1.2 SmartContractVulnerabilityDB

这个库是另一个宝藏。它收集了真实项目中被利用过的漏洞合约。每个条目都包含:

  • 漏洞合约源码
  • 攻击交易哈希
  • 漏洞类型描述
  • 修复后的合约版本

嗯,这里要注意。这个库的数据量不大,大概几百条。但每条都是“真金白银”的实战案例。我建议把它当作验证集,而不是训练集。为什么?因为数据太少,容易过拟合。

对比维度 SWC Registry SmartContractVulnerabilityDB
数据量 100+ 类别,每个类别 1-3 个示例 约 500 个真实案例
代码长度 短(10-50 行) 中长(100-500 行)
适用场景 分类标签定义、概念学习 验证集、真实场景测试
局限性 示例过于理想化 覆盖的漏洞类型不全

3.2 真实审计报告爬取:从 PDF 到结构化数据

开源库只是开胃菜。真正的大头是真实审计报告。我做过一个统计,市面上 80% 的公开审计报告都藏在 PDF 里。怎么把它们变成模型能吃的结构化数据?

我曾经花了两周时间,写了一套爬虫+解析管线。核心步骤就三步:

  1. 爬取源:主要抓 Trail of Bits、ConsenSys、OpenZeppelin 等知名审计公司的公开报告。
  2. PDF 解析:用 PyMuPDF 或 pdfplumber 提取文本。但这里有个坑——很多报告是扫描件,OCR 跑出来的文本错位严重。
  3. 结构化提取:用正则或简单 NLP 把“漏洞描述”、“风险等级”、“代码片段”、“修复建议”拆出来。
避坑指南: 我曾经天真地以为 PDF 解析能 100% 准确。结果发现,有些报告的代码块被分页切断了,模型训练时学到一半的代码,预测结果一塌糊涂。后来我加了“代码块合并”逻辑,按缩进和行号判断是否属于同一段。

爬下来的数据长什么样?我给你看个例子:

{
  "source": "Trail of Bits - Uniswap V3 Audit",
  "vulnerability": {
    "type": "Reentrancy",
    "severity": "High",
    "description": "The `flash` function does not follow the checks-effects-interactions pattern...",
    "code_snippet": "function flash(uint256 amount) external {\n    uint256 balanceBefore = address(this).balance;\n    ...\n    (bool success, ) = msg.sender.call{value: amount}(\"\");\n    if (address(this).balance < balanceBefore) revert();\n}",
    "fix_suggestion": "Move the balance check before the external call."
  }
}

这个结构,直接喂给模型做微调,效果比纯文本好得多。

3.3 人工标注规范:让标注员不吵架

数据有了,但没标注等于白搭。人工标注是数据集构建里最耗时、最容易被低估的环节。

我见过最离谱的标注项目:两个标注员对同一个漏洞,一个标“重入”,一个标“逻辑错误”。为什么?因为没有统一的标注规范。

所以,我总结了一套标注规范,核心就三条:

3.3.1 漏洞类型定义要明确

别用模糊词。比如“重入攻击”要定义为:

  • 外部调用(call/delegatecall)发生在状态更新之前
  • 攻击者可以通过回调函数重复执行关键逻辑

每个类型都要配 2-3 个正例和反例。标注员先看例子,再动手。

3.3.2 标注粒度要统一

是按函数标注?还是按代码行标注?我建议按“漏洞点”标注。一个函数里可能有多个漏洞点,每个点单独标。

举个例子:

// 漏洞点1:重入(第15行外部调用)
// 漏洞点2:整数溢出(第20行算术运算)
function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    (bool success, ) = msg.sender.call{value: amount}(""); // 漏洞点1
    balances[msg.sender] -= amount; // 漏洞点2
}

3.3.3 争议处理机制

标注员之间意见不一致怎么办?我定了个“三级仲裁”流程:

  1. 标注员自行讨论,达成一致
  2. 讨论无果,提交给资深工程师(也就是我)仲裁
  3. 如果还是争议,标记为“不确定”,不进入训练集
小技巧: 标注时,让标注员同时写出“为什么认为是这个漏洞”。这不仅能提高标注质量,还能积累解释数据,以后可以用来训练模型的“推理能力”。

3.4 知识体系总览

说了这么多,我画了张图帮你理清思路。这张图展示了数据集构建的完整流程:从开源库和审计报告获取原始数据,经过清洗和结构化,再到人工标注,最终形成训练集、验证集和测试集。

数据集构建知识体系 开源漏洞库 SWC Registry 真实审计报告 PDF爬取+解析 人工标注 规范+仲裁 数据清洗与结构化 去重 → 格式统一 → 代码块合并 → 漏洞点提取 人工标注与质量审核 类型定义 → 粒度统一 → 三级仲裁 → 质量抽检 训练集 / 验证集 / 测试集

这张图从左到右展示了三条数据来源,最终汇聚到清洗、标注、输出三个环节。你训练模型时,建议按 7:2:1 的比例划分训练集、验证集和测试集。验证集最好全部来自真实审计报告,这样能更真实地评估模型在实战中的表现。

好了,数据集构建这块就聊到这儿。记住,数据质量决定模型上限。花再多时间在数据上都不为过。下一章咱们聊聊模型选型和训练策略,到时候见。


专注资料整理