论文笔记:A Comprehensive Study of Bloated Dependencies in the Maven Ecosystem,Maven生态系统中膨胀依赖关系的综合研究,2020

A Comprehensive Study of Bloated Dependencies in the Maven Ecosystem,Maven生态系统中膨胀依赖关系的综合研究,2020

时间:2020/1,2021/3 published        页数:44

论文源地址:A comprehensive study of bloated dependencies in the Maven ecosystem | SpringerLink

与本篇论文相关联的续作:A Longitudinal Analysis of Bloated Java Dependencies-2105.14226

本文的工作重点:MAVEN,膨胀依赖

本文首次大规模量化研究分析了 Java的Maven包管理器中存在的膨胀依赖(W也称臃肿依赖,bloated dependencies)问题。

数量:分析了maven上9600个java工件,对30个著名开源项目进行了定性研究,表明75.1%的依赖是膨胀的,18/21个pull请求得到开发者的认可和合并,消除了131个膨胀依赖。

1.介绍

膨胀依赖会增大二进制文件代码的数量,同时也增加了攻击面,可能被嵌入可被利用的易受攻击的代码,而实际上对应用毫无帮助。 依赖关系膨胀的主要问题:最终部署的二进制文件包含的代码比需要的多。

近年来包管理器广泛采用,诸如 Java的Maven,Node.js的npm,Rust的Cargo 之前该团队做过相关的工作,见论文:

N. Harrand, A. Benelallam, C. Soto-Valero, O. Barais, and B. Baudry. Analyzing 2.3 million maven dependencies to reveal an essential core in apis. arXiv preprint arXiv:1908.09757, 2019.

主要工作:研究膨胀依赖关系的出现,研究DepClean工具分析Maven工件中的膨胀依赖项(bloated dependencies)。 由于手动分析Maven中上千个工件很困难,该团队设计实现了一个DepClean工具,能够自动分析Maven项目中的依赖项使用情况。

源代码+原始POM -> 【DepClean】 -> 删除了膨胀项的POM

2. 对maven库进行了一个简单的介绍

Maven:流行的包管理器和构建自动化工具,适用于Java项目,支持开发人员管理软件构建。依赖一个XML格式的特定配置文件pom.xml——包含有关项目信息,pom可以继承 base pom

Maven Project:项目 ,一组源代码文件及其配置文件。

Maven Artifact:工件,指已经编译好的Maven项目,已部署至外部库中可重用。maven中,工件常打包为JAR文件,其中包含Java字节码,公共API成员。 相关解决方案:依赖项解析机制是maven核心功能,分两步:

(1)确定构建单个工件所需的依赖项集;

(2)从外部库获取本地不存在的依赖项。maven构建了一个依赖书,表示已解析依赖关系的pom间的依赖关系。

三种依赖:

  • 直接依赖(direct):在项目或模块的pom文件中明确声明的依赖

  • 继承依赖(inherited):声明在父类pom文件中的依赖

  • 传递依赖(transitive):从直接依赖和继承依赖的传递闭包中获取的依赖

然后用JXLS依赖库为例,介绍了如何区别它的依赖

父pom:jxls-project

观察jcl-over-slf4j和slf4j-api,它拥有两个版本1.7.12和1.7.26,最终选择的是较低的1.7.12版本; maven使用最近胜利策略(nearest-wins strategy)来解决依赖版本冲突问题 文章的工作目标是确定是否真的需要maven项目类路径中的所有工件来构建运行项目

3.1 一些概念

定义1:Maven依赖图 (Maven Dependency Graph,MDG)

一个结点标记图,结点就是maven的工件(G:A:V),而边则表示依赖关系

定义2:Maven依赖树(Maven Dependency Tree,MDT)

有向无环图,以工件v为根节点 MDG每个工件都有自己的Maven依赖树(MDT),是工件所需所有依赖的传递闭包

定义3:膨胀依赖(Bloated Dependency)

如果MDT中,p和p的任意依赖d之间存在一条关联路径,但p没有直接或间接使用d的API中的任何元素,那么就称为存在膨胀依赖。 为了解释膨胀依赖关系,文章扩展了MDT,引入了边缘标签表示两种依赖的边缘状态:已使用(used)、膨胀的(bloated)

定义4:依赖使用树(Dependency Usage Tree)

是MDG的扩展,实现了依赖使用标记函数,该函数将以下六种一依赖使用类型之一分配给MDT中的maven工件以及其依赖之间的关系。

3.2 举例

分析jxls-poi中包含的字节码,可以找出哪些库未被使用到,进而安全的直接将bloated dependency从pom中删除。例如commons-codec和commons-collections,他们通过jxls-poi传递闭包包含在内,但从未直接在其字节码中使用,也从未间接通过API进行调用,因此这些膨胀对于构建jxls-poi是不必要的

3.3 DepClean:一种用于检测和删除膨胀依赖项的工具

DepClean GIT:

https://github.com/castorsoftware/depclean

实验:GitHub - ASSERT-KTH/depclean-experiments: Open-science repository containing our experiments about bloated dependencies in the Maven ecosystem with DepClean 输入:已构建的maven项目、一个工件存储库

过程:提取项目的依赖关系树,构造DUT识别项目实际使用的依赖关系集。

输出:

  • 返回一个报告,包含所有类型依赖项的使用状态;

  • 生成一个POM文件(POMd)的替代版本,删除了所有膨胀依赖项

算法一:检测和移除膨胀依赖的主要过程

line 1:首先复制p的POM文件,在本地解析所有直接和间接依赖

line 2:获得依赖树;如果p是个多模块项目的模块,那么它的parent POM声明的依赖项都被包含在直接依赖项中

line 3:构建一个p实际使用的依赖项集合

计算两个传递闭包:maven依赖树(line 2)和API调用图(line 3)

2种不同的方式删除膨胀依赖:

  • line9:如果在POM中显示声明膨胀依赖,那么删除它的声明子句;

  • line11:如果膨胀依赖是通过parent POM继承或者通过传递引入的,那么我们可以在POM中排除他。这种排除包括在直接依赖声明中添加一个<exclusion>子句。

算法二:获取一个maven工件全部已使用到的直接间接依赖

字节码分析,检测组件静态分析p的字节码及其所有依赖项,检查直接或间接引用了哪些API成员。

如何认定膨胀:如果一个依赖项d的API成员没有被调用,甚至连通过传递依赖项间接调用都没有,那么d就会被认为是膨胀的,可以删除。

原理

Depclean在Java中作为Maven插件实现,扩展了apache维护的 maven-dependency-analyzer 工具。 对于依赖书建立,depclean依赖于具有复制依赖关系和树目标的maven-dependency-plugin。depchean解决了java中静态分析的一些关键方面。它依赖于ASM库来访问所有的.class文件,以便在maven工件中注册对类方法、字段、注释的字节码调用。 定义了一个自定义的解析器,直接读取,class文件的常量池表中的条目,以防它包含ASM不支持的特殊引用。

允许插件静态地捕获基于字符串文字和连接地反射调用。Depclean位maven-dependency-analyzer 添加了特殊的功能,以检测和报告可传递和继承的膨胀依赖关系,并生成POM文件的去膨胀的版本。

4 实验方法

本节阐述研究问题,大规模分析。分为两个部分

第一部分,大规模定量研究

  • 问题1:膨胀依赖关系发生的频率有多高?量化研究9639个Maven artifacts

  • 问题2:重用实践如何影响到膨胀的依赖关系?考虑了两个不同方面:传递依赖导致的maven依赖树的额外复杂性,以及多模块架构选择

第二部分,重点关注30个著名的maven项目

  • 问题3:开发人员愿意在多大程度上消除膨胀的直接依赖关系?

  • 问题4:开发人员愿意在多大程度上消除膨胀的可传递依赖关系?

第一部分

下图是构建maven工件数据集的过程。步骤1、2用于收集一组具有代表性的maven工件;步骤3、4使用了depclean分析依赖项的使用情况。

步骤1,筛选工件 使用了先前的研究,随机选择14699个Maven工件的代表性样本,然后通过Shull的建议,对MDG每个工件的依赖性数量的概率分布进行采样来实现代表性。附加以下标准,最终产生了9639个工件的数据集

步骤2,分析依赖关系 从maven central下载所有选定工件的二进制文件及其POM,并将他们的所有直接和可传递依赖关系解析到本地存储库。丢弃了依赖于外部库的工件,丢弃了下载依赖项出现任何错误的工件。

步骤3,依赖使用分析 对于每个工件,首先解压缩JAR文件以及它的依赖。然后,对于每个JAR文件,使用depclean分析对API类成员的所有字节码调用。这对每个工件提供了一个依赖使用树(DUT),每个依赖关系都被标记成为了六个类别中的一个。

步骤4,收集依赖使用指标 依赖项使用的全局分布:使用类别的归一化分布 依赖项使用类型的分布:依赖关系总数的归一化比率 关于传递依赖项的数量 ......这里太多概念,就不一 一列举

第二部分 定性研究方案 RQ3 RQ4

系统选择了30个托管在github的著名开源项目进行分析 按星星数量进行排序的java项目,选择策略:

  • 可以使用maven成功构建

  • 最后一次提交是在2019年10月

  • pom中至少声明了一个依赖

  • 在README中有关于如何pull贡献的描述

  • 超过100颗星星

分析流程: (1)首先运行Depclean,尝试使用已删除的POM构建; 获取有关依赖使用情况报告,分析了编译和测试范围的依赖关系; 删除后,如果项目成功构建,则执行变化;如果构建失败,则不建议删除; (2)分析项目的历史,向开发人员提出变更; (3)建议在pom以pull请求形式进行更改; (4)讨论pull请求;

5 实验结果

MAVEN生态系统中膨胀的依赖项

RQ1: 依赖关系膨胀发生的频率有多高?

调研了9639个maven工件,723444个依赖,其中75.1%是膨胀的(bloated)

三种依赖关系

Bloated-direct,膨胀直接 举例ignite-zookeeper字节码,不存在对sl4j的任何API成员使用。在手动检查POM提交历史,发现是早期阶段进行使用,而后被专用记录器所取代,但其声明在POM保持完整

Bloated-inherited,膨胀继承 org.apache.drill:drill-protocol:1.14.0继承了其父POM org.apache.drill:drill-root:1.14.0的commons-codec和commons-io依赖项,然而并未使用

Bloated-transitive,膨胀传递 常发生在两种不同场景:(1)工件使用一些可传递依赖关系的API成员,而没有在他自己POM文件中声明;(2)可传递依赖是依赖树中另一个工件的必要提供者。 得出结论:直接膨胀较少,因为开发人员能直接进行维护;而传递较多;ut比bt中位数低,表明maven的默认依赖项解析机制不是最优

RQ2:重用实践如何影响膨胀的依赖关系?

传递依赖 大部分依赖树高度是4

单模块vs多模块

RQ1-RQ4 Answer 

6 讨论

6.1 结论

膨胀的依赖关系代表了不必要的额外漏洞来源,为潜在的攻击者打开了新的大门。依赖项管理系统的复杂性是一个关键工程问题,因为依赖项问题可以扩散到整个生态系统。 Depclean可以补充安全扫描解决方案,以确定是否可以删除依赖项。Maven建议开发人员通过POM显示声明来控制依赖,是很好的一个实践。 Java9中引入模块构造提供了比包更高级的聚合,可以有助于减少依赖关系的传递爆炸。 研究表明,即使是由严格的开发规则开发社区维护的项目,也会有依赖膨胀的情况。

6.2 有效性威胁

depclean构造的DUT可能不完整,因此在解决依赖关系会出现问题。 depclean检查臃肿依赖关系的有效性,java动态特性例如反射和动态类加载,对java源代码分析提出了特别的挑战,由于depclean静态分析字节码,没有进入字节码的内容就不会被检查到(例如常量、连接、带源代码的注释),有可能会导致误报。 目前仅针对java的maven生态进行分析,可以考虑其他的依赖关系包进行推广研究。

7 相关工作

开发人员考虑到删除带来的额外的问题(例如维护排除列表、不稳定性、不兼容性),部分开发人员倾向于保留膨胀 膨胀依赖带来的风险:增加复杂度、增加攻击面、降低了整体性能,容易出现漏洞/植入后门

7.1 软件膨胀的分析和缓解

Celik et al[8]提出了Molly,这是一个构建系统,可以在CI环境中惰性检索依赖项并减少构建时间。对于所研究的项目 Yu等人[43]调查了大型C项目头文件中不必要的依赖项的存在。他们的目标是减少构建时间。他们提出了一种基于图的算法,以静态地从应用程序中删除未使用的代码。他们的结果显示,增量构建减少了89.70%的构建时间,新构建减少了26.38%。我们的工作并不关注构建性能,我们分析了Maven包的庞大现代生态系统中依赖膨胀的普遍性。 大多数的清除技术都是建立在静态分析的基础上的[17],有一些激进的利用了先进的动态分析技术删除代码[14]

7.2 软件生态系统的依赖管理

Jezek等[16]用实际例子描述了Maven中由传递依赖关系引起的问题。他们提出了一种静态分析方法,用于查找依赖关系中丢失的、冗余的、不兼容的和冲突的API成员。他们基于29个Maven项目的数据集进行的实验表明,与传递依赖相关的问题在实践中很常见。 他们认为使用错误的依赖关系作用域是造成冗余的主要原因。我们的定量研究将这项工作扩展到Maven中央生态系统的规模,并提供了关于依赖冗余问题持久性的额外证据。

附加 启发与思考

Q:膨胀依赖检测是否适用于python、javascript、Ruby、C++等其他编程语言之中呢?

Q:如何通过包管理器MAVEN实现的依赖的遍历?

Q:它的实验方法中,Depclean如何静态分析的JAVA字节码,并通过字节码找到未被使用的(bloated)依赖的?

Q:如果使用动态方法,是否会比静态更好呢?(但是动态要比静态难)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值