软件成分分析(Software Composition Analysis,SCA)是通过对软件系统所使用组件的版本及依赖关系分析,对所引用的三方组件检测,识别已知组件漏洞及授权许可风险,规避开源代码所带来的合规性、安全性、知识产权等风险。同源检测是SCA的重要基础。SCA具有纵深代码同源检测核心能力,可以精准识别开发过程中引用的第三方开源组件,通过应用成分分析引擎多维度提取开源组件特征,计算组件指纹信息,挖掘组件中潜藏的各类安全漏洞及开源协议风险,满足供应链安全审查、软件合规性审查、第三方组件安全管控等行业应用场景需求。
代码同源检测是基于源代码文件维度,面向源代码,对软件进行同源性分析,即对软件进行成分分析、溯源分析、已知漏洞分析、恶意代码分析,析出所引用开源软件及关联信息。按照分析精度,可将其分为文件级、函数级、代码片段级3个层级,主要用于源代码片段与项目中其他代码片段或开源代码所存在的相同成分检测。同源检测原理及实现如图5-45所示。
图5-45 同源检测原理及实现
同源检测也称为代码克隆。代码克隆是指本地或开源代码库中存在着多个相同或相似的代码片段。使用克隆代码不仅可能引入代码片段存在的安全或授权许可风险,而且随着软件系统在敏捷开发模式下不断迭代,代码克隆将造成代码库急剧膨胀,如果管理不善,不仅会增加维护成本,而且软件缺陷会随着代码克隆在系统中传播,带来不可预知的可靠性、安全性问题。
代码克隆包括图5-46所示的完整克隆、重命名克隆、增删改克隆、自实现克隆4种类型。
完整克隆是指完全相同的两个代码片段,但不包括注释和空白符;重命名克隆是指对代码变量、类型、文件及函数名等进行修改,但两个代码片段的逻辑内容一致;增删改克隆是指在重命名克隆的基础上,增加、删除、修改相关语句或对源代码内容布局进行调整,两个代码片段内容相似;自实现克隆是指两个代码片段的逻辑功能相同,但编码实现方式不同,如通过替换同类型函数或表达式,但时间复杂度和输入输出一致。
图5-46 代码克隆分类及含义
对于测试方法而言,完整克隆、重命名克隆、增删改克隆主要通过文本相似性检测技术实现,自实现克隆通过功能相似性检测技术实现。此4种克隆类型,从前至后,检测难度渐进增加。
5.9.2.1 代码克隆检测算法
(1) 基于文本的代码克隆检测: 删除源代码中的空格及注释,基于预处理的源代码,使用文本相似度算法,通过比较文本中的词语、词法等不同结构信息,判断两组文本的相似性。基于源代码比较的文本相似度检测,能够覆盖完整克隆和重命名克隆两种类型。但如果将源代码特征及意义作为文本处理,可能导致信息丢失。当提取源代码特征指纹之后,源代码比较就简化成两个字符串文本的比较,这时利用特殊算法就能实现对增删改克隆的覆盖。如果能够实现基于自然语言处理的相似度算法,理论上也能覆盖自实现克隆。
(2) 基于令牌的代码克隆检测: 使用词法分析器将源代码划分为令牌序列,然后在令牌序列中找到相似的子序列并进行比较。基于令牌的检测方法对源代码进行词法分析,符号序列符合编译原则,源代码信息得到充分利用,但缺乏对代码语法和语义的分析,对增删改克隆、自实现克隆的检测效果并不理想。
(3) 基于树的代码克隆检测: 将源代码表示为抽象语法树或代码解析树,使用树匹配算法找到相同或相似子树,检测克隆代码。该方法对源代码进行语法分析,进一步提高了对源代码信息的利用,可以更好地适用于增删改克隆检测,且检测精度显著提高。
(4) 基于度量的代码克隆检测: 提取源代码的代码数量、变量数量、循环数量等特定的索引指标,将它们抽象到特征向量,确定基于特征向量之间的距离。
(5) 基于图的代码克隆检测: 基于图的代码克隆检测方法是利用源代码的语法结构及语义信息,将源代码转换为由数据流图和控制流图组成的程序依赖图,通过寻找齐次子图实现克隆检测。这种方法能够检测自实现克隆。但程序依赖图生成算法及同构程序依赖图子图匹配方法的时空复杂度高,基于图的代码克隆检测还无法应用于大型复杂系统的代码克隆检测。
5.9.2.2 同源检测技术
(1) 代码溯源分析: 代码溯源分析是基于相似哈希精准匹配的代码特征提取方法,对代码特征进行整合、计算后生成代码指纹信息,基于代码级大数据指纹库关联、匹配和分析,溯源目标代码引用的第三方开源项目信息,包括匹配项目的文件及代码行,结合第三方开源项目声明许可证,分析开源代码的引入是否存在兼容性、合规性等风险。
(2) 已知漏洞分析: 在软件开发过程中引入的第三方开源代码,可能存在被攻击者利用的已知漏洞。《2021年开源软件供应链安全风险研究报告》指出:超过80%的漏洞文件在开源项目中存在同源文件,漏洞文件影响的范围在开源项目的传播下放大了约54倍。通过同源检测技术识别出与当前代码同源的开源项目,结合漏洞信息库,可以检测当前代码是否来自有漏洞的开源项目,是否来自开源项目有漏洞的版本,是否涉及漏洞相关的代码。
(3) 恶意代码文件分析: 不少安全事件是由于攻击者有意在开源社区提交恶意代码并发布更新,或在开源项目中添加恶意依赖事件,或滥用软件包管理器分发恶意软件等所致。例如,在Mode JS仓管理和分发工具NPM包中,eslint-scope因黑客盗用开发者账号发布包含恶意代码的版本,event-stream因黑客混入项目维护者中,在项目中添加恶意依赖事件。SCA通过从源代码中提取敏感行为函数的特征数据,与提前收集的恶意代码特征数据进行比对,识别源代码中的恶意代码。
(4) 冗余代码检测: 在敏捷开发模式下,不同开发人员负责不同应用开发,可能存在着相同功能代码的实现。通过代码同源检测,识别同类型冗余代码,有利于消除和管理代码重用以及生命周期过程中的升级维护。通过对代码进行相似性检测,可以将相似代码整合成为一个SDK包,除了便于统一维护,也可供其他研发人员使用。
(5) 片段代码风险检测: 片段代码风险检测是指在一个代码库或项目中,识别出具有潜在安全风险或漏洞的代码片段,发现和修复潜在的安全问题。使用三方开源组件实现关键功能、性能时,通常是根据业务需求,对开源组件进行二次开发,而非直接使用。针对二次开发的开源组件,代码同源检测可通过缺陷代码片段表征,对开源组件及缺少版本特征的脚本代码进行组件漏洞关联。例如,对基于jar扩展名和开源组件进行组件漏洞关联时,主要通过版本号和组件指纹标识进行分析判断,但二次开发后的开源组件破坏了一部分指纹,可能导致无法识别。
(6) 代码知识侵权审核: 开源组件并非自由组件,其使用需要严格遵守开源许可协议,在违背开源项目作者授权意愿的情况下使用其克隆代码,应受开源项目许可协议约束。代码知识侵权是指在编码实现过程中,抄袭或复制他人的代码或算法,侵犯其知识产权,需要定期梳理检查开源组件代码的使用情况,尤其是针对脚本型语言的使用,规避开源许可协议风险。
(7) 安全编码执行溯源: 在安全开发体系建设过程中,安全编码规范是保证编码质量的重要依据。对于标准安全编码的使用,可通过代码同源检测技术进行审核,检查标准安全编码的应用情况,确保应用具备基础的安全性和健壮性。
(8) AI生成代码检查: 随着ChatGPT的爆发式应用,开发人员逐渐开始使用该技术自动生产代码。但由于AI本身收录样本学习,其自动生产的代码可能存在克隆。代码同源检测在代码生成时,能帮助提升安全风险审计能力。
代码克隆检测包含代码格式转换和相似度检查。不同代码克隆检测工具,其技术实现原理具有一定的差异性,但其主要执行流程大致相同。代码克隆检测原理及流程如图5-47所示。
(1) 开源/闭源代码库: 作为代码克隆检测的主要知识库,收集足够完整的开源/闭源代码项目,利用特定算法形成知识库特征表集合;作为检测目标,通过预处理移除无意义的代码片段并进行转换,执行特定的相似性比较,获得克隆检测结果。
(2) 预处理与转换: 针对软件供应链的安全检测场景,对缺陷代码、主干核心代码进行预处理,去除无意义的代码并进行标准化,通过将源代码分为不同片段,转换为可比较的单元。
图5-47 代码克隆检测原理及流程
(3) 源代码表征: 将源代码表征为文本,或进一步利用符号进行表征,将源代码转换成抽象语法树,用于存储记录或后续比较验证。
(4) 代码相似度比较: 将每一个代码片段与其他代码片段进行对比,找到代码克隆,比对结果以克隆对列表的方式呈现。相似度比较的算法在很大程度上由源代码表征方式决定。例如,如果将AST作为一种源代码表征方式,则这类源代码表征方式将决定选用何种相似度算法。
(5) 代码克隆结果整合: 将在前几个步骤获得的代码克隆和原始的源代码关联起来,以适当方式呈现。将检测结果提交给需求方,即提供给源代码所有者参考,要求整改或删除。
(6) 提供支撑: 为软件成分分析、自主可控率评价提供支撑。
5.9.4.1 开源代码获取
基于并行计算、分布式爬虫系统以及数据发现、数据抓取、数据抽取、持续更新,实现开源代码一站式转码、抽取及解析。数据发现旨在区分开源、闭源及混源代码差异,甄别有效的开源代码,提取不同代码类型,获取高价值代码,是代码的网络发布平台特性;数据抓取确保抓取行为符合浏览器要求,保证所抓取数据的完整性,是获取代码数据的行为方式;数据抽取是基于机器学习与规则实现的通用提取方案以及基于平台项目代码的结构化提取方案,基于代码数据清洗,过滤并修改不符合要求的数据,甄别出高价值的代码数据,加工提取原始开源代码的有效信息;持续更新是对于实时更新或停止更新的开源代码数据,制定数据更新机制以及已有代码数据库与候选更新代码的合并策略。
5.9.4.2 开源代码存储
基于面向列的分布式数据库,通过大规模可伸缩分布式处理,实时、随机访问超大规模数据集,基于自底向上的构建,通过增加节点实现线性扩展,保证高吞吐量和高容错性,提供高效、安全的数据结构序列化、存储和检索。为应对超大规模的数据量,通过顺序存储行及每行中列的方式,将插入性能与表的大小分割开,消除索引膨胀。当数据量持续增长到阈值时,表自行分裂成区域,并分布到可用节点上。通过一致性检验,确保数据复制和冗余过程中不发生偏差。面对海量代码,通过批处理方式,并行进行分布式作业,采用映射和规约调度策略,快捷、高效地完成数据读写。
在分布式数据库中,数据被分割至服务器集群并保存在冗余文件系统中,通过容错及安全机制,保证数据安全。第一种安全机制是通过心跳机制维持主节点和数据节点之间的有效联系,避免网络故障等导致错误操作,当数据节点无法发出心跳数据包时,则不派发后续操作,并认定该数据节点的数据无效。第二种安全机制是记录所有区块的校验和,当新操作发生时,对区块进行完整性检测,若不一致,则自动地从其他数据节点获取副本。第三种安全机制是对每个节点进行负载均衡检测,当节点动态变化导致数据分布不均匀时,自动均衡各节点上的数据。基于文件垃圾箱缓冲,判断只有超过阈值时才进行正式移除,这是一种工程上常用的安全机制。
5.9.4.3 开源代码管理
在部署过程中,若通过手工配置,将耗费大量时间资源。而自动部署,尤其是在应用、环境和部署流程相对复杂的情况下,对部署的应用、环境和流程建模得到一个良好的自动化部署系统,能够显著提高部署效率。
在海量数据爬取、存储、检索过程中,可能产生数据错误,对每个数据节点周期性发送心跳信号,通过心跳信号缺失检测网络割裂是否导致部分节点失联,若未发送心跳信号,标记为宕机,不再向其发送新的I/O请求。任何存储在宕机上的数据不再有效,宕机可能会引起一些数据块的副本系数低于指定值,节点不断地检测这些需要复制的数据块,一旦发现需复制的数据块就启动复制操作。
在数据读取过程中,因为数据节点存储设备、网络系统故障以及软件失效等可导致某个节点数据损坏。当对一个数据节点创建新文件时,计算该文件每个数据块的校验和,将校验和作为一个单独的隐藏文件保存在同一名字空间下。当获取文件内容时,检验从该数据节点获取的数据及相应校验和与文件中的校验和是否匹配,如果不匹配,则从其他节点获取该数据块的副本。
在数据维护过程中,需要对数据进行备份和容灾处理。一般地,数据备份策略包括:在备份机上建立主数据库拷贝;对所需跟踪的重要目标文件的更新进行监控和追踪,并通过网络将日志实时传送到备份系统;当数据库内容被修改后,按时间间隔周期性地将某个特定时刻的数据进行复制备份。当然,数据恢复也是一个重要问题,可以利用数据快照,将其恢复至过去一个数据未损坏的时间节点。
在数据监控过程中,在系统中设置编辑日志,记录访问请求及发生的错误,实时监控并记录系统行为,支持维护人员理解故障原因,进行故障定位,有助于检查集群的健康状况。日志监控是一种有效的监控手段。设定合理的数据均衡策略以维持集群均衡。例如,某个节点上的空闲空间低于特定的临界点,系统就会自动地将数据从该数据节点移动到其他空闲节点。当对某个文件的请求突然增加时,启动一个计划创建该文件新的副本,重新平衡集群中的其他数据。
5.9.4.4 代码大数据库设计
1.基本信息结构化设计
基于代码物理和逻辑实体的概念关系,从高层设计出发,自上而下,确保基础数据规整及代码分析、处理的有效性。基于开源项目,将实体及属性设计为表。表结构包括两部分:一部分以开源项目的原始信息为基础,其内容来源于爬取的GitHub上的项目信息;另一部分以所爬取的项目信息为基础,对项目进行分析的基础数据表。
(1)开源项目原始信息数据表包含repository、releases、commit、user共4个表,用以存储软件开发过程的项目、人员等信息。
(2)开源项目分析数据表包含file、function、function_token共3个表,用于存储开源项目经过预处理后的信息。
2.非结构化数据存储组织
Hadoop 以可靠、高效、可伸缩的方式进行数据处理,维护多个数据副本,确保能够对失败的节点重新进行分布式处理;通过挂载磁盘增加机器修改配置,动态调整数据存储容量及处理能力。面向代码大数据库的Hadoop分布式架构采用iscsi 服务方式,确保底层存储可以通过增加磁盘动态增加存储容量,双NameNode互为主备方式,提升集群的高可用性及可靠性。图5-48给出了一个代码大数据的分布式存储架构。
图5-48 代码大数据的分布式存储架构
与每个克隆实例关联的信息包括项目信息CC_Repository、版本信息CC_Release、克隆关联文件信息CC_File、克隆组信息CC_Group,这些信息独立成表。其中,克隆组信息是基于相对独立的克隆组检测算法,通过扫描每次克隆检测得到的克隆对关系,归纳得到构成克隆组的克隆实例,这是一个求最大图的问题。在更加简化的算法中,生成克隆对的同时直接生成克隆组。将每条克隆实例信息存储在表CC_ClassInstance中,以便代码演化和族谱分析。该表包括克隆组ID和代码块ID。克隆信息元模型及基础表结构如图5-49所示。
图5-49 克隆信息元模型及基础表结构
对每次克隆检测的设置、过程及结果,根据元模型进行持久化管理,导出代码克隆信息数据库结构。将每次克隆检测建模为实体集,用一个检查ID对每次检测标记,记录检测日期和时间。检测项目ID及项目的不同版本ID,分别用于表示本次所检测项目及项目的不同发布版本。
理论上,克隆检测结果包括克隆类和克隆实例两个层次。受制于克隆类计算的复杂性,大多克隆检测算法仅生成克隆对。克隆对是大部分克隆检测算法最直接的输出,基于克隆对,可以通过相对独立的算法计算克隆类。克隆数据库设计中保留了基本的克隆对存储,每个克隆对涉及两个块,以块ID表示。在数据库中,一个克隆对的两个块ID,其值小的ID在前、大的ID在后,由此得到克隆对信息表CC_ClonePair,为每个克隆对编号ClonePair_ID以及每个克隆对所对应代码块编号Block_ID1和Block_ID2。
5.9.6.1 多层次、多参数克隆检测
将每次克隆检测行为作为一次检测实体存入数据表CC_Detection中,用一个Detection_ID表示每次不同检测行为。假设检测粒度为函数级,相似度为80%,数据表CC_Detection对应字段Detection_ Granularity和Detection_Percent,两个字段的值分别为Function和0.8。这是第一次克隆检测行为,将克隆检测ID设置为1。
为了对比基于不同检测粒度和相似度的结果,可以进行多次克隆检测。假设检测相似度仍为80%且保持不变,将检测粒度调整为块级。这是第二次检测行为,对应的检测ID设置为2,以便与第一次克隆检测行为区分开。然后,假设检测粒度不变,即设置为函数级,将检测相似度修改为60%。为了与之前的克隆检测行为区分开,将克隆检测ID设置为3。最后,基于检测粒度和相似度同时改变的克隆检测,为了与之前的克隆检测形成对比,将检测粒度和相似度分别修改为块级和60%。为了与之前的克隆检测行为区分开,将克隆检测ID设置为4。
5.9.6.2 增量克隆检测
大多数克隆检测方法仅能对单一版本源代码进行完整的克隆检测。而对于大型复杂软件系统,每次代码变更后,需要对所有代码重新进行克隆检测,效率低下。基于已有克隆信息,对克隆进行增量检测,将显著提高检测效率。基于分组的代码克隆增量检测将源代码划分为变化和未变化两组,即变化组和未变化组。首先对两组间及变化组内的源代码进行克隆检测,然后将克隆检测结果与原始克隆信息合并,实现增量式检测。
1.增量克隆检测原理
增量克隆检测的输入是前后两个版本的源代码和前一版本的克隆信息,输出是后一版本的克隆信息。首先将后一版本的软件源码分为未经变化及经过变化的代码集合,然后利用克隆检测算法找到与改变过的代码相关的克隆信息,包括变化代码集合中的克隆信息及存在于变化代码集合和未变化代码集合之间的克隆信息,最后将基于前一版本的克隆信息与改变过代码相关的克隆信息合并得到后一版本的克隆信息。
源代码变化必然引起克隆代码改变。令第 、 个版本的源代码集合分别为 和 ,定义 为从源代码集合 到其克隆信息 的映射,即 ;定义 是从两个源代码集合 、 到它们之间的克隆信息 的映射,即 。 指存在于两个源代码集合之间的克隆信息。增量克隆检测需要通过 、 、 、 描述 。 。因此有
(5-9)
式中,⊕ 为合并操作符,意味着通过合并集合 、 及 与 之间的克隆信息,就能获得第 k 个版本的克隆信息。
2.增量克隆检测实现
增量克隆检测算法的输入包括原版本克隆信息、变化以及未变化的源代码,算法输出包括新版本克隆信息以及从原版本到新版本克隆信息的变化。克隆信息包括克隆类和克隆实例两个层次,克隆信息变化亦分为克隆类和克隆实例的变化两个层次,克隆信息的变化类型包括Created、Updated、Removed和Unchanged共4种。
1)差异克隆信息
根据两个版本之间的差异,将原版本源代码划分为变化和未变化两部分,称发生改变的部分为变化代码集合,未发生改变的部分为未变化代码集合。代码集合中的元素通常指源文件,也可以指更粗粒度的源文件目录或更细粒度的函数或方法。利用克隆检测工具得到新版本中变化代码集合内的克隆信息和变化代码集合与非变化代码集合之间的克隆信息,以克隆类及克隆实例方式呈现克隆信息,称这些克隆信息为差异克隆信息。
2)算法预处理
对原版本中克隆类及克隆实例进行标记。在算法处理过程中,用临时标记Unknown表示一个克隆类或克隆实例处于待定状态。将原版本中所有克隆类标记为Unknown,如果原版本的某一克隆实例出现在变化代码集合中,意味着不能确定其具体状态,暂时保留该实例的Unknown状态,反之标记该克隆实例为Unchanged。
3)生成增量克隆类/克隆实例
基于所收集的差异克隆信息及预处理标记,由增量克隆检测算法得到增量克隆类及克隆实例。在增量克隆检测算法中,首先默认原版本中的克隆类和新版本中的克隆类,即所要得到的新版本克隆类集合NCSSet。然后对差异克隆类集合中的每一个差异克隆类进行分析。
如果一个差异克隆类不能在NCSSet找到对应的克隆类,则该差异克隆类为新增克隆类,将其标记为Created;反之,则意味着该克隆类在原版本中存在但在新版本中被修改,于是将该克隆类标记为Updated。被标记为Updated的克隆类需要与前一版本的克隆类进行合并。在甄别完新增和被更新的克隆类后,在剩下的被标记为Unknown的克隆类中进一步区分未被改动的克隆类和被移除的克隆类,得到新版本的所有克隆类。
针对差异克隆类DiffCS的每一个差异克隆实例,在原克隆类OCS中寻找相应的克隆实例。对应的克隆实例表示前后两个版本中代码片段相似且代码位置相似的代码。如果找不到对应的克隆实例,意味着该克隆实例为新版本中新增的克隆实例,将其标记为Created;反之,观察差异克隆实例的位置/代码是否与找到的克隆实例完全一致。如果完全一致则意味着该克隆实例未被改动,于是将原克隆实例标记为Unchanged即可;反之,则意味着该克隆实例被修改,选取差异克隆实例作为新版本克隆实例,并将其标记为Updated,且保留其原版本信息。
4)后置处理
当增量克隆算法处于待完成状态时,所有克隆类处于Created、Updated或Unknown状态,而所有克隆实例均处于Created、Updated、Unchanged或Unknown状态。后处理旨在清除所有Unknown状态。算法中,所有新增及被修改的克隆类、克隆实例及未被修改的克隆实例均已被标记,对于剩下且被标记为Unknown的克隆类和克隆实例,仅仅需要区分它们是Removed还是Unchanged即可,但需区分克隆类和克隆实例的这两种状态。
(1) 对于克隆实例的状态区分 。在待完成状态,一个标记为Unknown的原版本克隆实例,在新版本中必然是被移除的克隆实例,如果一个标记为Unknown的原版本克隆实例仍然存在,意味着该克隆实例未被改变,或已被更新。进行预处理时,该克隆实例被标记为Unknown,意味着该克隆实例出现在变化代码集合中,必然存在于一个与原克隆类对应的差异克隆类中。在差异克隆类中,该克隆实例并未被标记为Unchanged,也未被标记为Updated,表明该克隆实例肯定被更改但未被更新。由此,将在待完成状态时标记为Unknown的原版本克隆实例标记为Removed。
(2) 对于克隆类的状态区分 。在待完成状态,对于一个克隆类,如果所有克隆实例是Unchanged状态,表明该克隆类从未被改变;反之,至少有一个克隆实例没有被标记为Unchanged时,若被标记为Unknown,则该克隆类在新版本中被移除。如果一个克隆类的所有克隆实例都是Unchanged状态,则要么是该克隆类的克隆实例一开始就未出现在变化代码集合中,由差异代码集合产生的差异克隆类不会与其对应;要么是至少有一个该克隆类的克隆实例出现在变化代码集合中,合并结果仍然表明这些克隆实例的状态是Unchanged。
对于以上任何一种情况,该克隆类的克隆实例必然均未改变过。反之亦然,所有克隆实例都是Unchanged的克隆类必然未被改变过。在待完成状态,如果一个Unknown的克隆类一定被改变过,且非新增或更新,那么它一定是被移除的克隆类。反之,如果它被标记为Unknown,则该克隆类被标记为Removed。此外,如果一个克隆类,其非Removed状态的克隆实例数量小2,那么也将该克隆类标记为Removed。
3.增量克隆检测应用
为分析每个 repository 的演化情况,需要追踪代码的变化情况并进行克隆检测。对 repository 每个commit对应的源代码进行克隆检测,如果对每次commit对应的源代码进行全量检测,资源耗费巨大,而依次基于前一次克隆结果进行增量式克隆检测,则可能造成积累误差。一个行之有效的办法是基于全量和增量克隆检测进行综合检测。一个GitHub项目的release和commit按时间顺序开展的增量计算克隆如图5-50所示。横轴代表项目的时间线,用灰色填充点代表一次release,未填充点代表一次commit。
图5-50 增量计算克隆
调用ccfinderx命令,对每个release进行全量克隆检测,得到项目每个release对应版本的克隆信息。然后对某次commit,根据commit_ID将repository选择到对应版本,找到该commit前最近的一次release,取出该release对应的克隆信息,利用git命令分别得到该commit和release的源代码以及该commit对应的版本克隆信息,实现对每个commit的增量克隆检测。将得到的所有克隆信息按实际release或commit发布日期顺序排列,得到项目完整的克隆演变情况。