ReposVul:一个仓库级的高质量漏洞数据集
ReposVul: A Repository-Level High-Quality Vulnerability Dataset
文章链接:https://dl.acm.org/doi/pdf/10.1145/3639478.3647634, ICSE2024
摘要
开源软件(OSS)漏洞对软件安全性带来了巨大挑战,并对我们的社会构成了潜在风险。为了应对这一问题,大量研究工作致力于自动化漏洞检测,其中基于深度学习(DL)的方法被证明是最有效的[1]。然而,基于DL的方法的性能通常依赖于标注数据的数量和质量,而当前的标注数据存在以下局限性:
- 纠缠的补丁:开发者可能在补丁中提交与漏洞修复无关的代码更改,导致补丁纠缠不清。
- 缺乏跨过程漏洞:现有的漏洞数据集通常包含函数级和文件级漏洞,忽略了函数之间的关系,从而使方法无法检测跨过程漏洞。
- 过时的补丁:现有数据集通常包含过时的补丁,这可能在训练过程中对模型产生偏差。
为了解决上述局限性,本文提出了一种自动化数据收集框架,并构建了首个仓库级高质量漏洞数据集,名为 ReposVul。所提出的框架主要包含以下三个模块:
- 漏洞解缠模块:旨在从纠缠的补丁中区分出与漏洞修复相关的代码更改,该模块联合使用了大型语言模型(LLMs)和静态分析工具。
- 多粒度依赖提取模块:旨在捕获漏洞的跨过程调用关系,我们为每个漏洞补丁构建了多粒度信息,包括仓库级、文件级、函数级和行级。
- 基于追踪的过滤模块:旨在过滤掉过时的补丁,利用基于文件路径追踪的过滤器和基于提交时间追踪的过滤器,以构建最新的数据集。
构建的仓库级数据集 ReposVul 涵盖了 6,134 个 CVE 条目,代表了 1,491 个项目中的 236 种 CWE 类型,并涉及四种编程语言。详尽的数据分析和人工检查表明,ReposVul 具有高质量,并缓解了以往漏洞数据集中存在的补丁纠缠和过时的问题。
1 引言
近年来,随着开源软件(OSS)规模和复杂性的不断增加,OSS 漏洞的影响也随之扩大,并可能对社会造成巨大损失。
例如,Cisco 在 2023 年发现了一个 WebUI 中的安全漏洞,被标识为 CVE-2023-20198 [1]。该漏洞允许未经授权的远程攻击者获取更高的权限。截至目前,已有超过 41,000 台相关设备受到影响,给企业造成了重大损失。准确且及时地识别漏洞有助于减轻潜在风险,因而受到了业界和学术界的广泛关注。现有的漏洞检测方法大致可分为两类:基于程序分析的方法 [2–5] 和基于深度学习(DL)的方法 [6–9],其中基于 DL 的方法被证明更为有效。尽管基于 DL 的方法取得了成功,但其性能往往受到训练用漏洞数据集的限制。例如,Croft 等人 [10] 发现,广泛使用的漏洞数据集(如 Devign [11] 和 BigVul [12])包含噪声、不完整和过时的数据。低质量的数据可能会在模型训练和评估过程中引入偏差。因此,构建一个高质量的真实世界漏洞数据集对于漏洞检测任务而言至关重要,但目前尚未得到充分探索。
本文聚焦于通过缓解现有数据集的以下局限性来构建高质量数据集:
-
纠缠的补丁:漏洞补丁可能包含与漏洞修复无关的代码更改,导致补丁纠缠不清。现有数据集 [11–13] 通常认为一次补丁提交中的所有代码更改均与漏洞相关,这引入了自然数据噪声。例如,图 1(a) 所示的 CVE-2012-0030 补丁中包含了路径修改的代码更改,其中函数
webob.Request.blank()
的请求路径参数发生了变化(第 2-3 行)。此类与漏洞无关的更改可能涉及代码重构或新功能实现,难以区分。因此,从一个补丁中的多个文件中识别出与漏洞修复相关的文件是一项挑战。 -
缺乏跨过程漏洞:现实世界中的漏洞通常涉及多个文件和函数之间的调用,而单个函数本身未必存在漏洞。现有数据集 [11, 13] 主要关注函数级粒度,忽略了调用信息。图 1(b) 展示了一个跨过程漏洞(CVE-2017-6308)的例子。在此例中,函数
checked_xmalloc()
的返回值通过函数xmalloc()
(第 3 行)传递。考虑到size
参数可能发生溢出,存在触发 CWE-190(整数溢出或环绕)的潜在风险。然而,函数checked_xmalloc()
和xmalloc()
本身并无问题,而现有的标注方法可能将它们标记为漏洞。基于未考虑跨过程调用信息的漏洞数据训练的模型会产生偏差,从而限制其在实际场景中的性能。 -
过时的补丁:补丁可能引入新的漏洞并变得过时,而当前数据集并未考虑补丁的时效性。图 1© 展示了来自 CVE-2019-19927 的原始补丁及其后续补丁。在原始补丁中,开发者通过对现有条件语句添加更强的约束来避免 CWE-125(越界读取)[14](第 6-7 行)。然而,在随后的补丁中,条件语句后的循环语句发生了变化(第 7-8 行),提交信息表明“修复大页检查的起始页”。显然,这一额外修复是由于原始补丁的不完整性,因此原始补丁已过时,应被过滤掉。
本文提出了一种自动化数据收集框架,并构建了一个仓库级高质量漏洞数据集,名为 ReposVul,以解决上述局限性。我们的框架由以下三个模块组成:
- 漏洞解缠模块:我们建议整合大型语言模型(LLMs)和静态分析工具的决策,以区分补丁中与漏洞修复相关的文件,分别利用其强大的上下文理解能力和领域知识。
- 多粒度依赖提取模块:我们提取整个仓库中漏洞的跨过程调用关系,旨在为每个漏洞补丁构建多粒度信息,包括文件级、函数级和行级信息。
- 基于追踪的过滤模块:我们首先根据文件路径和提交时间追踪补丁的提交历史。通过分析补丁的历史信息,然后通过追踪其提交差异来识别过时的补丁。
总结来说,我们的贡献如下:
- 我们引入了一个用于获取漏洞数据的自动化数据收集框架。该框架包括一个漏洞解缠模块,用于识别纠缠补丁中与漏洞修复相关的文件;一个多粒度依赖提取模块,用于构建跨过程漏洞;以及一个基于追踪的过滤模块,用于识别过时的补丁。
- ReposVul 是首个仓库级漏洞数据集,涵盖大规模 CVE 条目,代表了 1,491 个项目中的 236 种 CWE 类型,涉及四种编程语言,并提供了详细的多粒度补丁信息。
- 通过人工检查和数据分析,ReposVul 质量较高,并缓解了现有漏洞数据集的局限性。我们已公开发布源代码及 ReposVul 数据集:https://github.com/Eshe0922/ReposVul。
本文其余部分组织如下:第 2 节介绍收集 ReposVul 的框架;第 3 节展示评估和实验结果;第 4 节讨论 ReposVul 的数据应用及局限性;第 5 节介绍 OSS 漏洞数据集和检测方法的背景;第 6 节总结全文。
2 框架
图 2 展示了我们的数据收集框架的概览,该框架包含四个步骤以构建漏洞数据集。框架从原始数据爬取步骤开始,旨在收集漏洞条目及其相关的补丁,从而形成初始数据集。然后,通过以下三个关键模块对这一初始数据集进行处理,最终生成 ReposVul 数据集:
- 漏洞解缠模块:通过联合考虑大型语言模型(LLMs)和静态分析工具的决策,自动识别纠缠补丁中与漏洞修复相关的文件。
- 多粒度依赖提取模块:提取整个仓库中与漏洞相关的跨过程调用关系。
- 基于追踪的过滤模块:根据文件路径和提交时间追踪补丁的提交历史,旨在分析与补丁对应的提交差异。
2.1 原始数据爬取
此阶段的目标是收集广泛的原始漏洞数据,详细的条目信息如表 1 所示。创建初始数据集涉及三个步骤:
- 从开源数据库中爬取漏洞条目;
- 从多个平台获取与漏洞条目相关的补丁;
- 获取补丁中涉及的更改文件的详细信息。
2.1.1 漏洞条目收集
我们从 Mend [15] 收集开源漏洞数据,其中包含流行社区资源以及鲜为人知的社区资源,并涵盖了大量漏洞条目。在收集过程中,我们按时间顺序检索 CVE 条目,以便识别过时的补丁。然后,我们将这些条目存储为结构化格式,如表 1 中“漏洞条目信息”部分所示。每个条目包含诸如 CVE-ID、CVE 描述、关联的 CWE-ID 和其他相关信息等关键特征。
2.1.2 补丁收集
为了全面分析每个漏洞条目,我们收集相应的补丁。对于大多数项目,我们从 GitHub [16] 收集这些补丁,并记录其 Commit-ID 和 Commit-Message。此外,对于 Android 和 Chrome 等两个特殊项目,我们分别在 Google Git [17] 和 bugs.chromium [18] 上进行补丁收集,因为它们的部分补丁并未发布在 GitHub 上。详细的补丁级信息总结于表 1 中。
2.1.3 相关文件收集
为了在文件级和仓库级提取漏洞代码片段,我们使用唯一的 Commit-ID 下载与每个补丁相关的父仓库和子仓库。对于补丁中的每个文件,我们检索其代码更改前后的内容。在表 1 中,我们展示了每个相关文件的详细信息,包括文件名(File-Name)、更改前代码(Code-Before)和更改后代码(Code-After)等关键特征。
2.2 漏洞解缠模块
漏洞解缠模块旨在从补丁中移除与漏洞修复无关的代码更改。我们分别使用 LLMs 评估代码更改与漏洞修复的相关性,并使用静态分析工具检查代码更改中的漏洞。综合考虑两者的输出结果,我们确定某个更改后的文件是否与漏洞修复相关。
2.2.1 LLMs 评估
LLMs 具有强大的上下文理解能力和广泛的编程语言适用性。其对不同编程语言的适应性确保了基于 LLMs 的评估可以覆盖各种代码语法和结构。我们选择通义千问 [19] 来有效评估代码更改与漏洞修复的相关性,因其提供了免费的 API 并支持长上下文输入。
为了充分利用 LLMs 的上下文理解能力,我们设计了一个任务特定的提示来评估代码更改与相应漏洞修复的相关性,如图 3 所示。
该提示由以下几个部分组成:
- 系统提示:LLM 充当专家,分析代码漏洞及其相应的修复。
- 上下文提示:提示包括四种类型的上下文信息:
- CWE 描述:基于与每个补丁关联的 CWE-ID,我们提供简要的漏洞描述。
- CWE 解决方案:我们提供根据 CWE-ID 推荐的解决方案,帮助 LLMs 评估代码更改是否符合漏洞解决方案。
- 提交信息:补丁中的提交信息有助于 LLMs 理解修复的目的。
- 函数:我们使用 Tree-sitter 工具 [20] 提取文件中受代码更改影响的函数。这为代码更改提供了上下文,有助于精确分析。
- 输入代码更改:文件中具体的代码更改。
- 回答提示:LLMs 评估文件中代码更改与漏洞修复的相关性,并输出“YES”或“NO”,表示代码更改是否与漏洞修复相关。
鉴于提示中包含了详细的漏洞和补丁信息,LLMs 可以基于代码上下文有效判断代码更改与漏洞修复的相关性。
2.2.2 静态分析工具检查
除了 LLMs,我们还使用静态分析工具进行漏洞检查。静态分析工具通过提取源代码模型 [21] 并应用多种漏洞规则来检测代码中的漏洞,确保高代码覆盖率和低假阴性率。我们采用多种静态分析工具来检查代码更改中的漏洞,这些工具整合了来自不同工具的漏洞规则和专家知识。
我们选择了四种成熟的工具,包括 Cppcheck [22]、Flawfinder [23]、RATS [24] 和 Semgrep [25],考虑到它们的开源可用性和灵活配置选项,以实现跨多种编程语言的有效漏洞检测。针对不同的语言,我们根据工具的适用性选择不同的静态分析工具。具体而言,对于 C 和 C++ 文件,我们使用所有四种工具;对于 Python 文件,我们使用 RATS 和 Semgrep;而对于 Java 文件,仅使用 Semgrep。对于补丁中的每个文件,我们首先使用静态分析工具检查修复前的文件是否包含漏洞。只有当修复前版本中检测到的漏洞与补丁中的代码更改相对应时,我们才将该文件标记为与漏洞修复相关。
2.2.3 联合决策
我们结合 LLMs 的理解能力和静态分析工具的领域知识,识别一个补丁中与漏洞修复相关的文件。只有当 LLMs 和静态分析工具得出相同结论时,文件才会被标记为相关或不相关。对于 LLMs 和静态分析工具结果冲突的文件,将在后续处理中排除。
2.3 多粒度依赖提取模块
现有的漏洞数据集主要集中于函数级漏洞,忽略了丰富的上下文信息。为缓解这一挑战,我们提取了整个仓库中漏洞的跨过程调用关系,并为每个漏洞补丁构建了多粒度信息,包括仓库级、文件级、函数级和行级信息。
2.3.1 先导经验
图 1(a) 展示了一个跨过程 CWE-190(整数溢出或环绕,Integer Overflow or Wraparound)漏洞 [26] 的示例。图 4(a) 和图 4(b) 展示了在后续提交中实现的两种真实世界解决方案,分别涉及调用方函数的更改和被调用方函数的调整。具体而言,图 4(a) 展示了第一种解决方案,其中函数 check_mul_overflow()
被用于在调用 xcalloc()
函数之前验证变量 size
是否过大(第 3-4 行)。图 4(b) 展示了第二种解决方案,该方案在 xcalloc()
函数内部进行检查以评估 size
的大小(第 3-4 行)。因此,对于这些代码片段,无论是作为调用者还是被调用者,函数都可能引入跨过程漏洞。需要注意的是,图 4(a) 的代码更改并未覆盖第六行,其中包含跨过程漏洞 API xmalloc()
。因此,在从代码片段中捕获跨过程漏洞时,我们需要将搜索范围扩展到其顶级函数。
2.3.2 仓库级依赖提取算法
我们开发了一种依赖提取算法,包含两个组件:仓库级依赖提取(算法 1)和调用者与被调用者树根提取(算法 2)。算法 1 是依赖提取的主要流程,而算法 2 则专门解决函数调用者和被调用者树根的提取问题。
算法 1 展示了我们的仓库级依赖提取框架。它以选定的编程语言和项目中所有与漏洞修复相关的文件作为输入,并生成两个输出:函数调用者树(callerTree
)和函数被调用者树(calleeTree
)。算法的工作方式如下:对于每个与漏洞修复相关的文件中的更改代码片段,我们首先识别调用者树和被调用者树的根函数,分别命名为 callerRoot
和 calleeRoot
(第 8-9 行)。随后,使用特定的静态分析工具从 callerRoot
构建调用者树,并从 calleeRoot
构建被调用者树(第 11-12 行)。针对不同的编程语言,我们使用不同的工具:C/C++ 使用 cflow
[27],Java 使用 Java-all-call-graph
[28],Python 使用 PyCG
[29],因为这些工具专为特定编程语言设计。
算法 2 旨在识别函数调用者和被调用者树的根。对于被调用者树根的提取,我们识别文件中的所有顶级函数(第 5 行)。接下来,我们检索与提供的代码片段重叠的顶级函数(第 6-10 行)。对于调用者树根的提取,我们首先将 API 提取范围扩展到包含给定代码片段的顶级函数(第 15 行)。然后利用 Tree-sitter 工具 [20] 提取范围内所有相关 API(第 16 行)。
2.3.3 多粒度代码片段
为了识别跨过程和过程内漏洞并促进漏洞定位,我们为每个补丁构建了多粒度信息,包括仓库级、文件级、函数级和行级信息。
如表 2 所示,每个补丁包含多粒度信息:
- 仓库级:对于每个与漏洞修复相关的文件,我们使用算法 1 提取整个仓库中漏洞的跨过程调用关系。
- 文件级:我们将漏洞修复相关文件的更改前和更改后版本分别视为易受攻击和非易受攻击。对于与漏洞修复无关的文件,我们将更改前和更改后版本均视为非易受攻击。
- 函数级:对于每个受代码更改影响的函数,如果该函数定义在与漏洞修复相关的文件中,我们将更改前和更改后版本分别视为易受攻击和非易受攻击;如果该函数定义在与漏洞修复无关的文件中,我们将更改前和更改后版本均视为非易受攻击。对于每个未受代码更改影响的函数,我们将其视为非易受攻击。
- 行级:我们从补丁中的代码更改中提取行更改及其行号,并将这些行更改作为精确检测目标。
2.4 基于追踪的过滤模块
补丁可能会引入漏洞并变得过时,但现有数据集并未区分过时的补丁,这对数据质量构成了潜在风险。在此模块中,我们首先根据文件路径和提交时间追踪补丁的提交历史。通过分析补丁的历史信息,我们通过追踪提交差异来识别过时的补丁。
2.4.1 基于文件路径追踪的过滤器
我们首先根据后缀过滤噪声文件。例如,一些文件(如描述文档,后缀为 .md
或 .rst
,数据文件,后缀为 .json
或 .svg
,变更日志,后缀为 .ChangeLog
,以及输出文件,后缀为 .out
)通常与功能实现无关。对于剩余的文件,我们创建一个字典,将文件路径与其在收集的漏洞补丁中最新的提交日期关联起来。然后,我们审查每个文件的提交日期,并将其与字典中记录的最新日期进行比较。如果文件的提交日期不是最新的,我们保留该文件以供基于提交时间追踪的过滤器进一步处理;否则,我们将其过滤掉。如图 1© 所示,文件 ttm_page_alloc.c
在首次代码更改后仍然存在漏洞。我们根据文件路径“ttm_page_alloc.c”及其最新的提交日期在字典中创建条目。根据文件的提交日期,我们将较早提交的文件(提交 ID a66477b
)视为易受攻击。
2.4.2 基于提交时间追踪的过滤器
我们首先根据提交时间检索每个原始补丁的父补丁和子补丁。然后,我们评估父补丁和子补丁中更改的文件是否与原始补丁中更改的文件重叠。如果基于文件路径追踪过滤器保留的文件中有重叠,我们将原始补丁识别为过时。如图 1© 所示,由于原始补丁的不完整性,原始补丁(提交 ID a66477b
)及其子补丁(提交 ID ac1e516
)更改了相同的文件 ttm_page_alloc.c
。通过比较原始补丁和其子补丁中更改文件的重叠部分,我们可以发现相同的更改文件 ttm_page_alloc.c
。由于该文件被基于文件路径追踪的过滤器保留,我们将原始补丁视为过时。
3 评估与实验结果
在本节中,我们将阐述 ReposVul 的优势,并围绕以下四个研究问题(RQs)展开讨论:
- RQ1:与现有的漏洞数据集相比,ReposVul 具有哪些优势?
- RQ2:ReposVul 中数据标签的质量如何?
- RQ3:漏洞解缠模块中大型语言模型(LLMs)的选择和提示设计对标签质量的影响有多大?
- RQ4:ReposVul 在过滤过时补丁方面的表现如何?
3.1 RQ1:ReposVul 的优势
为回答 RQ1,我们从多个方面将 ReposVul 与六个广泛使用的漏洞检测数据集 [11–13, 30–32] 进行比较,包括漏洞的粒度、CWE 类型的数量、漏洞标注方法、过时补丁的识别以及附加信息。
如表 3 所示,ReposVul 相较于现有数据集具有以下优势:
-
多粒度信息:与其他仅包含行级、函数级和文件级漏洞的数据集相比,ReposVul 提供了更全面的粒度,包括仓库级、文件级、函数级和行级漏洞。这不仅考虑了跨过程漏洞,还提供了超出单一补丁范围的信息。如表 4 所示,ReposVul 涵盖了来自 6,897 个补丁的 14,706 个文件。具体而言,ReposVul 包含了 C 语言中的 212,790 个函数、C++ 中的 20,302 个函数、Java 中的 2,816 个函数以及 Python 中的 26,308 个函数。
-
广泛的 CWE 覆盖:如表 3 所示,ReposVul 覆盖的 CWE 类型比其他所有数据集都多。ReposVul 分别覆盖了 C 语言中的 149 种 CWE 类型、C++ 中的 105 种、Java 中的 129 种以及 Python 中的 159 种。由于某些 CWE 类型并非特定于某种编程语言,ReposVul 涵盖了跨多种编程语言的 CWE 类型。这表明 ReposVul 提供了比现有基准更为全面的数据。
-
高效的标注方法:以往的研究 [10] 已经指出现有数据集中存在的噪声数据问题,这是由当前的标注方法导致的。本文中,ReposVul 提出了漏洞解缠模块以提高漏洞数据的质量。该模块结合了静态分析工具提供的漏洞规则和领域知识,以及 LLMs 强大的上下文理解能力。
-
过时补丁的识别:当前的漏洞数据集并未区分过时补丁。ReposVul 使用基于追踪的过滤模块来识别潜在的过时补丁。该基于追踪的过滤模块整合了基于文件路径追踪的过滤器和基于提交时间追踪的过滤器,从而为过时补丁提供标注。
-
丰富的附加信息:ReposVul 包含了最为丰富的附加信息,包括表 1 中展示的 CVE 描述、CVSS 评分和补丁提交历史,以及表 2 中展示的静态分析信息。漏洞的综合信息使开发人员和研究人员能够采取有效措施进行漏洞检测。
对 RQ1 的回答:与现有数据集相比,ReposVul 整合了多粒度代码片段和最广泛的 CWE 类型范围。它还采用了有效的漏洞数据标注方法,为过时补丁提供了标注,并附带了其他丰富的附加信息。
3.2 RQ2:ReposVul 的标签质量
为回答 RQ2,我们首先进行了实验以评估 ReposVul 的标注方法(即数据收集框架中的漏洞解缠模块)。然后,我们将 ReposVul 的标签质量与之前的数据集进行比较。
与 LLMs 和静态分析工具的比较
我们将 ReposVul 的标签质量与单独使用 LLMs 和静态分析工具获得的结果进行比较。结果如表 5 所示。具体而言,我们为每种编程语言随机选择了 50 个 CVE 案例。我们招募了三名学术研究人员作为参与者,他们均拥有五年以上的软件漏洞检测经验。根据漏洞描述和补丁的提交信息,参与者手动将补丁中文件的代码更改标记为“Yes”或“No”,分别表示文件的代码更改是否与漏洞修复相关。经过评估,参与者在 96% 的案例上达成一致。对于剩余的分歧,参与者通过协商达成共识。
如表 5 所示,我们观察到所提出的标注方法在四种编程语言上的平均准确率最高,达到 85%,比单独使用 LLMs 高出 10%,比单独使用静态分析工具高出 5.5%。
与现有数据集的比较
以往的研究 [10] 表明,在最先进的开源软件(OSS)漏洞数据集中,20%-71% 的漏洞标签不准确。分析表 5 中的结果,我们观察到 ReposVul 在 C、C++、Java 和 Python 上的标注准确率分别为 85%、90%、85% 和 80%。与 [10] 中报告的现有 OSS 漏洞数据集相比,ReposVul 在四种编程语言上均实现了相对更高的准确率,尤其是在 C++ 编程语言中,准确率高达 90%,表现尤为突出。我们的观察表明,由于整合了 LLMs 的上下文理解能力和静态分析工具的领域知识,ReposVul 的标签质量优于之前的数据集。
对 RQ2 的回答:通过对部分标注数据进行人工检查,ReposVul 的标签质量优于现有数据集,在 C、C++、Java 和 Python 上的准确率分别达到 85%、90%、85% 和 80%。所提出的标注方法也比单独应用 LLMs 或静态分析工具表现更好。
3.3 RQ3:不同 LLMs 和提示设计对标签质量的影响
为回答 RQ3,我们进行了实验,分析漏洞解缠模块中 LLM 和提示设计对 ReposVul 标签质量的影响。在本节中,我们随机选择了 20 个 CVE 案例,这些案例包含了可能与漏洞修复相关或无关的代码更改。我们招募了参与第 3.2 节人工标注的三位学术研究人员进行标注。参与者将代码更改手动标记为“相关”和“无关”。经过评估,参与者在所有 20 个案例上达成了 100% 的一致意见。
LLM 选择
我们在表 6 中展示了九种研究的 LLMs。ChatGLM [33] 和 Baichuan2 [34] 分别是由 THUDM 和 baichuan-ai 开发的开源 LLMs,基于中英双语语料训练。Llama2 [35] 相较于 Llama [36] 拥有更大的训练数据集。CodeLlama [37] 基于 Llama2 并使用代码相关数据进行训练和微调。ChatGPT [38] 和 GPT-4 [39] 是由 OpenAI 提供的商业 LLMs。通义千问 [19] 由阿里云开发,在多项任务中表现出色。
实验结果如表 6 所示。结果显示,GPT-4 和通义千问达到了 85% 的最高准确率,能够识别所有与漏洞修复相关的代码更改,并正确判断大部分与漏洞修复无关的代码更改。相比之下,Llama2 将所有代码更改均视为与漏洞修复相关,因此无法有效区分纠缠的补丁。其他 LLMs 的准确率未超过 80%。考虑到通义千问 [19] 在提供免费 API 接入的同时具有最高准确率,我们选择该 LLM 进行标注。
提示设计
以往的研究 [6] 表明,代码相关任务的性能很大程度上受到提示设计的影响。我们也构建了四种变体,包括不包含 CWE 描述(即 w/o CWE Description)、CWE 解决方案(即 w/o CWE Solution)、提交信息(即 w/o Commit Message)以及代码更改涉及的函数(即 w/o Function)。如表 7 所示,ReposVul 使用的提示设计达到了 85% 的最高准确率。四种变体分别导致准确率下降了 30%、15%、5% 和 5%。结果表明,CWE 描述对 LLM 的影响最大,因为其包含丰富的漏洞信息,而提交信息对 LLM 的影响较小,可能是由于提交信息中存在不准确和冗余的内容。
对 RQ3 的回答:在不同的 LLMs 中,GPT-4 和通义千问在标注代码更改与漏洞修复相关性方面达到了 85% 的最佳准确率。在提示设计中,CWE 描述和 CWE 解决方案对 LLM 的决策有较大影响。所提出的提示设计达到了 85% 的最高准确率。
3.4 RQ4:ReposVul 过滤过时补丁的表现
为回答 RQ4,我们从多个方面展示了 ReposVul 识别的过时补丁的统计数据,包括 CWE 类型、时间、项目和编程语言。
CWE 类型
图 5(a) 展示了按过时补丁数量排名前 10 的 CWE 类型中,过时补丁与总补丁的分布情况。我们观察到,CWE-119(内存缓冲区内操作限制不当)[40]、CWE-787(越界写入)[41] 和 CWE-125(越界读取)[14] 包含最多的过时补丁,分别为 121、108 和 97 个。这些 CWE 类型与缓冲区操作相关,表明开发者在修改涉及缓冲区的代码片段时更容易引入新漏洞,从而导致补丁过时。
时间
图 5(b) 描绘了过去十年中过时补丁与总补丁的分布情况。结果显示,与开源软件(OSS)相关的补丁总数呈现持续上升趋势,从 2014 年的 264 个增加到 2022 年的 1,132 个。同时,过时补丁的数量也有所增加,从 2014 年的 53 个增加到 2022 年的 118 个。值得注意的是,2023 年这两项指标均有所下降,这可能是由于未公开的漏洞所致。此外,过时补丁占总补丁的比例显著下降,从 2014 年的 20.07% 下降到 2022 年的 10.42%。这一下降表明开发者在增强补丁的鲁棒性方面投入了更多关注。
项目
图 5© 展示了按过时补丁数量排名前 10 的项目。Linux 项目中过时补丁的比例最高,达到 16.05%。紧随其后的是 ImageMagick 和 Vim,分别占比 10.26% 和 6.58%。Linux 中过时补丁比例较高的原因可以归因于其庞大的项目范围和大量文件,使得在提交补丁时更容易引入新漏洞。值得注意的是,尽管 ImageMagick 和 Vim 的项目规模较小,但它们的过时补丁数量仍然较多,这可能与安全厂商维护补丁信息的延迟有关。
编程语言
图 5(d) 展示了按编程语言分类的过时补丁分布情况。在这些语言中,C 语言占据了最大份额,达 70.7%,其次是 Java(14.6%)、Python(8.1%)和 C++(6.6%)。C 语言中过时补丁的高发率与其缓冲区操作不当相关,尤其是 CWE-119、CWE-787 和 CWE-125。这些漏洞主要归因于灵活数组的使用以及缺乏内置的边界检查机制。
对 RQ4 的回答:在识别的过时补丁中,与缓冲区操作相关的补丁占大多数。随着时间推移,过时补丁的数量增加,但其在总补丁中的比例有所下降。在项目和编程语言层面,Linux 和 C 分别以 16.05% 和 70.7% 的比例拥有最高的过时补丁占比。
4 讨论
4.1 数据应用
ReposVul 是首个跨多种编程语言的仓库级漏洞数据集。ReposVul 可用于解决一系列与开源软件(OSS)漏洞相关的任务。我们建议将 ReposVul 作为基准,推动对模型性能进行标准化和实用化的评估。
-
多粒度漏洞检测:ReposVul 涵盖了多粒度信息,包括仓库级、文件级、函数级和行级特征。研究人员可以利用这些特征来检测跨过程和过程内漏洞。此外,实验表明,ReposVul 在标签质量方面优于现有的最先进数据集,这为基于深度学习(DL)的漏洞检测方法提供了更好的训练支持。
-
补丁管理:ReposVul 包含丰富的补丁信息,包括提交日期、父补丁以及漏洞补丁的历史提交信息。研究人员和实践者可以利用这些及时的信息,了解现有软件漏洞的修补过程,并识别过时的补丁。
-
漏洞修复:ReposVul 提供了 CVE 描述、CWE 解决方案、CWE 后果以及 CWE 方法。这些信息有助于开发人员更好地理解漏洞的原因及其解决方案。未来的研究有望结合这些丰富的上下文信息,实现 OSS 漏洞的自动修复。
4.2 威胁与局限性
一个有效性威胁来源于数据收集的平台。在收集过程中,我们从 GitHub、Google Git 和 bugs.chromium 收集 ReposVul 数据,这导致一些托管在其他平台上的项目未能包含在 ReposVul 中。
第二个有效性威胁与编程语言相关。由于语言特异性,我们仅针对四种广泛使用的编程语言提取了仓库级依赖关系。然而,漏洞也存在于其他语言中,例如 JavaScript、Go 和 PHP。我们计划在未来的工作中为更多编程语言提取仓库级依赖关系。
另一个有效性威胁来自数据收集的时间范围。我们仅收集了 2010 年及之后的 CVE 数据,因此 ReposVul 不包含此前的 CVE 数据。这可能导致某些漏洞在前几年已被发现并修复,但未被纳入数据集中。
5 相关工作
5.1 开源软件漏洞数据集
以往的研究使用不同的方法构建开源软件(OSS)漏洞数据集,包括基于人工检查、提交代码差异和静态分析工具生成的方法。基于人工检查的数据集 [42–45] 使用人工设计的测试用例。例如,SARD [46] 包含学生编写和工业生产中的样本。Pradel 等人 [47] 使用代码转换将非漏洞样本转化为漏洞样本。然而,基于人工检查的方法在标注效率方面存在局限性。
基于提交代码差异的数据集 [12, 13, 32] 从开源仓库中收集补丁,并从补丁的代码更改中提取漏洞数据。CVEfixes [48] 从国家漏洞数据库(NVD)[49] 中获取漏洞条目及其参考链接对应的修复内容。CrossVul [31] 生成了一个覆盖 40 种编程语言和 1,675 个项目的权威数据集,但仅提供文件级源代码。此外,一些数据集使用静态分析工具 [22, 24, 50] 标注漏洞数据。例如,Russell 等人 [51] 整合多种静态分析工具的结果以确定代码片段中是否存在漏洞。D2A [30] 从六个开源项目中收集漏洞,并使用 Infer [50] 进行数据标注。然而,所有这些现有的漏洞数据集在标签质量和跨过程漏洞的捕获方面都面临挑战。本文提出了漏洞解缠模块,用于从纠缠补丁中区分与漏洞修复相关的代码更改,还提出了多粒度依赖提取模块,用于捕获跨过程调用关系。
5.2 开源软件漏洞检测
开源软件漏洞检测对于识别安全缺陷和维护软件安全性至关重要。现有的 OSS 漏洞检测方法包括基于程序分析的方法 [4, 5, 21, 52] 和基于学习的漏洞检测方法 [7, 8, 53–56]。
基于程序分析的方法利用专家知识提取特征以识别漏洞,包括数据流分析 [57] 和符号执行 [58]。数据流分析 [23, 24, 59–61] 跟踪程序执行路径上的数据流,以获取程序点的状态信息,从而基于程序点的安全性检测漏洞。符号执行 [62–64] 使用符号输入代替实际输入,并通过判断符号表达式是否满足约束条件来检测程序漏洞。
基于学习的方法可以根据源代码的表示形式分为基于序列的方法 [6, 9, 65, 66] 和基于图的方法 [67–70]。基于序列的方法将代码转换为标记序列。例如,VulDeePecker [71] 使用代码小工具表示程序,并采用双向长短时记忆网络(BiLSTM)进行训练。μVulDeePecker [72] 结合了三个 BiLSTM 网络,能够检测多种类型的漏洞。Russell 等人 [51] 整合卷积神经网络和循环神经网络进行特征提取,并使用随机森林分类器捕获漏洞模式。
基于图的方法将代码表示为图,并使用图神经网络进行软件漏洞检测。Qian 等人 [73] 使用属性控制流图构建漏洞搜索引擎。Devign [11] 采用门控图神经网络处理从源代码生成的多个有向图。然而,所有这些基于学习的方法都需要大量高质量的标注样本来实现良好性能。本文构建了一个仓库级高质量数据集 ReposVul,以促进模型训练。
6 结论
本文提出了一种自动化数据收集框架,并构建了首个仓库级漏洞数据集 ReposVul。我们的框架包括一个漏洞解缠模块,用于识别纠缠补丁;一个多粒度依赖提取模块,用于提取跨过程漏洞;以及一个基于追踪的过滤模块,用于识别过时补丁。ReposVul 涵盖了 1,491 个项目和四种编程语言中的 6,134 个 CVE 条目。经过全面的数据分析和人工检查,ReposVul 被证明比现有漏洞数据集质量更高且适用范围更广。我们的源代码及 ReposVul 数据集已公开发布,访问地址为:https://github.com/Eshe0922/ReposVul。