包管理器之间的代码共享
文章背景与核心概要
本文对二十个主流包管理器进行了深入的代码复用分析,揭示了这些工具之间密集的代码共享网络。研究发现,虽然部分工具直接依赖竞争对手的库或共享生态系统(如 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 声明了二十,个
uvcrate 作为其 PyPI 后端(包括uv-resolver、uv-distribution、uv-client、uv-install-wheel、uv-pep440和uv-git)。 - pnpm 依赖五个 Yarn Berry 的软件包(
@yarnpkg/core、@yarnpkg/lockfile、@yarnpkg/pnp、@yarnpkg/nm、@yarnpkg/extensions)。 - uv 引入了 Cargo 仓库中的
cargo-util。
- Pixi declares twenty-eight
uvcrates as its PyPI backend (includinguv-resolver,uv-distribution,uv-client,uv-install-wheel,uv-pep440, anduv-git).- pnpm depends on five Yarn Berry packages (
@yarnpkg/core,@yarnpkg/lockfile,@yarnpkg/pnp,@yarnpkg/nm,@yarnpkg/extensions).- uv incorporates
cargo-utilfrom 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软件包,包括semver、ssri、hosted-git-info、validate-npm-package-name、npm-registry-fetch、libnpmpublish、node-gyp和bin-links。 - Python (PyPA): 发布了
packaging(PEP 440 说明符,被 pip、Poetry 和 Conda 使用)以及pyproject-hooks(PEP 517 构建后端调用,被 pip 和 Poetry 使用)。 - 跨生态系统的 Rust Crates:
uv和Pixi都声明了 Embark Studios 的spdxcrate 用于解析许可证表达式。 - C/C++:
DNF5和Mamba都依赖于libsolv,这是语料库中唯一一个被不同注册表跨界采用的求解器库。
- JavaScript: npm, Yarn Berry, and pnpm share up to eight
npm-orgpackages in varying combinations, includingsemver,ssri,hosted-git-info,validate-npm-package-name,npm-registry-fetch,libnpmpublish,node-gyp, andbin-links.- Python (PyPA): Publishes
packaging(PEP 440 specifiers, used by pip, Poetry, and Conda) andpyproject-hooks(PEP 517 build-backend invocation, used by pip and Poetry).- Cross-ecosystem Rust Crates: Both
uvandPixideclare Embark Studios'spdxcrate for licence-expression parsing.- C/C++:
DNF5andMambaboth rely onlibsolv, the single solver library in the corpus adopted across unrelated registries.
出于引导目的的 Vendoring
Vendoring for Bootstrap Purposes
出于引导程序(bootstrapping)相关的考虑,像 pip、Bundler 和 Homebrew 这样的工具更多地通过 vendoring(内联源码)而非正式声明依赖的方式来复用代码:
For reasons related to bootstrapping, tools like
pip,Bundler, andHomebrewreuse code via vendoring rather than formal dependency declarations:
- pip 维护了一个包含诸如
packaging、pyproject_hooks、distro、platformdirs、requests等约十几个软件包的_vendor/目录。 - 对 vendored 库的安全修复是通过重新 vendoring 的提交来处理的(例如,pip 针对锁定的
vendor.txt运行nox -s vendoring)。
- pip maintains a
_vendor/directory containing packages likepackaging,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 vendoringagainst a pinnedvendor.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-32803、CVE-2021-32804、CVE-2021-37701、CVE-2021-37712、CVE-2021-37713);修复工作在一个代码库中完成,并通过版本升级被所有消费者所采用。semver(CVE-2022-25883)和 hosted-git-info(CVE-2021-23362)也经历了类似的模式。
When packages are shared, a single patch fixes vulnerabilities across multiple tools simultaneously. For instance, npm's
tarpackage 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 withsemver(CVE-2022-25883) andhosted-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 一起发布,允许其内部组件作为独立的软件包发布,供竞争对手轻松消费。
* RubyGems 和 pip 缺乏等效的机制;由 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-sourcegem 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.
脚注
- 依赖数据基于 2026 年 7 月各仓库默认分支的快照。分析的二十个包管理器涵盖了 Ruby(3)、JavaScript(3)、Rust(3)、Python(5)和 C/C++(6)。文中提及的针对 2021-2022 年 CVE 的
tar、semver和hosted-git-info关联边已在各个漏洞公告发布时针对 npm 7.20、Yarn Berry 3.0.0 以及 pnpm 的git-resolverpackage.json 进行了验证。↩
Footnotes
- 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, andhosted-git-infoedges cited against 2021–2022 CVEs were verified against npm 7.20, Yarn Berry 3.0.0, and pnpm’sgit-resolverpackage.json at the time of each respective advisory. ↩