我们的软件依赖问题

2020-07-20 23:54:40

许多开发人员从未查看过代码的转移依赖的完整列表,也不知道它们依赖于什么。例如,2016年3月,NPM用户社区发现,许多流行的项目-包括Babel、Ember和Reaction-都直接依赖于一个名为Left-Pad的小程序包,该程序包只有一个8行的函数体。当Left-Pad的作者从NPM中删除该程序包时,他们发现了这一点,无意中破坏了大多数Node.js用户的构建。在这方面,Left-Pad也不例外。例如,在NPM上发布的750,000个包中,有30%依赖于-至少间接地-依赖于转义字符串-regexp.根据Leslie Lamport关于分布式系统的观察,依赖项管理器很容易造成这样一种情况,在这种情况下,您甚至不知道存在的包的故障可能会导致您自己的代码不可用。检查过程应该包括运行包自己的测试。如果包通过了检查,并且您决定使您的项目依赖于它,则下一步应该是编写新的测试,重点放在应用程序所需的功能上。这些测试通常从简短的独立程序开始编写,以确保您可以理解包的API,并确保它能完成您认为的功能。(如果您不能理解,或者它不能做到,现在就回头吧!)然后花额外的努力将这些程序转变为可以实现的自动化测试是值得的。您将希望能够以可测试的方式重新运行这些特定于项目的代码,以确保修复不会破坏任何其他内容。对于代码搜索,我们从过去的经验中了解到,PCRE有时要花很长时间来执行某些正则表达式搜索。我们最初的计划是使用单独的线程池进行“简单”和“复杂”的正则表达式搜索。我们运行的第一个测试之一是基准测试,将cregrep与其他几个grep实现进行比较。当我们发现,对于一个基本测试用例,pcregrep比最快的grep慢70倍。根据程序包的不同,您可能会在以后重新访问。可能更新会将程序包带向新的方向。可能会发现严重的安全问题。可能会出现更好的选择。由于所有这些原因,使您的项目轻松迁移到新的依赖项是值得的。如果将从项目源代码中的许多位置使用包,则迁移到新依赖项将需要对所有这些不同的源位置进行更改。更糟糕的是,如果包将在您自己的项目API中公开,迁移到新依赖项将需要在调用API的所有代码中进行更改,这可能无法控制。要避免这些成本,定义您自己的接口以及使用依赖项实现该接口的薄包装器是有意义的。请注意,包装器应该只包含什么内容。通过只更改包装器,迁移每个项目的测试以使用新的接口,可以测试接口和包装器实现,还可以轻松地测试依赖项的任何潜在替换。对于代码搜索,我们开发了一个抽象的Regexp类,它定义了任何正则表达式引擎所需的接口代码搜索。然后,我们围绕实现该接口的PCRE编写了一个薄包装器。这种间接性使得测试备用库变得很容易,并防止我们意外地将PCRE内部的知识引入源代码树的其余部分。这反过来又确保了在需要时可以很容易地切换到不同的依赖项。在运行时隔离依赖项也可能是合适的,以限制它中的错误可能造成的损害。例如,Google Chrome允许用户向浏览器添加依赖项-扩展代码。当Chrome在2008年发布时,它引入了关键功能(现在在所有浏览器中都是标准功能),将每个扩展隔离在运行在独立操作系统进程中的沙箱中。15因此,编写糟糕的扩展中的可利用错误不会自动访问浏览器本身的整个内存,并且可以阻止其进行不适当的系统调用。16对于代码搜索,在完全放弃PCRE之前,我们的计划是在类似的沙箱中至少隔离PCRE解析器。现在,另一个选择是基于轻量级虚拟机管理程序的类似沙箱的gVisor。17隔离依赖项可以降低运行该代码的相关风险。即使有了这些示例和其他现成的选项,可疑代码的运行时隔离仍然太困难,而且很少做到。