跳转至

包管理器之间的代码共享

文章背景与核心概要

本文对二十个主流包管理器进行了深入的代码复用分析,揭示了这些工具之间密集的代码共享网络。研究发现,虽然部分工具直接依赖竞争对手的库或共享生态系统(如 npm 和 PyPA),但另一些工具则出于引导程序(bootstrapping)的原因采用 vendoring(内联源码)的方式复用代码。这种相互连接的状态——以及在 Git 包装器和归档解压程序等关键领域的严重孤立——直接影响了安全漏洞和 CVE 修复在整个软件供应链中的传播效率。

通过分析可以发现,代码共享能够实现“一次修补、多处受益”,而缺乏共享的领域(如 Git 参数注入漏洞)则需要各个工具进行独立且重复的排查与修复。这种现状背后的主要制约因素在于分发基础设施的差异,这也为未来提高软件供应链安全性提供了重要的改进方向。


竞争对手之间的直接依赖

Direct Dependencies Between Competitors

二十个包管理器的直接依赖关系调查显示,这些工具偶尔会直接依赖来自竞争生态系统或同行的软件包:

Investigating the direct dependencies of twenty package managers reveals that tools occasionally rely directly on packages from competing ecosystems or peers:

  • Pixi 声明了二十,个 uv crate 作为其 PyPI 后端(包括 uv-resolveruv-distributionuv-clientuv-install-wheeluv-pep440uv-git)。
  • pnpm 依赖五个 Yarn Berry 的软件包(@yarnpkg/core@yarnpkg/lockfile@yarnpkg/pnp@yarnpkg/nm@yarnpkg/extensions)。
  • uv 引入了 Cargo 仓库中的 cargo-util
  • Pixi declares twenty-eight uv crates as its PyPI backend (including uv-resolver, uv-distribution, uv-client, uv-install-wheel, uv-pep440, and uv-git).
  • pnpm depends on five Yarn Berry packages (@yarnpkg/core, @yarnpkg/lockfile, @yarnpkg/pnp, @yarnpkg/nm, @yarnpkg/extensions).
  • uv incorporates cargo-util from the Cargo repository.

生态系统标准与共享库

Ecosystem Standards and Shared Libraries

除了直接的跨项目依赖外,几个关键库在特定的语言生态系统中得到了广泛复用:

Beyond direct cross-project dependencies, several libraries are widely reused within specific language ecosystems:

  • JavaScript: npm、Yarn Berry 和 pnpm 以不同的组合共享了多达八个 npm-org 软件包,包括 semverssrihosted-git-infovalidate-npm-package-namenpm-registry-fetchlibnpmpublishnode-gypbin-links
  • Python (PyPA): 发布了 packaging(PEP 440 说明符,被 pip、Poetry 和 Conda 使用)以及 pyproject-hooks(PEP 517 构建后端调用,被 pip 和 Poetry 使用)。
  • 跨生态系统的 Rust Crates: uvPixi 都声明了 Embark Studios 的 spdx crate 用于解析许可证表达式。
  • C/C++: DNF5Mamba 都依赖于 libsolv,这是语料库中唯一一个被不同注册表跨界采用的求解器库。
  • JavaScript: npm, Yarn Berry, and pnpm share up to eight npm-org packages in varying combinations, including semver, ssri, hosted-git-info, validate-npm-package-name, npm-registry-fetch, libnpmpublish, node-gyp, and bin-links.
  • Python (PyPA): Publishes packaging (PEP 440 specifiers, used by pip, Poetry, and Conda) and pyproject-hooks (PEP 517 build-backend invocation, used by pip and Poetry).
  • Cross-ecosystem Rust Crates: Both uv and Pixi declare Embark Studios' spdx crate for licence-expression parsing.
  • C/C++: DNF5 and Mamba both rely on libsolv, the single solver library in the corpus adopted across unrelated registries.

出于引导目的的 Vendoring

Vendoring for Bootstrap Purposes

出于引导程序(bootstrapping)相关的考虑,像 pipBundlerHomebrew 这样的工具更多地通过 vendoring(内联源码)而非正式声明依赖的方式来复用代码:

For reasons related to bootstrapping, tools like pip, Bundler, and Homebrew reuse code via vendoring rather than formal dependency declarations:

  • pip 维护了一个包含诸如 packagingpyproject_hooksdistroplatformdirsrequests 等约十几个软件包的 _vendor/ 目录。
  • 对 vendored 库的安全修复是通过重新 vendoring 的提交来处理的(例如,pip 针对锁定的 vendor.txt 运行 nox -s vendoring)。
  • pip maintains a _vendor/ directory containing packages like packaging, pyproject_hooks, distro, platformdirs, requests, and roughly a dozen others.
  • Security fixes in vendored libraries are handled via re-vendor commits (e.g., pip uses nox -s vendoring against a pinned vendor.txt).

在 Ruby 生态系统中,重叠的代码非常少。ruby-macho(用于读取 Mach-O 头部)被 Homebrew 和 CocoaPods 共同使用。此前,CocoaPods 的 Molinillo 求解器曾被 Bundler(2014年)和 RubyGems(2015年)采用并 vendored,不过 Bundler 在 2022 年 12 月过渡到了 pub_grub,随后 RubyGems 也在 2026 年 6 月进行了同样的迁移。

Within the Ruby ecosystem, overlap is sparse. ruby-macho (for reading Mach-O headers) is shared by Homebrew and CocoaPods. Previously, CocoaPods’ Molinillo resolver was vendored by Bundler (2014) and RubyGems (2015), though Bundler transitioned to pub_grub in December 2022, followed by RubyGems in June 2026.

除了 JavaScript 之外,没有任何两个包管理器共享带有路径检查功能的归档解压程序,也没有任何包管理器共享 Git 子进程包装器(Pixi 对 uv-git 的依赖除外)。

Outside of JavaScript, no two package managers share an archive extractor with path checks, nor do any share a git subprocess wrapper (aside from Pixi's reliance on uv-git).

对安全漏洞和 CVE 的影响

Impact on Security Vulnerabilities and CVEs

大多数反复出现的客户端漏洞类型都源于重复实现的逻辑操作: * 归档解压程序中的路径遍历(Path traversal) * Git 包装器中的参数注入(Argument injection) * 版本范围解析器中的 ReDoS(正则表达式拒绝服务) * 注册表 HTTP 客户端中的凭据泄漏 * 下载期间完整性检查失败时直接放行(Failing open)

Most recurring client-side bug classes stem from reimplemented operations: * Path traversal in archive extractors * Argument injection in git wrappers * ReDoS in version-range parsers * Credential leaks in registry HTTP clients * Integrity checks failing open during downloads

当软件包被共享时,单次修补即可同时修复多个工具中的漏洞。例如,npm 的 tar 软件包在 2021 年遭受了五次路径遍历漏洞公告的影响(CVE-2021-32803CVE-2021-32804CVE-2021-37701CVE-2021-37712CVE-2021-37713);修复工作在一个代码库中完成,并通过版本升级被所有消费者所采用。semverCVE-2022-25883)和 hosted-git-infoCVE-2021-23362)也经历了类似的模式。

When packages are shared, a single patch fixes vulnerabilities across multiple tools simultaneously. For instance, npm's tar package suffered from five path-traversal advisories in 2021 (CVE-2021-32803, CVE-2021-32804, CVE-2021-37701, CVE-2021-37712, CVE-2021-37713); fixes were made in one codebase and adopted by all consumers via version bumps. Similar patterns occurred with semver (CVE-2022-25883) and hosted-git-info (CVE-2021-23362).

相反,对 --end-of-options Git 参数注入 CVE 的一项调查追踪了跨越六个工具(Bundler、Composer、CocoaPods、Poetry、pip 和 Go)历时五年的漏洞历程。由于这些实用工具缺乏共享代码或通用库,每个工具的漏洞都是完全独立地被发现、报告和修补的——这导致某些工具在攻击模式首次公开后几年内依然容易受到攻击。

Conversely, a survey of --end-of-options git argument-injection CVEs traced vulnerabilities across six tools (Bundler, Composer, CocoaPods, Poetry, pip, and Go) over a span of five years. Because these utilities lacked shared code or a common library, each tool was discovered, reported, and patched completely independently—leaving some vulnerable for years after the attack pattern was first publicized.

结构性制约因素

Structural Constraints

为什么代码共享在某些领域能够成功而在另一些领域失败,通常取决于分发基础设施: * npm 随 Node 一起发布,允许其内部组件作为独立的软件包发布,供竞争对手轻松消费。 * RubyGemspip 缺乏等效的机制;由 RubyGems 团队发布的 git-source gem 会成为 RubyGems 自身无法声明的依赖项。 * 那些具有独立工具级漏洞公告流的操作(例如 Git 调用、经过检查的归档解压、以及“下载-验证-缓存”流程)缺乏专门的标准所有者或集中的共享库,导致各个包管理器不得不重复造轮子,并频繁犯下相同的安全错误。

Why code sharing succeeds in some areas and fails in others often comes down to distribution infrastructure: * npm ships inside Node, allowing its internals to be published as standalone packages that competitors can easily consume. * RubyGems and pip lack an equivalent mechanism; a git-source gem published by the RubyGems team would be a dependency RubyGems itself could not declare. * Operations with per-tool advisory streams (such as git invocation, checked archive extraction, and download-verify-cache) lack dedicated spec owners or centralized shared libraries, leaving individual package managers to reinvent the wheel—and frequently repeat the same security mistakes.


脚注

  1. 依赖数据基于 2026 年 7 月各仓库默认分支的快照。分析的二十个包管理器涵盖了 Ruby(3)、JavaScript(3)、Rust(3)、Python(5)和 C/C++(6)。文中提及的针对 2021-2022 年 CVE 的 tarsemverhosted-git-info 关联边已在各个漏洞公告发布时针对 npm 7.20、Yarn Berry 3.0.0 以及 pnpm 的 git-resolver package.json 进行了验证。

Footnotes

  1. Dependency data is based on a July 2026 snapshot of each repository’s default branch. The twenty analyzed package managers cover Ruby (3), JavaScript (3), Rust (3), Python (5), and C/C++ (6). The tar, semver, and hosted-git-info edges cited against 2021–2022 CVEs were verified against npm 7.20, Yarn Berry 3.0.0, and pnpm’s git-resolver package.json at the time of each respective advisory.