fix(security): backport path-traversal and containment fixes to v10#12504
Conversation
Backports three merged security fixes from main to release/10: - Contain hoisted dependency aliases so a crafted lockfile alias cannot escape node_modules or overwrite pnpm-owned layout. The hoisted graph builder now validates each alias at the safeJoinModulesDir sink. (GHSA-fr4h-3cph-29xv, #12343) - Contain pnpm patch-remove deletions to the configured patches directory. (#12341) - Reject path-traversal config dependency names and versions from pnpm-workspace.yaml before they are used to build filesystem paths. (GHSA-qrv3-253h-g69c, #12470) Written by an agent (Claude Code, claude-opus-4-8).
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
PR Summary by QodoBackport v10 security fixes for traversal-safe installs and patch removal Description
Diagram
High-Level Assessment
Files changed (19)
|
Code Review by Qodo
1. Misleading directory guarantee comment
|
|
Code review by qodo was updated up to the latest commit 9c9ff42 |
|
🚢 10.34.4 |
Summary
Backports three merged security fixes from
maintorelease/10. Each keeps the original fix's behavior but is adapted to v10's package layout (no pacquet, no env-lockfile, noverifyLockfileResolutionsgate).1. Contain hoisted dependency aliases — GHSA-fr4h-3cph-29xv (port of #12343)
On a frozen/up-to-date lockfile the
nodeLinker: hoistedrestore skips resolution, so the alias validation added in #11954 never ran. A crafted lockfile alias (../../../escape) could be joined directly under a hoistednode_modulesdirectory, and reserved aliases (.bin,.pnpm,node_modules) could overwrite pnpm-owned layout.fs/symlink-dependency/safeJoinModulesDir.tsnow rejects aliases that are not valid npm package names (path-traversal, absolute, reserved) viavalidate-npm-package-name, in addition to its existing containment check, and is exported.dep.namesink inpkg-manager/headless/lockfileToHoistedDepGraph.tsnow goes throughsafeJoinModulesDirinstead of a rawpath.join.v10 has no
verifyLockfileResolutions, so the upstream layer-2 verifier gate does not apply; the sink guard (which both the isolated and hoisted linkers route through) closes the exploit.2. Contain
patch-removedeletions (port of #12341)pnpm patch-removenow validates the configured patches directory and the full removal batch (canonicalizing parents, rejecting directory entries and nested-symlink escapes, unlinking final symlinks without following them) before unlinking any patch file, so it can't delete files outside the configured patches directory.3. Reject path-traversal config dependency names/versions — GHSA-qrv3-253h-g69c (port of #12470)
Config dependency names and versions from
configDependenciesinpnpm-workspace.yamlbecome path segments ofnode_modules/.pnpm-config/<name>and the store's<name>/<version>/<hash>, but were used unvalidated.config/deps-installer/normalizeConfigDeps.ts(the install-boundary chokepoint, before any path is built) now requires names to be valid npm package names and versions to be exact semver.__proto__is rejected (leading_). v10 has no env lockfile / optional config-subdeps, so the validation is scoped to the workspace-manifest config deps.Tests
@pnpm/symlink-dependency: helper tests extended with reserved-name aliases + valid controls (24 passed).@pnpm/headless: newlockfileToHoistedDepGraphexploit-regression test — traversal/reserved aliases rejected before any fetch or filesystem work (6 passed).@pnpm/plugin-commands-patching:isSubdirectory+patchRemovecontainment tests (9 passed); existingpatch.test.tsstill green (34 passed).@pnpm/config.deps-installer: traversal-name,__proto__, and traversal-version regression tests (12 passed incl. existing).eslint,lint:meta(meta-updater), andcspellall clean.Written by an agent (Claude Code, claude-opus-4-8).