起因是今天完善竞赛作品想整多语言的漏洞数据集,恰好找到了这个:cvefixes
感觉应该还可以,打算先试试手,但是11个G,下的也太慢了。
然后就想起来之前我看见过NDM这个下载软件,当时记得性能不错,今天一用,果真快了不少。
(一)NDM
1. 下载连接
2. 配置
因为我是下外面的东西,所以得科学上网,正好NDM有给配置代理的地方。
在 Settings --> Proxy/Socks
中设置一下就好,clash的端口号自己去clash看一下
3. 资源嗅探
在Edge的扩展中下载NeatDownloadManager Extension,可以实现网站的资源嗅探,直接下载对应的视频等。
比较可惜的是对于m3u8格式的视频是无法成功提取的,只支持mp4,不过也很好了~
(二)cvefixes
之前尝试去获取这个数据集,在Windows系统下按照Readme文件操作,一直没成功。后面自己因为其他原因装了WSL,就又在这个Linux环境下试了一下,成功了。
1. 提取db文件
开始之前确保自己配置了linux环境,我这里直接以WSL作为示例了
(1)标准做法
运行:
(cvefixesEnv) G:\VulnerabilityDataset\CVEfixes\CVEfixes>wsl sh Code/create_CVEfixes_from_dump.sh
可能提示
Code/create_CVEfixes_from_dump.sh: 7: gzcat: not found
Code/create_CVEfixes_from_dump.sh: 7: sqlite3: not found
那就得下载对应的工具,执行:
sudo apt update
sudo apt install gzip sqlite3
再次运行.sh脚本即可
(2)我的做法
由于我安装了这些工具还是执行失败,问题出在gzcat命令的运行上而不是提取上,因此我选择:
将 create_CVEfixes_from_dump.sh
的内容替换如下:
DATA_PATH=Data/
sqlite3 $DATA_PATH/CVEfixes.db < $DATA_PATH/CVEfixes.sql
直接用Bandzip解压CVEfixes.sql.gz,解压出来的CVEfixes.sql存放在Data目录下。
再次运行.sh脚本,直接开始漫长的db文件提取
简而言之,数据集前期的工作就是从CVEfixes.sql.gz变为CVEfixes.sql,最终成为CVEfixes.db
2. 设置配置文件
在项目根目录(即本 INSTALL.md 文件所在位置)创建一个 .CVEfixes.ini 配置文件(仓库包含一个文件 example.CVEfixes.ini,复制改名即可)。
在 [CVEfixes] 部分定义以下变量以自定义路径:
- database_path:应包含 CVEfixes 数据库文件(以及提取过程中的一些临时文件)的目录。
- sample_limit: 要提取的样本数量,sample_limit = 0 被解释为无限数量的样本
3. 设置 GitHub 令牌
这一步是因为CVEFixes数据集在db文件中存的是Github提交url,可以下载DB Browser for Sqlite来看具体的数据库表结构~
因此,获取一个 GitHub 令牌并配置到 .CVEfixes.ini 配置文件,其中包含两个变量 user 和 token,分别填写你的 GitHub 用户名和访问令牌。
如何获取令牌就自行百度吧,很简单~
4. 提取源代码
以下是我整的一个脚本,记得替换输出目录。
import sqlite3
import pandas as pd
from pathlib import Path
import os
# --- 步骤 1: 定义路径 ---
BASE_PATH = Path(__file__).parent
DB_PATH = BASE_PATH / "Data" / "CVEfixes.db"
# 定义输出目录 - 按用户要求修改路径
VULNERABLE_DIR = Path("G:/VulnerabilityDataset/CVEfixes/vulnerability_dataset/vulnerable")
NON_VULNERABLE_DIR = Path("G:/VulnerabilityDataset/CVEfixes/vulnerability_dataset/non_vulnerable")
# --- 步骤 2: 创建输出目录 ---
print("正在创建源代码数据集输出目录...")
VULNERABLE_DIR.mkdir(parents=True, exist_ok=True)
NON_VULNERABLE_DIR.mkdir(parents=True, exist_ok=True)
print(f"修复版本将保存到: {VULNERABLE_DIR}")
print(f"未修复版本将保存到: {NON_VULNERABLE_DIR}")
# --- 步骤 3: 连接数据库并查询数据 ---
try:
conn = sqlite3.connect(DB_PATH)
print(f"\n成功连接到数据库: {DB_PATH}")
except sqlite3.Error as e:
print(f"数据库连接失败: {e}")
exit()
# 编写 SQL 查询语句 - 修复查询逻辑,获取文件级别的代码和CWE信息
query = """
SELECT DISTINCT
f.file_change_id,
f.filename,
f.code_before,
f.code_after,
f.programming_language,
cc.cwe_id,
cv.cve_id
FROM file_change f
JOIN commits c ON f.hash = c.hash
JOIN fixes fx ON c.hash = fx.hash
JOIN cve cv ON fx.cve_id = cv.cve_id
JOIN cwe_classification cc ON cv.cve_id = cc.cve_id
WHERE f.programming_language IN ('C', 'C++', 'C/C++')
AND f.code_before IS NOT NULL
AND f.code_after IS NOT NULL
AND f.code_before != ''
AND f.code_after != ''
ORDER BY f.file_change_id
"""
print("正在从数据库查询源代码数据...")
df = pd.read_sql_query(query, conn)
conn.close()
print(f"查询完毕!共找到 {len(df)} 条源代码变更记录。")
# --- 步骤 4: 遍历数据并保存文件 ---
if df.empty:
print("未查询到任何源代码数据,请检查数据库。")
else:
print("\n开始处理并保存文件...")
total_files = len(df)
processed_count = 0
for index, row in df.iterrows():
try:
# 获取原始文件的后缀名
original_filename = row['filename']
extension = Path(original_filename).suffix
if not extension:
# 根据编程语言设置默认后缀
if row['programming_language'] == 'C':
extension = ".c"
elif row['programming_language'] in ['C++', 'C/C++']:
extension = ".cpp"
else:
extension = ".txt"
# 构造新的文件名:CWE-ID_CVE-ID_文件变更ID.后缀
cwe_id_sanitized = str(row['cwe_id']).replace(':', '_').replace('/', '_')
cve_id_sanitized = str(row['cve_id']).replace('-', '_')
file_change_id = str(row['file_change_id'])
new_filename = f"{cwe_id_sanitized}_{cve_id_sanitized}_{file_change_id}{extension}"
# 获取修复前和修复后的代码
code_before = row['code_before']
code_after = row['code_after']
# 保存未修复版本 (vulnerable) - 修复前的代码
if code_before and isinstance(code_before, str) and code_before.strip():
vulnerable_path = NON_VULNERABLE_DIR / new_filename
with open(vulnerable_path, 'w', encoding='utf-8') as f:
f.write(code_before)
# 保存已修复版本 (non_vulnerable) - 修复后的代码
if code_after and isinstance(code_after, str) and code_after.strip():
fixed_path = VULNERABLE_DIR / new_filename
with open(fixed_path, 'w', encoding='utf-8') as f:
f.write(code_after)
processed_count += 1
if processed_count % 100 == 0:
print(f" 已处理 {processed_count}/{total_files} 个文件...")
except Exception as e:
print(f"处理第 {index + 1} 行时出错: {e} - 文件名: {row.get('filename', 'N/A')}")
continue
print(f"\n所有文件处理完毕!成功处理了 {processed_count} 个文件。")
print(f"修复版本保存在: {VULNERABLE_DIR}")
print(f"未修复版本保存在: {NON_VULNERABLE_DIR}")