acm-header
登录

ACM通信

实践

幸存的软件依赖关系


计算机代码的抽象图像

信贷:t·埃米尔

回到顶部

几十年来,对软件重用的讨论比实际的软件重用更常见。今天,情况正好相反:开发人员每天以软件依赖的形式重用其他人编写的软件,而这种情况大多没有经过检查。

我的背景包括在谷歌的内部源代码系统中工作了十年,该系统将软件依赖性视为一流的概念,17以及开发对Go编程语言中的依赖项的支持。2

软件依赖带来了严重的风险,这些风险常常被忽视。向简单的、细粒度的软件重用的转变发生得如此之快,以至于我们还不了解有效选择和使用依赖项的最佳实践,甚至不知道它们什么时候合适,什么时候不合适。本文的目的是提高对风险的认识,并鼓励对解决方案进行更多的研究。

在当今的软件开发中,a依赖是程序员想要调用的附加代码。添加依赖项可以避免重复工作:设计、测试、调试和维护特定的代码单元。在本文中,该代码单元称为;有些系统使用这些术语图书馆而且模块代替。

采用外部编写的依赖并不新鲜。大多数程序员在他们的职业生涯中都曾经历过手动安装所需库的步骤,如C的PCRE或zlib;c++的Boost或Qt;或Java的JodaTime或JUnit。这些包包含高质量的、经过调试的代码,需要大量的专业知识进行开发。对于需要其中一个包提供的功能的程序,手动下载、安装和更新包的繁琐工作比从头开始重新开发该功能的工作要容易得多。然而,重用的高固定成本意味着手动重用的包往往很大;一个小的包更容易重新实现。

一个依赖关系管理(或称。包管理器)自动下载和安装依赖包。由于依赖管理器使单个包更容易下载和安装,较低的固定成本使发布和重用更小的包更经济。例如,Node.js依赖管理器NPM提供了对超过750,000个包的访问。其中一个,escape-string-regexp,由一个函数组成,该函数在其输入中转义正则表达式操作符。整个实现是:

var matchOperatorsRe =
/[|\\{}()[\]^$+*?.]/ g;
模块。导出=函数(str) {
If (typeof STR !== 'string') {
把新的TypeError (
“将一个字符串”);

返回str.replace (
matchOperatorsRe , '\\$&');
};

在依赖管理器出现之前,发布一个8行代码库是不可想象的:开销太多,收益太少。然而,NPM使开销几乎为零,结果是几乎微不足道的功能都可以被打包和重用。2019年4月下旬,escape-string-regexp软件包被其他近千个NPM软件包明显依赖,更不用说开发人员为自己使用而编写的所有软件包了。

现在几乎每种编程语言都有依赖管理器:Maven Central (Java)、NuGet (. net)、Packagist (PHP)、PyPI (Python)和RubyGems (Ruby),每种语言都有超过100,000个包。这种细粒度的、广泛的软件重用的出现是过去二十年来软件开发中最重要的转变之一。如果我们不多加小心,就会导致严重的问题。

回到顶部

会出什么问题?

对于本文的讨论,包是从Internet上下载的代码。将一个包作为依赖项添加会将开发代码、设计、编写、测试、调试和维护的工作外包给Internet上的其他人,而这些人通常是程序员不知道的。使用该代码将程序暴露给依赖中的所有失败和缺陷。现在程序的执行取决于从网上这个陌生人那里下载的代码以这种方式呈现,听起来非常不安全。为什么会有人这么做?

因为它很简单,似乎很有效,其他人都在这么做,最重要的是,它似乎是古老的既定做法的自然延续。但有一些重要的差异被忽视了。

几十年前,大多数开发人员都信任别人来编写他们所依赖的软件,比如操作系统和编译器。该软件是从已知的来源购买的,通常带有某种支持协议。仍然有可能出现漏洞或完全的恶作剧,20.但至少开发者知道他们是在和谁打交道,通常有商业或法律资源可用。

在互联网上免费分发的开源软件现象已经取代了许多早期的软件购买。当重用很困难时,很少有项目发布可重用代码包。尽管它们的许可证通常不包括任何“对适销性和适合某一特定用途的默示保证”,但这些项目建立起了众所周知的声誉,这往往是人们决定使用哪一种产品的重要因素。信任软件来源的商业和法律支持被声誉支持所取代。许多常见的早期软件包仍然享有良好的声誉:例如BLAS(1979年发布)、Netlib(1987年)、libjpeg(1991年)、LAPACK(1992年)、HP STL(1994年)和zlib(1995年)。

依赖管理器已经扩展了这个开放源代码重用模型。现在,开发人员可以在由几十行代码组成的单个函数的粒度上共享代码。这是一项重大的技术成就。有无数的包可用,编写代码可能涉及大量的包,但是信任代码的商业、法律和声誉支持机制还没有延续下来。开发人员信任更多的代码,而这样做的理由较少。

采用不良依赖的成本可以被视为所有可能的不良结果的总和,即每个不良结果的成本乘以其发生的概率(风险),如等式所示。

ueq01.gif

使用依赖项的上下文决定了坏结果的代价。一个极端是个人爱好项目,大多数糟糕结果的成本接近于零:您只是在享受乐趣,bug除了浪费时间外没有真正的影响,甚至调试也可以是有趣的。所以,风险概率几乎不重要,它乘以几乎为零的失败成本。在光谱的另一端是必须维护多年的生产软件。在这种情况下,依赖项中的一个错误的代价可能非常高:服务器可能宕机,敏感数据可能泄露,客户可能受到伤害,或者公司可能倒闭。高昂的故障成本使得评估和降低严重故障的风险变得更加重要。


开发人员信任更多的代码,而这样做的理由较少。


无论预期的成本是多少,具有较大依赖关系的经验为估计和减少添加软件依赖关系的风险提供了一些方法。可能需要更好的工具来帮助降低这些方法的成本,就像依赖管理器迄今专注于降低下载和安装的成本一样。

回到顶部

检查依赖关系

你不会雇佣一个你从未听说过或对其一无所知的软件开发人员。你会首先对这个人有更多的了解:检查推荐信,进行工作面试,进行背景调查,等等。在依赖于在Internet上找到的软件包之前,同样谨慎的做法是先对其进行一些了解。

基本的检查可以让您了解在使用此代码时遇到问题的可能性有多大。如果检查发现了可能的小问题,您可以采取措施准备或可能避免它们。如果检查发现重大问题,最好不要使用包装;也许你会找到一个更合适的,或者也许你需要自己开发一个。请记住,开放源码包是由它们的作者发布的,他们希望它们是有用的,但没有可用性或支持的保证。在生产中断期间,您将是调试包的人。正如最初的GNU通用公共许可证所警告的那样,“有关程序的质量和性能的所有风险都由您承担。如果程序被证明有缺陷,您将承担所有必要的服务、修理或更正的费用。”7

以下是在检查一个包并决定是否依赖它时的一些考虑:

设计。文档是否清晰?API是否有清晰的设计?如果作者能够在文档中很好地解释包的API及其设计,那就增加了他们在源代码中向计算机很好地解释实现的可能性。使用清晰、设计良好的API编写代码也更容易、更快,而且希望更不容易出错。为了使将来的升级兼容,作者是否记录了他们对客户端代码的期望?(例子包括c++238兼容文件。)

代码质量。代码写得好吗?读一些吧。作者是否看起来很细心、认真、前后一致?它看起来像您想要调试的代码吗?你可能需要。

开发自己的系统方法来检查代码质量。例如,像编译一个启用了重要编译器警告(例如-Wall)的C或c++程序这样简单的事情,可以让您了解开发人员如何认真地避免各种未定义的行为。最近的语言,如Go、Rust和Swift都使用an不安全的关键字标识违反类型系统的代码;看看有多少不安全的代码。更高级的语义工具,如Infer6或SpotBugs19也有帮助。细句的帮助较小:你应该忽略关于主题的死记硬背的建议,比如大括号样式,而是关注语义问题。

对不熟悉的开发实践保持开放的心态。例如,SQLite库作为一个20万行C源文件和一个11000行头文件(称为合并。这些文件的大小应该会引起最初的警告,但更仔细的调查会发现实际的开发源代码,一个传统的文件树,有超过100个C源文件、测试和支持脚本。结果表明,单文件分发是根据原始源代码自动构建的,对于最终用户来说更容易,特别是那些没有依赖项管理器的用户。(编译后的代码运行得更快,因为编译器可以看到更多的优化机会。)

测试。代码有测试吗?你能运行一下吗?他们通过吗?测试确定代码的基本功能是正确的,并且它们表明开发人员非常重视保持代码的正确性。例如,SQLite开发树有一个令人难以置信的完整测试套件,包含超过30,000个独立测试用例,以及解释测试策略的开发人员文档。10另一方面,如果测试很少或没有测试,或者测试失败,这是一个严重的危险信号。未来对软件包的修改很可能会引入很容易被发现的回归。如果您坚持在自己编写的代码中进行测试(您确实如此,对吗?),那么您应该坚持在外包给他人的代码中进行测试。

假设测试存在、运行并通过,您可以通过使用运行时工具(如代码覆盖率分析、竞态检测、16内存分配检查和内存泄漏检测。

调试。找到包的问题跟踪器。是否有许多未打开的错误报告?他们开了多久了?是否有许多已修复的bug ?最近有bug被修复了吗?如果您看到许多看起来像真正的bug的开放问题,特别是如果它们已经开放了很长一段时间,这不是一个好迹象。另一方面,如果已解决的问题表明很少发现bug并及时修复,那就太好了。

维护。查看包的提交历史记录。代码被积极维护了多长时间?现在还在积极维护吗?积极维护了很长一段时间的包更有可能继续被维护。有多少人负责这个项目?许多软件包都是开发人员在业余时间创建和分享的个人项目。另一些则是一群付费开发人员数千小时工作的结果。一般来说,后一种包更有可能有及时的bug修复、稳定的改进和一般的维护。

另一方面,有些代码确实“完成了”。例如,NPM的escape-string-regexp,可能永远不需要再次修改。

使用。是否有许多其他包依赖于此代码?依赖管理器通常可以提供关于使用情况的统计信息,或者您可以使用Web搜索来估计其他人写关于使用包的文章的频率。更多的用户应该至少意味着更多的人,他们的代码运行得足够好,同时可以更快地发现新bug。广泛使用也可以避免持续维护的问题;如果一个广泛使用的包失去了它的维护者,感兴趣的用户可能会挺身而出。

例如,像PCRE、Boost或JUnit这样的库被广泛使用。这就更有可能——尽管不能保证你可能遇到的bug已经被修复了,因为其他人会先遇到它们。

安全。您将使用包处理不可信的输入吗?如果是的话,它对恶意输入是否健壮?它是否有在NVD(国家漏洞数据库)中列出的安全问题的历史?13

例如,在2006年,当Jeff Dean和我开始开发谷歌代码搜索时5grep优于公共源代码,流行的PCRE正则表达式库似乎是一个明显的选择。然而,在与谷歌的安全团队的早期讨论中,我们了解到PCRE有缓冲区溢出等问题的历史,特别是在它的解析器中。我们可以通过在NVD中搜索PCRE来了解同样的情况。这一发现并没有立即导致我们放弃PCRE,但它确实让我们对测试和隔离进行了更仔细的思考。

许可。代码是否得到了适当的许可?它有执照吗?许可证是否适用于您的项目或公司?令人惊讶的是,GitHub上有相当一部分项目没有明确的许可。您的项目或公司可能会对所允许的依赖项的许可施加进一步的限制。例如,谷歌不允许使用在类似agpl的许可证(太繁琐)下许可的代码,也不允许使用类似wtfpl的许可证(太模糊)。9

依赖关系。代码是否有自己的依赖关系?间接依赖中的缺陷与直接依赖中的缺陷一样对程序有害。依赖管理器可以列出给定包的所有传递依赖项,理想情况下,应该像这里描述的那样检查每一个依赖项。具有许多依赖项的包会产生额外的检查工作,因为这些相同的依赖项会产生需要评估的额外风险。

许多开发人员从未查看过代码的传递依赖项的完整列表,也不知道它们依赖于什么。例如,NPM用户社区在2016年3月发现,包括Babel、Ember和Reactall在内的许多流行项目都间接依赖于一个名为left-pad的小包,它由单一的八行函数体组成。当left-pad的作者从NPM中删除了那个包,无意中破坏了大多数Node.js用户的构建时,他们发现了这一点。22左pad在这方面并不例外。例如,在NPM上发布的750,000个包中,30%至少是间接依赖的escape-string-regexp。根据Leslie Lamport对分布式系统的观察,依赖管理器很容易造成这样一种情况:您甚至不知道存在的包的故障会导致您自己的代码不可用。

回到顶部

测试的依赖

检查过程应该包括运行包本身的测试。如果包通过了检查,并且您决定让您的项目依赖于它,那么下一步应该是编写新的测试,重点关注应用程序所需的功能。这些测试通常从编写简短的独立程序开始,以确保您能够理解包的API以及它所做的事情。(如果你不能或者它没有,现在就回去!)值得付出额外的努力,将这些程序转换为可以在包的新版本上运行的自动化测试。如果您发现了一个错误并有一个可能的修复,您将希望能够轻松地重新运行这些特定于项目的测试,以确保修复不会破坏其他任何东西。

尤其值得对基本检查所确定的可能问题领域进行练习。对于代码搜索,我们从过去的经验中知道,PCRE有时需要很长时间才能执行某些正则表达式搜索。最初的计划是为“简单”和“复杂”正则表达式搜索拥有独立的线程池。最初的测试之一是比较pcre-grep与其他几个grep实现的基准测试。对于一个基本测试用例,pcree -grep比最快的grep慢70倍,因此我们开始重新考虑使用PCRE的计划。即使最终完全放弃了PCRE,该基准仍然保留在今天的代码库中。

回到顶部

抽象的依赖

依赖于软件包是一个很可能在以后重新考虑的决定。也许更新会把这个包带向一个新的方向。也许会发现严重的安全问题。也许会有更好的选择。由于所有这些原因,值得努力使您的项目更容易地迁移到新的依赖项。

如果该包将从项目源代码中的许多地方使用,那么迁移到一个新的依赖项将需要对所有这些不同的源位置进行更改。更糟糕的是,如果包将在您自己的项目API中公开,那么迁移到一个新的依赖项将需要更改所有调用您的API的代码,这可能是您无法控制的。为了避免这些成本,有必要定义自己的接口,以及使用依赖项实现该接口的瘦包装器。注意,包装器应该只包含项目需要的依赖项,而不是依赖项提供的所有内容。理想情况下,这允许您稍后仅通过更改包装器来替换一个不同的、同样合适的依赖项。将每个项目的测试迁移到使用新接口将测试接口和包装器实现,并使测试依赖项的任何潜在替换变得容易。

对于代码搜索,我们开发了一个摘要正则表达式类定义了任何正则表达式引擎所需的代码搜索接口。然后,我们围绕实现该接口的PCRE编写了一个薄包装器。这种间接性使得测试替代库变得容易,并且防止意外地将PCRE内部知识引入到源代码树的其余部分。这反过来确保了在需要时可以轻松切换到不同的依赖项。

回到顶部

孤立的依赖

为了限制bug可能造成的损害,在运行时隔离依赖也可能是合适的。例如,谷歌Chrome允许用户添加依赖扩展代码到浏览器。当Chrome在2008年推出时,它引入了一个关键功能(现在是所有浏览器的标配):将每个扩展隔离在一个沙盒中,运行在一个独立的操作系统进程中。18

因此,一个编写糟糕的扩展中的可利用bug不会自动访问浏览器本身的整个内存,并且可以阻止进行不适当的系统调用。12对于代码搜索,在我们完全放弃PCRE之前,我们的计划是至少将PCRE解析器隔离在一个类似的沙盒中。今天,另一种选择是基于管理程序的轻量级沙盒,如gVisor。11隔离依赖减少了运行该代码的相关风险。

即使有这些示例和其他现成的选项,可疑代码的运行时隔离仍然非常困难,而且很少进行。真正的隔离需要一种完全内存安全的语言,没有进入非类型化代码的逃生通道。这不仅是对完全不安全的语言(如C和c++)的挑战,也是对提供受限不安全操作的语言的挑战,如Java在包含JNI (Java本机接口)时,或Go、Rust和Swift在包含它们的“不安全”特性时。即使在诸如JavaScript这样的内存安全语言中,代码访问的内容也常常远远超出其所需。2018年11月,NPM包事件流的最新版本被发现包含2.5个月前添加的模糊恶意代码,该版本为JavaScript事件提供了功能性流API。这段代码从“共同支付”移动应用程序的用户那里获取了大量比特币钱包,它正在访问与处理事件流完全无关的系统资源。1针对这类问题的一种可能的防御措施是更好地限制可访问的依赖项。

回到顶部

避免依赖

如果某个依赖项看起来风险太大,而您又找不到隔离它的方法,那么最好的答案可能是完全避免它,或者至少避免您已经确定的问题最大的部分。


即使在所有这些工作之后,您还没有完成对依赖项的照料。重要的是要继续监控它们,甚至重新评估使用它们的决定。


例如,当我们更好地理解与PCRE相关的风险和成本时,我们的谷歌代码搜索计划从“直接使用PCRE”演变为“使用PCRE但沙箱解析器”,演变为“编写一个新的正则表达式解析器但保留PCRE执行引擎”,演变为“编写一个新的解析器并将其连接到一个不同的、更有效的开源执行引擎”。后来我们也重写了执行引擎,这样就不会留下任何依赖项,并且我们开放了结果:RE2。4

如果您只需要依赖项的一小部分,那么最简单的解决方案可能是制作所需内容的副本(当然,要保留适当的版权和其他法律通知)。您要承担修复bug、维护等等的责任,但是您也完全与更大的风险隔离开来。关于这一点,Go开发者社区有一句谚语:“一点复制胜过一点依赖。”14

回到顶部

升级的依赖

在很长一段时间里,关于软件的传统智慧是:“如果它没坏,就不要修它。”升级有可能会引入新的bug;如果没有相应的奖励,比如你需要的新功能,为什么要冒险呢?这种分析忽略了两个成本。首先是最终升级的成本。在软件中,修改代码的难度不是线性增长的:做10个小的修改比做一个等价的大的修改工作量更少,更容易做对。其次是发现已经修复的错误的代价。特别是在安全上下文中,已知的错误被积极利用,您每等待一天,攻击者就可以闯入一天。

例如,想一想2017年Equifax的高管们在详细的国会证词中所讲述的情况。213月7日,Apache Struts被披露了一个新的漏洞,并发布了一个补丁版本。3月8日,Equifax收到了来自US-CERT(美国计算机应急准备小组)的通知,要求更新Apache Struts的所有使用。Equifax分别在3月9日和3月15日进行了源代码和网络扫描;两次扫描都没有发现特定的面向公众的Web服务器组。5月13日,攻击者发现了Equifax安全团队无法访问的服务器。他们利用Apache Struts漏洞侵入Equifax的网络,然后在接下来的两个月窃取了1.48亿人的详细个人和财务信息。Equifax终于在7月29日注意到这一漏洞,并于9月4日公开披露。到9月底,Equifax的首席执行官、首席信息官和首席信息官都辞职了,国会正在进行调查。

Equifax的经验充分说明了这一点,尽管依赖管理器知道他们在构建时使用的版本,但是必须做出其他安排来通过生产部署过程跟踪该信息。对于Go语言,我们正在尝试在每个二进制文件中自动包含一个版本清单,以便部署过程可以扫描二进制文件,查找需要升级的依赖项。Go还使这些信息在运行时可用,这样服务器就可以查询已知bug的数据库,并在需要升级时向监控软件自我报告。

及时升级是很重要的,但这意味着向项目中添加新代码,这应该意味着更新基于新版本使用依赖项的风险评估。至少,您会希望浏览显示从当前版本到升级版本所做更改的差异,或者至少阅读发行说明,以确定升级代码中最可能关注的区域。如果大量代码正在更改,因此差异难以消化,您可以将这一事实合并到风险评估更新中。

您还需要重新运行已编写的特定于项目的测试,以确保升级后的包至少与早期版本一样适用于项目。重新运行包自己的测试也有意义。如果包有它自己的依赖项,那么您的项目配置完全有可能使用那些依赖项的版本(旧的或新的)与包的作者使用的版本不同。运行包自己的测试可以快速识别特定于配置的问题。

再次强调,升级不应该是完全自动的。在部署升级版本之前,必须验证升级版本是否适合您的环境。3.


如果某个依赖项看起来风险太大,而您又找不到隔离它的方法,那么最好的答案可能是完全避免它,或者至少避免您已经确定的问题最大的部分。


如果升级过程包括重新运行已经为依赖项编写的集成和确认测试,以便在新问题到达生产环境之前发现它们,那么在大多数情况下,延迟升级比快速升级风险更大。

关键安全升级的窗口期特别短。在Equifax事件发生后,取证安全团队发现,攻击者(可能是不同的攻击者)在3月10日成功利用了受影响服务器上的Apache Struts漏洞,而这距离该漏洞被公开披露仅3天,但他们只运行了一个whoami命令。

回到顶部

小心你的依赖

即使在所有这些工作之后,您还没有完成对依赖项的照料。重要的是要继续监控它们,甚至重新评估使用它们的决定。

首先,确保您一直使用您认为自己是的特定包版本。现在,大多数依赖项管理器使得记录给定包版本的预期源代码的加密散列变得很容易,甚至是自动的,然后在另一台计算机上或测试环境中重新下载包时检查该散列。这确保您的构建使用您检查和测试的相同依赖项源代码。这些检查可以防止前面描述的事件流攻击者在已经发布的3.3.5版本中静默插入恶意代码。相反,攻击者必须创建一个新版本3.3.6,并等待人们升级(而不仔细查看更改)。

观察新的间接依赖也很重要。升级可以很容易地引入项目成功所依赖的新包。他们也值得你的关注。在事件流的情况下,恶意代码隐藏在另一个包flatmap-stream中,新的事件流版本将其添加为新的依赖项。

爬行依赖也会影响项目的大小。在谷歌的Sawzall的开发过程中15这是一种JIT日志处理语言,作者在不同的时候发现,主解释器二进制文件不仅包含Sawzall的JIT,还包含(未使用的)PostScript、Python和JavaScript解释器。每次,罪魁祸首都被证明是由Sawzall所依赖的库声明的未使用的依赖项,再加上谷歌的构建系统消除了开始使用新依赖项所需的任何手动工作。这类错误是Go语言将导入未使用的包作为编译时错误的原因。

升级是重新考虑使用正在变化的依赖项的决定的自然时机。周期性地重新审视任何依赖也是很重要的不是改变。不存在需要修复的安全问题或其他错误,这似乎合理吗?这个项目被放弃了吗?也许是时候开始计划取代这种依赖了。

重新检查每个依赖项的安全历史记录也很重要。例如,Apache Struts在2016年、2017年和2018年披露了不同的主要远程代码执行漏洞。即使您有一个运行它的所有服务器的列表,并及时更新它们,跟踪记录可能会让您重新考虑是否使用它。

回到顶部

结论

软件重用终于来了,它的好处不应该被低估。它为软件开发人员带来了巨大的积极转变。即便如此,我们在没有完全考虑到潜在后果的情况下接受了这种转变。信任依赖关系的旧理由正在变得不那么有效,而与此同时,依赖关系比以往任何时候都要多。

本文中概述的对特定依赖项的批判性检查是一项大量的工作,并且仍然是例外而不是规则。任何开发人员都不太可能真正为每一个可能的新依赖项做这些工作。我只为我自己的依赖的一个子集做了其中的一个子集。大多数情况下,整个决策就是,“让我们看看会发生什么。”通常情况下,任何超出这个范围的事情都显得太费劲了。

对Copay和Equifax的攻击是对当今软件依赖使用方式中存在的现实问题的明确警告。我们不应忽视这些警告。以下是三个主要的建议:

  1. 认识到问题。这篇文章希望让您相信,这里有一个值得解决的问题。我们需要很多人集中精力解决这个问题。
  2. 为今天建立最佳实践。使用当前可用的工具管理依赖关系需要最佳实践。这意味着制定评估、减少和跟踪风险的过程,从最初的采用决策到生产使用。事实上,就像一些工程师擅长测试一样,其他人可能需要擅长管理依赖关系。
  3. 为明天开发更好的依赖技术。依赖管理器基本上消除了下载和安装依赖的成本。未来的开发工作应该集中于减少使用依赖项所需的评估和维护的成本。例如,包发现网站可能致力于寻找更多的方法,允许开发人员分享他们的发现。构建工具至少应该使运行包自己的测试变得容易。更积极的是,构建工具和包管理系统还可以一起工作,以允许包作者针对其api的所有公共客户机测试新的更改。语言还应该提供隔离可疑包的简单方法。

市面上有很多好软件。让我们一起努力,找出如何安全地重用它。

ACM队列的q戳相关文章
queue.acm.org

服务可用性的计算
本·特莱诺,迈克·达林,维维克·劳,贝琪·拜尔
https://queue.acm.org/detail.cfm?id=3096459

跟踪和控制微服务依赖关系
西尔维娅·埃斯帕拉契里,坦尼娅·赖利,阿什丽·伦兹
https://queue.acm.org/detail.cfm?id=3277541

你不应依靠我
托拜厄斯·劳英格,阿卜杜勒贝里·查巴内,克里斯托·b·威尔逊
https://queue.acm.org/detail.cfm?id=3205288

回到顶部

参考文献

1.鲍德温,A.事件流事件的细节。npm博客(2018年11月);https://bit.ly/2DRjySJ

2.考克斯,R. Go &版本化,2018;https://research.swtch.com/vgo

3.考克斯,R.围棋版本控制的原则。GopherCon新加坡(2018年5月);https://www.youtube.com/watch?v=F8nrpe0XWRg

4.Cox, R. RE2:正则表达式匹配的原则性方法。谷歌开源博客(2010年3月);https://bit.ly/2XoLFzC

5.使用三元ram索引的正则表达式匹配或谷歌代码搜索的工作原理。Swtch.com(2012年1月);https://swtch.com/~rsc/regexp/regexp4.html

6.Facebook。Infer:在Java和C/ c++ /Objective-C代码发布之前检测bug的工具;https://fbinfer.com/

7.GNU项目。GNU通用公共许可证,版本1,1989;https://www.gnu.org/licenses/old-licenses/gpl-1.0.html

8.项目。围棋1和围棋程序的未来,2013;https://golang.org/doc/go1compat

9.谷歌开源的。使用第三方许可证;https://opensource.google.com/docs/thirdparty/licenses/#banned

10.Hipp, D. R.如何测试SQLite;https://www.sqlite.org/testing.html

11.Lacasse, N.,开源gVisor,沙箱容器运行时。谷歌云(2018年5月);http://bit.ly/2wzA84D

12.铬的seccomp沙盒。ImperialViolet(2009年8月);https://www.imperialviolet.org/2009/08/26/seccomp.html

13.国家标准与技术研究所。国家脆弱性数据库研究与统计;https://nvd.nist.gov/vuln/search

14.派克,R. Go谚语,2015;https://go-proverbs.github.io/

15.Pike, R., Dorward, S., Griesemer, R.和Quinlan, S.解释数据:与Sawzall的并行分析。科学编程J. 13, 4 (2005), 277298;https://doi.org/10.1155/2005/962135

16.测试铬:ThreadSanitizer v2,下一代数据竞争检测器。Chromium博客(2014年4月);http://bit.ly/2WN29o0

17.为什么谷歌在一个存储库中存储数十亿行代码。Commun。ACM 59, 7(2016年7月),7887;https://doi.org/10.1145/2854146

18.多进程架构。Chromium博客(2008年9月);https://blog.chromium.org/2008/09/multi-process-architecture.html

19.SpotBugs:在Java程序中查找bug;https://spotbugs.github.io/

20.关于信任的思考。Commun。ACM 27, 8(1984年8月),761763;https://doi.org/10.1145/358198.358210

21.美国众议院监督和政府改革委员会。Equifax数据泄露,多数员工报告,第115届国会(2018年12月);http://bit.ly/2Gf53IJ

22.Willis, N.单个节点故障。LWN.net(2016年3月);https://lwn.net/Articles/681410/

23.温特斯,T. SD-8:标准库兼容性,c++常设文档,2018;http://bit.ly/2QNhT5k

回到顶部

作者

拉斯考克斯在谷歌领导Go编程语言的开发,目前专注于提高使用软件依赖的安全性和可靠性。他和Jeff Dean一起创建了谷歌代码搜索。他在贝尔实验室的9号计划操作系统上工作了很多年。


版权归作者/所有者所有。
向所有者/作者请求(重新)发布权限

数字图书馆是由计算机协会出版的。版权所有©2019 ACM, Inc.


没有发现记录

Baidu
map