重新审视 2015 年开源普查Revisiting the 2015 Open Source Census
作者回溯十年前的开源项目普查数据,识别出当时最具风险的开放源代码项目。分析基于项目活跃度、维护者数量和社区支持度等指标,提前预警了若干后来遭遇严重问题的知名项目。结果突显早期社区健康度对长期生存的关键作用。
Andrew Nesbitt
2015年7月,也就是“心脏出血”漏洞曝光一年后,Linux基金会的核心基础设施倡议(Core Infrastructure Initiative)发布了一份开源项目的普查报告。其初衷是防患于未然:在下一个OpenSSL被发现之前,我们先找到它。他们以Debian的流行度排行榜为基础,对每个软件包进行风险评估,并生成一个优先级列表,以便集中资源提供支持。David Wheeler设计了评分模型,一个小团队进行了人工审核,最终输出了一份包含428个项目的CSV文件,按风险指数(risk_index)从1到13排序。
直到本周丹尼尔·斯坦伯格(Daniel Stenberg)在Mastodon上发了一条十年回顾的帖子,我才想起这份报告的存在,并链接回他2016年的文章。当时我已经在构建Libraries.io大约九个月了。起初我认为自己只是在做一个发现引擎——一种跨注册中心查找软件包的工具。又过了几年,我才意识到,我积累的依赖关系图对于可持续性和安全性问题比搜索更有价值。因此我记得读过那份普查结果,但和大多数人一样,之后便没再关注。数据自2016年以来一直未被改动过。如今重读这份报告,感觉有些奇怪,因为你清楚接下来十年发生了什么,而填写表格的人却并不知情。
xz-utils 排在第254位,风险指数为6,处于后半段。liblzma5——这个最终被接入全球一半SSH服务的库——排在237位,得分相同。
更值得关注的字段是最后一列,评审人员在此处输入了自由文本评论:
广泛使用的压缩/解压缩工具。此处存在漏洞可能非常严重……提交者人数较少,活跃度尚可。未发现 bug 跟踪系统。
有人在2015年就直截了当地指出:xz 为何危险?他用最朴素的语言写下了原因。然后公式给它评了6分,让它落在了榜单后236名之外。
评分机制如何运作
风险指数由六个维度的分数累加而成。没有官网可得一分,过去有CVE记录最多得三分;用C语言编写可得几分;流行程度也占一定分值;若涉及网络暴露或处理不可信数据,再加一两分。其中最重的单项高达五分,条件是过去十二个月内没有任何贡献者。
这是一种特定失败模式的建模:一个广泛部署的C语言库,负责解析来自网络的字节流,却已无人维护。这恰好是2014年初OpenSSL的状态——考虑到普查的初衷,这样的建模是合理的。
按此标准,xz看起来一切正常。OpenHub记录显示前一年有五名贡献者,刚好抵消了最大扣分项。它有一个历史CVE,仅扣一分。它的总分六分来自“流行的、处理不可信数据的C代码”,这与榜单上其他上百个项目处于同一基准线。然而,这五名贡献者实际是同一个人,而且此人已经精疲力尽——这些细节对公式而言毫无意义。一个仍在持续提交代码的疲惫维护者,仍被视作健康的项目。该模型衡量的是“放弃维护”,而xz的问题恰恰相反:一个外部人士真心想帮忙,却无处可施。更糟的是,如果有第二位活跃协作者加入,xz本应进一步下移——这正是Jia Tan到来后发生的情况,他的加入提升了所有相关贡献指标。
榜单前列的合理之处
只谈失误确实容易,但这份几个月内完成的电子表格,其前列部分的表现其实比你预想的要好。
第1行是 libexpat,得分为13分(满分),注释指出维护工作“似乎在2012年后已实际停止”,其漏洞追踪系统返回错误页面。Expat随后接连曝出大量CVE漏洞,并成为CII资助的项目之一。如今该项目已恢复积极维护。这正是普查设计所期望达成的效果。
第3行是unzip,至今仍与过去一样问题重重。第12行的rsync在2025年初也暴露出包括9.8分高危漏洞在内的六个CVE。第51行的zlib中,CVE-2018-25032竟多年未被察觉。ntp、openssl、krb5、gnutls均位列前四分位,持续产出模型所关注的漏洞类型。若在2015年就将前50个项目分配给每位安全工程师负责,就能及时发现真实风险。
第148行的libxml2则呈现另一种结果:模型准确识别了该项目的状况,但后续毫无作为。普查因其作为广泛使用且贡献者稀少的C语言解析器而评分为8分。去年其唯一维护者宣布不再处理未付费的安全报告后最终离职——这几乎一字不差地重现了普查旨在预防的情景,且早在十年前就在正确项目中识别出来。与xz不同,这并非评分失误,而是执行不力。电子表格完成了它的任务,资金流向了别处。
不在列表中的项目
风险公式是流程中的第二个模型。第一个是候选集:基于popcon阈值筛选的Debian软件包,加上若干人工添加项。该过滤器在评分前就已自行决定哪些属于基础设施范畴,而本十年最严重的几个漏洞因此被排除在外,而非排名错误。
sudo不在428个项目中。Baron Samedit(CVE-2021-3156)自2011年起就存在可被本地用户利用的堆溢出漏洞。polkit也不在其中,PwnKit(CVE-2021-4034)自2009年就已存在于代码中。log4j被排除在外尚可理解——毕竟列表公开聚焦于Linux安装底层的C语言组件——但sudo和polkit无疑是核心基础设施。它们缺失的原因在于输入集的构建方式,而非风险模型的判断。
一个数字
2015年公式中最重要的指标是一年内的贡献者数量,最高可占13分中的5分,正是这一项将xz排到了下半区。如今,以“bus factor”或“活跃维护者”等名称出现的同一指标,仍是大多数项目健康度评分体系的关键依据。OpenSSF Scorecard会检查过去90天内的提交记录。criticality_score作为普查公式的直系后代,同样重视近期提交者数量和提交频率。多个CHAOSS健康模型也采用类似方式依赖贡献者数量。这是个有用的信号,但也正因如此,它能将“一名精疲力竭的维护者加一名耐心的攻击者”误判为健康的二人团队。
丹尼尔最近从另一个角度展示了这一点。三月份,他注意到一个项目健康仪表盘显示 curl 的年下载量为 10,467 次。这个数据来自 ecosyste.ms,也就是说其实是我提供的。ecosyste.ms 会索引各个注册表公开的原始数据,而下载量统计正是系统包管理器和 Go 语言这类工具通常不会发布的信息之一。curl 随每个 Linux 发行版打包,并被集成到地球上近一半的软件中,但几乎没有任何渠道会通过报告计数器的注册表来分发。因此,10,467 这个数字准确反映了被监控的渠道数量,但它并不代表 curl 的实际下载次数——而这两者之间的差异在仪表盘上以单一字段呈现时就会丢失。
该指标集中的每一个数据都存在类似的问题。CVE 数量奖励那些无人审计的项目;流行度只是影响范围的代理指标,却完全无法说明是否有人在认真维护。"用 C 语言编写"曾是 2015 年合理的启发式判断,当时它会把 log4j 标记为安全。这些指标作为快速浏览尚可接受,但若用来对 428 行数据进行排序则极其危险——一旦成为排序依据,下半部分的数据就会被忽略。
需要完整排版与评论请前往来源站点阅读。