Abstract

摘要——第三方库在软件开发中发挥着关键作用,因为它们可以减轻开发人员重新实现常见功能的沉重负担。 但是,第三方库和客户端项目是异步发展的。 因此,客户端项目中可能会使用过时的第三方库,而开发人员却没有意识到潜在的风险(例如,安全漏洞)。 过时的第三方库可能会延迟更新客户端项目,开发人员可能不太了解更新中的潜在风险(例如,API 不兼容)。 第三方库的开发者可能不知道他们的第三方库是如何在客户项目中使用或更新的。 因此,对开源项目中第三方库的使用、更新和风险进行定量和整体研究,可以为这些问题提供具体证据,并为改善生态系统提供实用见解。
在本文中,我们在 Java 生态系统中做出了这样的研究。 特别是,我们对 806 个开源项目和 13,565 个第三方库进行了库使用分析(例如,使用强度和过时度)和库更新分析(例如,更新强度和延迟)。 然后,我们对 806 个开源项目和 544 个安全漏洞进行了库风险分析(例如,使用风险和更新风险)。 这些分析旨在从两个整体角度(即开源项目和第三方库)量化使用和更新实践以及使用和更新过时第三方库的安全漏洞的潜在风险。 我们的发现对开发人员和研究人员在维护第三方库(例如,智能警报和过时第三方库的自动更新)方面的问题和潜在解决方案具有实际意义。 为了表明我们发现的有用性,我们设计了一个智能警报系统,用于帮助开发人员在更新第三方库时做出自信的决定。 33 和 24 个开源项目在收到我们的警报后确认并更新了第三方库。
索引词——过时的库、安全漏洞

I. INTRODUCTION

第三方库允许开发人员重用常用功能,而不是重新发明轮子,并大大提高开发人员的工作效率。 与第三方库给软件开发带来的好处相比,由于第三方库和客户端项目之间的异步演化,会出现一些问题。 从客户项目的角度来看,过时的第三方库可以通用但很少更新,或者更新延迟。 开发人员需要在软件维护上投入大量成本,以使第三方库保持最新。 旧的第三方库版本包含错误,可能会导致客户端项目崩溃或增加攻击面。 新的第三方库版本重构代码、修复错误并添加功能,这可能会破坏库 API [12, 25]。 更糟糕的是,开发人员缺乏有效的机制来意识到使用中的这些潜在风险和更新第三方库。 从第三方库的角度来看,开发者也缺乏有效的渠道来了解他们的第三方库在客户端项目中的使用和更新情况。 结果,这些信息无法反馈到库开发周期以改进其设计。

为了为这些问题提供具体和全面的证据,需要对开源项目中第三方库的使用、更新和风险进行定量和整体研究。 一方面,研究应该定义指标来量化使用、更新和风险,以便具体揭示问题的严重性。 另一方面,研究应该从所有相关方(即开源项目和第三方库)的角度出发,以便对生态系统进行整体视图描述。 尽管已经在 Java 生态系统中提出了几项研究,但没有一个可以贡献这样的研究。 例如,一些研究测量了不同粒度的第三方库的使用流行度(例如,版本 [27、35]、类 [15、23、36] 和方法 [29、42]); 一些研究调查了过时的第三方库的使用情况 [26, 28]; 一些研究探讨了更新或不更新第三方库的原因 [11, 28]。 但是,他们只是从第三方库的角度分析使用或更新; 未能表征使用和更新第三方库的严重性和风险,例如使用的第三方库过时、更新第三方库的延迟、使用的第三方库的安全漏洞、更新时的库方法不兼容 第三方库。 这种情况隐藏了维护第三方库的问题,阻碍了实际的解决方案。

为了可持续地改善这种情况,本文通过回答三个研究问题,有助于定量和整体地描述 Java 生态系统开源项目中第三方库的使用、更新和风险:

  1. RQ1:Library Usage Analysis。What is the usage intensity and usage outdatedness of third-party libraries?
  2. RQ2: Library Update Analysis. What is the update intensity and update delay of third-party libraries?
  3. RQ3: Library Risk Analysis. What is the potential risk in using and updating outdated third-party libraries?

我们对 806 个维护良好的 Java 开源项目和 13565 个第三方库进行库使用分析和库更新分析,对 806 个项目和 13565 个第三方库中的 544 个安全漏洞进行库风险分析。 这些分析是基于制定的指标,本研究中的数据爬取和分析在台式机上花费了大约四个月的时间。

通过这些分析,我们旨在为开发人员和研究人员提供有用的发现。 例如,33.0% 的项目有超过 20% 的方法调用库 API。 60.0% 的库最多有 2% 的 API 跨项目调用。 37.2% 的项目在不同模块中采用同一个库的多个版本。 54.9% 的项目使超过一半的库依赖项从未更新。 50.5% 的项目更新延迟超过 60 天。 68.8% 的库版本包含安全漏洞。 35.3% 的有缺陷的库版本在安全版本中删除了 300 多个 API。

我们的发现有助于发现维护第三方库的问题,量化这些问题的重要性以引起生态系统的关注,并使后续研究能够解决这些问题; 例如,生态系统级别的知识图谱,以及过时库的智能警报和自动更新。

为了证明我们发现的有用性,我们提出了一个安全错误驱动的警报系统,为开发人员提供多个细粒度的信息,以便对第三方库版本更新做出自信的决定。 我们的初步评估表明,在 451 个采用有 bug 的第三方库版本的开源项目中,有 89.6% 是安全的。 对于 38 个不安全的开源项目,我们量化了风险和更新工作。 33 和 24 个开源项目在收到我们的警报后确认并更新了有缺陷的第三方库。

综上所述,本文做出以下贡献:

  1. 我们进行了大规模的实证分析,以定量和整体地分析 Java 开源项目中第三方库的使用、更新和风险。
  2. 我们为开发人员和研究人员提供了实际意义,发布了我们的数据集,并提出了一个原型系统来证明我们的发现的有用性。

II. RELATED WORK

Usage Analysis
Update Analysis
Risk Analysis

III. EMPIRICAL STUDY METHODOLOGY

A. Study Design

我们在 RQ1 中的库使用分析系统地分析了项目中当前使用的库。 它首先调查项目对库的依赖程度(即从项目角度来看的使用强度)以及跨项目使用库的程度(即从库的角度来看的使用强度)。 它旨在量化在项目开发中使用库的重要性以及不断发展的库的影响。 然后,它从项目和库的角度调查采用的库版本与最新版本的差距(即使用过时)。 它旨在量化项目中采用过时库的普遍性和严重性,并激发 RQ2 的必要性。

我们在 RQ2 中的库更新分析系统地分析了项目中的历史库版本更新。 它首先探讨了项目更新库版本的密集程度(即,从项目角度来看的更新强度)以及跨项目更新库版本的密集程度(即,从库的角度来看的更新强度)。 它旨在量化更新库版本的做法。 然后,它从项目和库的角度测量库版本更新滞后于库发布的时间(即更新延迟)。 它旨在量化开发人员对新库发布的反应时间,并激发 RQ3 的必要性。

我们在 RQ3 中的 library 风险分析系统地调查了 library 中的安全漏洞。 它首先测量一个项目使用了多少错误的库版本(即从项目的角度来看的使用风险)以及在一个库版本中存在多少安全错误(即从库的角度来看的使用风险)。 它旨在量化使用过时库和延迟库版本更新与安全漏洞相关的风险。 然后,它测量一个项目调用有多少错误库版本中的库 API(即,从项目角度来看的更新风险)以及有多少错误库版本中的库 API 与安全库版本不同(即,从角度来看更新风险 library )。 它旨在根据潜在的 API 不兼容性来量化更新有缺陷的库的风险。

B. Corpus Selection

我们对选自 GitHub 的 Java 开源项目语料库进行了这项研究。 我们专注于 Java,因为它被广泛使用,因此我们的发现可能对更广泛的受众有益。 具体来说,我们首先选择了 200 星以上的非分叉 Java 项目,以保证项目质量,初步形成了 2216 个项目。 在这些项目中,我们选择了使用 Maven 或 Gradle 作为自动构建工具的项目,以简化项目中声明的库依赖项的提取,这将我们的选择限制在一组 1,828 个项目中。 我们选择了在过去三个月内提交的活跃且维护良好的项目,目的是为了以牺牲全球普遍性为代价,更倾向于对我们的发现进行局部普遍性。 鉴于我们的调查结果,维护良好的项目的受众可能更具可操作性,而包括不活跃的项目会对他们产生较少代表性的调查结果。 最后,我们有 806 个项目,记为 P。我们使用具有 2.29 GHz Intel Core i5 CPU 和 8 GB RAM 的桌面从 GitHub 爬取了他们的存储库和提交到 master 分支。 我们在同一个桌面上进行了库爬取和库分析,总共耗时四个月。

IV. LIBRARY USAGE ANALYSIS

为了研究库的使用,我们开发了 lib-extractor 以在提交中从每个项目的配置文件(即 pom.xml 和 build.gradle 用于 Maven 和 Gradle 项目)中提取库依赖项。 Maven 和 Gradle 支持各种机制(例如,继承、版本范围和变量扩展)来声明库依赖项,我们在 lib-extractor 中支持它们。 基本上,对于 Maven 项目,lib-extractor 通过解析三个字段来提取库依赖:groupId、artifactId 和 version; 对于 Gradle 项目,它通过解析类似字段来提取库依赖项:组、名称和版本。

库依赖 d 表示为 4 元组

  • p : project
    • p.date : 项目被爬取的日期
  • f : configuration file
  • com : commit
    • com.date : commit 的提交日期
  • v :
    • l : library - < group, name >
    • ver : version number of l

我们对 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图1 中每个项目的最新提交运行 lib-extractor,得到 164,470 个库依赖项,记为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图2,24,205 个库版本,记为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图3= {d.v | d ∈ 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图4},以及 13,565 个库,表示为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图5 = {v.l | v ∈49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图6}。

A. Usage Intensity

Definition. 我们从项目和库的角度在细粒度级别定义使用强度:

  • 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图7,项目 p 调用库 API 的方法的百分比,
  • 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图8,跨项目调用的库 l 的 API 的百分比。

与以前的研究不同(例如,[23、27、35、36]),我们在方法级别探索库的使用。 最接近的工作来自 Qiu 等人。 [42],但只考虑 JDK 库。

为了计算 usip 和 usil ,我们需要提取库API、项目方法和项目方法中的API调用。 我们从配置文件中声明的库存储库(例如,Maven 和 Sonatype)中爬取了每个库版本 v ∈Vus 的 jar 文件。 我们成功爬取了 16,384 个库版本的 jar 文件,表示为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图9,但其他 7,821 个(32.3%)库版本失败了。 主要原因是快照版本(76.1%)的jar文件不再可用; 其他原因是不再可用的非常旧的库版本以及我们无权访问的私有库。 从 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图10,我们确定了7,229个库,表示为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图11

然后,我们在 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图12的 jar 文件中使用 Soot [48] 来提取库 API,表示为 A。每个库 API a ∈A 表示为 2 元组

  • v : v ∈ 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图13
  • api : library API

在这里,我们保守地将公共类中的公共方法和字段视为库 API。 接下来,我们使用 JavaParser [46] 对项目存储库和使用的库版本的 jar 文件进行类型绑定,以提取项目方法,表示为 M,以及项目方法中的 API 调用,表示为 C。每个项目方法 m∈M 表示 为二元组

  • p : project
  • method : method in p

每个 api 调用 c ∈ C 表示为二元组

  • a : a∈A library API
  • m : m∈M denotes the project method where a is called.

最终我们得到公式
image.png

Findings.使用 usip 和 usil,我们在图 1a 和 1b 中显示了跨项目和库的使用强度分布,其中 y 轴分别表示使用强度落在某个范围内的项目和库的数量。 一方面,74 个(9.2%)项目不调用库 API; 即,28 个项目不使用库,5 个项目使用不可用的库版本,41 个项目仅使用 jar 文件中的资源文件。 265 (32.9%) 个项目最多有 10% 的方法调用库 API。 266 (33.0%) 和 64 (7.9%) 项目分别有超过 20% 和 40% 的方法调用库 API。 另一方面,4,337 (60.0%) 个库最多有 2% 的 API 被跨项目调用; 只有 281 (3.9%) 个库有超过 40% 的 API 跨项目调用。 请注意,733 库没有类文件,而只有 jar 文件中的资源文件(例如,Angular 仅包含 Web 资产),并且不包含在图 1b 中。image.png

Summary. 项目通常对库 API 有适度的依赖。 这种具体的使用依赖性也反映了库维护所需的努力(例如,库更新和迁移)。 大多数库中只使用了很小一部分库 API。 库开发人员应利用此类使用统计数据来指导 API 发展。
项目开发人员可以定制未使用的库功能。

Summary. 快照库版本和同一个库的多个版本常用在三分之一的项目中。 由于 API 不兼容,从长远来看,它们可能会增加维护成本。 当模块相互依赖时,后者甚至可能导致依赖冲突。因此,需要工具来更好地管理它们。

B. Usage Outdatedness

Definition. 我们将库依赖项 d 的使用过时度定义为 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图16,表示为在存储库爬网时具有更高版本号的库版本数。

对于每个库 l ∈ 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图17,我们从库存储库中抓取 l 的所有库版本的版本号和发布日期。 我们有 288,312 个库版本,用 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图18 表示。 每个库发布 r ∈Rus 表示为 2 元组

  • v :version
  • data : v’s release date.

使用 Dus 和 Rus,我们得到公式:
image.png

然后,我们从项目和库的角度定义使用过时度:

  • 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图20,项目p中库依赖的平均使用过时度,
  • 49 | An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects - 图21,库l上的库依赖的平均使用过时度。

我们得到公式:
image.png
Findings. 使用 usop 和 usol,我们在图 2a 和 2b 中报告了跨项目和库的使用过时分布。 一方面,只有 28 个(3.5%)项目使用最新的库版本。 83 个 (10.3%) 项目使用的库平均处于最远离最新的两个版本。 306 个 (38.0%)、118 个 (14.6%) 和 19 个 (2.4%) 项目采用平均分别比最新版本相差 10、20 和 50 个版本的库。 图 2a 中没有包含 33 个项目,因为 28 个项目不采用库,并且 5 个项目中的库版本的 jar 文件都不再可用。 另一方面,在所有使用它们的项目中,3,269 (45.2%) 个库已经是最新的,1,419 (19.6%) 个库平均与最新版本相差最多两个版本,1,025 个 (14.2%) 和 134 个 ( 1.9%) 库平均分别与最新版本相差 10 和 50 多个版本。

image.png

Summary. 每个项目几乎都采用过时的库。 与最新版本的距离通常相当大。 需要有机制让项目开发人员意识到过时库的风险(例如,安全漏洞)或新版本的好处(例如,新功能),同时允许库开发人员直接向采用其库的项目通知此类风险和好处。

V. LIBRARY UPDATE ANALYSIS

A. Update Intensity

image.png
Summary. 项目开发人员会更新库。 尽管如此,仍有一半的项目留下了超过一半的被采用的库从未更新过; 超过一半的使用它们的项目中有三分之一的库没有更新。 考虑到所选项目维护良好,更新实践相当差。 应该提高对更新 library 重要性的认识。

image.png

Summary. 没有严格遵循语义版本控制。 需要工具来分析在新版本发布之前是否引入了任何不兼容的更改,并建议遵循语义版本控制的正确版本号。 此外,主要版本应该有一个保持更新的机制。

B. Update Delay

image.png

Summary. 项目开发人员对新的库版本反应迟缓。 如此宽的时间窗口可能会增加使用过时库的风险(例如,安全漏洞),甚至会增加更新到新版本的难度,因为在此时间窗口内将使用更多的库 API。

VI. LIBRARY RISK ANALYSIS

image.png

Summary. 超过一半的项目使用包含安全漏洞的库版本。 三分之二的库版本包含安全漏洞。 安全漏洞的相对普遍存在表明,如果项目开发人员不了解已使用库中的安全漏洞或延迟库更新,项目面临的潜在风险。

image.png

Summary.这些 API 级别的使用上限和更新风险在使用和更新有缺陷的库版本时存在中等风险。 需要工具通过组合项目中使用的库 API 和在安全版本中更改的库 API 来提供严格的风险估计,以便开发人员可以自信地更新有缺陷的库。

VII. DISCUSSION

A. Implications to Researchers and Developers

  • Ecosystem-Level Knowledge Graph
  • Library Debloat
  • Multiple Version Harmonization
  • Smart Alerting and Automated Updating
  • Usage-Driven Library Evolution

B. Application for Usefulness Demonstration

C. Threats to Validity

  • Indirect Library Dependencies
  • Subject Representativity

VIII. CONCLUSIONS

我们进行了一项定量和整体研究,以描述 Java 开源项目中第三方库的使用、更新和风险。 具体来说,我们从开源项目和第三方库的角度量化了使用和更新实践,分析了第三方库的安全漏洞风险。 我们就维护第三方库的问题和补救措施向开发人员和研究人员提供了启示。 我们为有缺陷的库设计了一个安全漏洞驱动的警报系统原型。 我们在 https://3rdpartylibs.github.io 上发布了我们的数据集和工具的源代码