使用 PNPM 升级传递依赖关系:修复安全漏洞而不破坏系统
修复安全漏洞可能是一项令人沮丧的任务,尤其是当它涉及传递依赖关系时。了解如何在不影响直接依赖关系的情况下升级它们。
如今,安全漏洞在软件开发中是一个常见问题。幸运的是,我们有像 GitHub Dependabot 这样的工具,可以帮助我们通过自动检测和拉取请求使我们的依赖关系保持最新。
然而,事情并不总是如预期般顺利。由于某些依赖关系是传递性的,升级它们对于这些工具来说可能很困难,因为它们不知道更改的影响,以及在出现冲突时该如何决策。我们需要手动处理这些情况。
无效的方法
- 像
pnpm up
这样的官方命令会弄乱你的pnpm-lock.yaml
文件。这方面有一个 问题,在撰写本文时仍然开放。 - 安装具有目标传递依赖的直接依赖的最新版本可能不起作用。如果直接依赖没有升级版本定义,传递依赖将不会被升级,因为它已在
pnpm-lock.yaml
文件中解析并锁定。
解决方案
overrides
字段 是 PNPM 中的一项强大功能,允许你覆盖某些版本的解析。我们将使用此功能来升级传递依赖关系,并使更改尽可能最小化。
让我们使用上述的 Dependabot 警报作为示例。它告诉我们 follow-redirects
包有一个安全漏洞,并且已修补的版本是 1.15.6
。然而,直接依赖 gatsby
使用了 axios
,而 axios
依赖于 [email protected]
。
第一步:查找传递依赖关系
有很多方法可以定位传递依赖关系,但我推荐最直接的方法:搜索 pnpm-lock.yaml
文件。
“pnpm why” 如何?
pnpm why <package>
命令确实很有用。然而,在这种情况下它可能会让你困惑。例如,当我运行 pnpm why follow-redirects
时,以下是输出的一部分:
实际上,pnpm-lock.yaml
文件中 follow-redirects
只有一个解析。pnpm why
命令可能会向你显示依赖于同一版本包的多个路径。
第二步:添加覆盖
最简单的情况是在 pnpm-lock.yaml
文件中只有一个解析。你可以将覆盖直接添加到 package.json
文件中:
如果你在一个工作区中,你应该将覆盖添加到工作区的根 package.json
文件中。
第三步:应用更改
运行 pnpm install
以应用更改。我们可以看到 follow-redirects
包升级到 1.15.6
,并且 pnpm-lock.yaml
文件的更改最小。
现在你可以从 package.json
文件中删除 overrides
字段以保持文件整洁。然后再次运行 pnpm install
以验证没有 overrides
字段时更改也可以应用。
故障排除
上述步骤是理想情况。事情不总是如预期般顺利。以下是一些故障排除提示:
移除 "overrides" 字段后版本恢复了
当依赖包具有固定版本或不包含目标版本的范围时,可能会发生这种情况。检查依赖包的 package.json
文件,在这种情况下是 axios
,看看是否适用。
如果是这样,则需要将 overrides
字段保留在 package.json
文件中,直到依赖包升级版本定义。
对于传递依赖关系有多个解析
随着项目的增长,pnpm-lock.yaml
文件中可能会出现多个对同一包的解析。例如,可能有两个主要版本的相同包 foo
:
漏洞报告显示 [email protected]
存在安全问题,并在 1.0.1
中修复,而 [email protected]
不受影响。我们不能简单地添加一个覆盖到 foo
:
- 如果我们添加覆盖
"foo": "^1.0.1"
,则[email protected]
将被降级到1.0.1
。这可能会破坏项目,因为可能会在依赖包中使用[email protected]
的某些新功能。 - 如果我们添加覆盖
"foo": "^2.0.0"
,则[email protected]
将会升级到2.0.0
。这可能会破坏项目,因为[email protected]
可能有一些重大更改。
假设 foo
遵循语义版本控制,我们可以这样添加覆盖:
这将仅会将 [email protected]
升级到 1.0.1
,并保持 [email protected]
不变。
结论
在 PNPM 直接支持升级传递依赖关系之前,overrides
字段是修复安全漏洞而不破坏系统的一个很好的解决方法。希望本文能帮你更有效地处理这些情况。在 Logto,我们使用这种方法来保持我们的依赖关系最新和安全。