Files
Zoltan Kochan 08bf69c811 fix: prevent fork-bomb during packageManager-driven version switching (#11346)
* fix: prevent fork-bomb during packageManager-driven version switching

When pnpm was installed via one method (e.g. `npm install -g pnpm@A`)
and run in a project whose package.json's packageManager field selected
a different pnpm version (pnpm@B), and a pnpm-workspace.yaml existed at
the project root, the install-child spawned by `installPnpmToTools` to
fetch pnpm@B inherited a cwd under the pnpm home directory. pnpm's
workspace walk-up from there discovered the ancestor pnpm-workspace.yaml,
adopted the root package.json, and re-triggered switchCliVersion inside
the child. Because the target tool dir had not yet been symlinked in, the
recursive installPnpmToTools call saw alreadyExisted === false and kicked
off another nested install, recursing forever at 100% CPU.

Force the install-child's environment to disable its own version handling:
- `npm_config_manage_package_manager_versions=false` (v10 setting name)
- `pnpm_config_pm_on_fail=ignore` (v11+ setting name)

Also set the v11 setting on the final spawn at the end of switchCliVersion,
so when v10 hands off to a v11 target the child's check/download paths stay
disabled regardless of which env-var convention the child reads.

Closes #11337.

* test: add v11-switch and same-version regression tests for #11337

- v11 switch with a root pnpm-workspace.yaml: covers the primary #11337
  reproducer (target major differs from running major). Before the fix this
  fork-bombed via the install-child's workspace walk-up; now it reaches the
  terminal spawn and `installPnpmToTools` completes.
- Same-version short-circuit: with a root pnpm-workspace.yaml and
  `packageManager: pnpm@<current>`, `switchCliVersion` must return at the
  `pm.version === packageManager.version` guard, and the tool dir must not
  be created. Guards against a future regression where the ancestor
  pnpm-workspace.yaml alone accidentally triggers an install.

* fix(installPnpmToTools): isolate the install-child from the caller's workspace

Pass `--ignore-workspace` to the child pnpm so it doesn't walk up from the
stage directory and adopt the caller's pnpm-workspace.yaml as its own root.
That walk-up was both (a) the mechanism that caused the #11337 fork-bomb
(the child would rediscover the caller's packageManager field and re-enter
switchCliVersion) and (b) a correctness problem in its own right: once the
child treats the caller's project as its workspace, `pnpm add` runs with
semantics that don't match an isolated tool-dir install. The env-var guards
from the previous commit stay in place as a defense-in-depth measure in
case any future code path surfaces a wantedPackageManager without going
through workspace discovery.

Also fold the new v11-switch regression into the existing v11 test rather
than adding a second v11 install, so CI doesn't fetch pnpm@11.0.0-rc.5
from the real npmjs registry twice. The tool-dir assertion in that test
now doubles as a fork-bomb regression check for the v11-target path.
2026-04-23 13:27:49 +02:00
..