Commit Graph

10332 Commits

Author SHA1 Message Date
Zoltan Kochan
4f7fd82d3e fix: update embedded Node.js release keys
nodejs/release-keys added a new release key (Stewart X Addison,
655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD), which made the
check:node-release-keys release gate fail. Sync the embedded keys
via update-node-release-keys.mjs --update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 13:07:04 +02:00
Zoltan Kochan
e5dce41eab chore(release): 10.34.5 (#12900) 2026-07-10 12:26:37 +02:00
Zoltan Kochan
36928beae9 fix(config): do not expand env vars in proxy fields from pnpm-workspace.yaml (#12898)
Backport of 5a4daec4bd from main.

Project-level pnpm-workspace.yaml is repository-controlled and therefore
untrusted, so getOptionsFromPnpmSettings refuses to expand ${...}
environment-variable placeholders in request-destination fields. The guarded
set only covered registry; the httpProxy, httpsProxy, noProxy, proxy, and
noproxy fields were missing, so a malicious repository could route requests
through an attacker-controlled proxy whose URL embedded a secret and
exfiltrate install-time secrets before any lifecycle script runs.

Add the proxy fields to REQUEST_DESTINATION_SCALAR_KEYS so they receive the
same protection already applied to the registry channel.

Reported by YESHYUNGSEOK as flaw F01 of GHSA-vx52-2968-3vc6.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:46:20 +02:00
Zoltan Kochan
78e29fe558 fix(security): backport lockfile & manifest name/slot containment to v10 (#12890)
Backports the TypeScript portion of 51300fd41c (#12872) to release/10.
Two advisories:

- GHSA-c59q-g84q-2gj5: lockfile depPath name -> isolated linker + PnP.
- GHSA-vq4v-j7r6-jq4m: dependency manifest `name` from the fetched
  tarball (e.g. @x/../../../path) -> resolver/linker.

Apply safeJoinModulesDir at lockfileToDepGraph (deps/graph-builder) and
the PnP packageLocation (lockfile-to-pnp); validate manifest names in
resolve-dependencies (extendGraph + resolvePeers); contain the
global-virtual-store slot dir by rejecting a traversal version segment
in iterateHashedGraphNodes (calc-dep-state), the single point every GVS
slot path funnels through.

The pacquet and env-installer changes from the original commit do not
apply to v10 (no pacquet stack; installConfigDeps has no GVS subdep
installation and config-dependency names are already validated by
assertValidConfigDepName from #12504).

All rejections surface ERR_PNPM_INVALID_DEPENDENCY_NAME.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:50:19 +02:00
Zoltan Kochan
47ef6f04ed fix: allow pnpm v10 to switch/self-update to pnpm v12 (#12835)
* fix: allow pnpm v10 to switch/self-update to pnpm v12

pnpm v12 (the Rust port) publishes the `pnpm` and `@pnpm/exe` packages
with placeholder bins that a preinstall script replaces with the host's
native binary from a `@pnpm/exe.<platform>-<arch>[-musl]` optional
dependency. Because pnpm installs its own engine with --ignore-scripts,
that relinking never ran, leaving a non-executable placeholder and a
broken switch/self-update.

installPnpmToTools now:
- converges on the native `pnpm` package for v12+ (equal content to
  `@pnpm/exe`, ships every target incl. darwin-x64);
- relinks the native binary for any wrapper that ships one (`@pnpm/exe`
  for all majors and, from v12, `pnpm`), recognizing both the legacy
  `@pnpm/<os>-<arch>` and the new `@pnpm/exe.<platform>-<arch>[-musl]`
  platform-package naming; and

verifyPnpmEngineIdentity now verifies the native platform package's npm
registry signature for v12's `pnpm` wrapper, not only `@pnpm/exe".

* fix(security): verify pnpm engine before relinking and sanitize wrapper bin names

Addresses a Windows-only pre-verification write primitive: linkExePlatformBinary
read bin *keys* from the not-yet-verified wrapper package.json and used them as
path.join destinations. A crafted bin map (e.g. '../../evil') could unlink/hardlink
outside the staged dir before verifyPnpmEngineIdentity ran.

- Run verifyPnpmEngineIdentity before linkExePlatformBinary so no file whose name
  derives from the wrapper's own manifest is mutated until the wrapper is proven
  to be a genuine, signed pnpm release.
- Add safeWrapperBinNames() as defense in depth: relink only bin names that
  resolve to a direct child of the wrapper dir (no '..', separators, or absolute
  paths).
2026-07-07 11:39:02 +02:00
Zoltan Kochan
e28b9cb47d chore(release): 10.34.4 (#12511) v10.34.4 2026-06-19 00:16:04 +02:00
Zoltan Kochan
a748181783 fix: resolve pnpm audit failures on release/10 (#12509)
Bump tmp (>=0.2.7), tar (>=7.5.16), and @babel/core (>=7.29.6) to patched
versions, and ignore two dev-only advisories with no compatible patch
(js-yaml GHSA-h67p-54hq-rp68, http-proxy-middleware GHSA-64mm-vxmg-q3vj).
2026-06-19 00:12:21 +02:00
Zoltan Kochan
352ae489f1 fix(security): backport path-traversal and containment fixes to v10 (#12504)
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).
2026-06-18 21:54:19 +02:00
Zoltan Kochan
217fbe0972 fix: only suggest a pnpm config set command for shell-safe keys (#12335)
The key embedded in the warning's suggested 'pnpm config set' command comes
from a repository-controlled .npmrc. The previous guard only suppressed the
example for keys containing a ${...} placeholder, but a shell also expands
$(...), backticks and $VAR inside double quotes — so a crafted key could turn
the suggested copy-paste command into command execution. The example is now
emitted only for keys made up entirely of shell-inert characters.
2026-06-11 20:13:13 +02:00
Zoltan Kochan
c7c97c30df chore(release): 10.34.3 (#12332) v10.34.3 2026-06-11 18:45:16 +02:00
Zoltan Kochan
598ea3f717 fix: clearer warning when a project .npmrc uses env variables in registry/auth settings (#12331)
The previous warning only said the setting was ignored. It now explains why
(the project .npmrc is committed to the repository and must not expand secrets
into request destinations or credentials) and how to fix it: move the value to a
trusted source such as the user-level ~/.npmrc or via pnpm config set, with a
link to the docs.

The suggested 'pnpm config set' example is only shown when the key has no
${...} placeholder, so the snippet is always safe to copy-paste (a shell would
otherwise expand a placeholder embedded in the key). The wording no longer
claims a specific destination file, since pnpm config set delegates registry
and auth keys to npm.
2026-06-11 18:37:46 +02:00
Zoltan Kochan
f85557b73f fix: upgrade @pnpm/npm-conf to 3.0.3 to block trusted config redirect (#12328)
A repository-controlled project or workspace .npmrc could set userconfig,
globalconfig, or prefix to redirect pnpm into loading an attacker-supplied,
repo-shipped file as a trusted user/global config source. Such sources are not
subject to the untrusted-env-expansion filtering added for GHSA-3qhv-2rgh-x77r
and may set tokenHelper, so this was a bypass of that fix that could exfiltrate
environment secrets to an attacker-selected registry.

@pnpm/npm-conf@3.0.3 resolves the user/global config destinations from trusted
layers (cli, env, builtin, defaults) before the project/workspace .npmrc files
are added to the config chain.
2026-06-11 17:22:11 +02:00
Zoltan Kochan
343441ca62 chore(release): 10.34.2 (#12311) v10.34.2 2026-06-10 15:40:37 +02:00
Zoltan Kochan
14bceb1e0b fix(security): harden build-approval artifact identities on v10 (#12306)
* fix(security): harden build-approval artifact identities on v10

Port of pnpm/pnpm#12294 (commit bf1b731ee6) to release/10.

Package-name entries in onlyBuiltDependencies (and allowBuilds) no longer
approve lifecycle scripts for artifacts whose identity a name cannot pin:
git, git-hosted tarball, direct tarball, and local directory dependencies.
To approve such an artifact explicitly, use its peer-suffix-free lockfile
depPath as the key — the GIT_DEP_PREPARE_NOT_ALLOWED hint, pnpm
ignored-builds, and pnpm approve-builds print exactly that key.

- AllowBuild policy functions identify packages by DepPath instead of
  caller-supplied name/version. Identity trust is derived from the depPath
  shape: a registry-style depPath (name@semver) is a trusted identity.
- The trust is sound because lockfile entries are structurally checked
  wherever they are materialized into fetchable resolutions
  (pkgSnapshotToResolution) and before rebuild runs scripts: a
  registry-style key backed by a git, directory, or git-hosted tarball
  resolution is rejected with ERR_PNPM_RESOLUTION_SHAPE_MISMATCH.
- preparePackage requires a pkgResolutionId and gates on the synthesized
  name@<resolution id> depPath; scp-style git URLs are normalized to
  ssh:// form in resolution ids and the git fetcher reuses
  createGitHostedPkgId from the resolver.
- isGitHostedTarballUrl matches case-insensitively and is shared from
  @pnpm/lockfile.utils; new removePeersSuffix() in @pnpm/dependency-path
  and allowBuildKeyFromIgnoredBuild() in @pnpm/builder.policy.
- pnpm rebuild and approve-builds accept depPath specs for selecting and
  approving artifact builds; installs rebuild ignored builds approved by
  depPath keys.
- shell-quote is overridden to 1.8.4 (GHSA-w7jw-789q-3m8p /
  CVE-2026-9277).

Differences from main: v10 has no lockfile resolution verifier, so the
structural pass lives in pkgSnapshotToResolution and the rebuild loop; the
AllowBuild policy keeps v10's boolean return; the revoked-approval
detection and global-virtual-store hash changes have no v10 counterpart.

* test: serve the direct-tarball fixture from an off-registry origin

The registry mock binds only localhost, so a hard-coded 127.0.0.1 URL is
refused in CI, and a tarball URL on the registry's own origin resolves as
a registry package, which defeats the direct-tarball identity the test
needs. An in-test HTTP server redirecting to the mock provides a separate
origin whose binding the test controls.

* test: approve the git-protocol licenses fixture by its depPath

A git-hosted artifact has an untrusted package identity, so the
ajv-keywords build has to be approved by its depPath. Pin the fixture to
the commit the depPath names.
2026-06-10 15:10:00 +02:00
Zoltan Kochan
c452019ac0 fix(security): port the latest security fixes to v10 (#12300)
* fix(package-bins): reject reserved manifest bin names

Manifest bin keys "", ".", "..", and scoped forms such as "@scope/.."
passed the bin-name guard because encodeURIComponent leaves them
unchanged. When joined to the global bin directory during global
remove/update/add operations, "." resolves to the bin directory itself
and ".." to its parent, which removeBin then recursively deletes.

Reject empty, ".", and ".." bin names after scope stripping.

Backport of pnpm/pnpm#12289 to v10.

* fix: block untrusted request destination env expansion

Makes environment expansion trust-aware for registry/auth config and
request destinations:

- Stops project and workspace .npmrc files from expanding ${...}
  placeholders in registry/proxy request destinations, URL-scoped keys,
  and registry credential values.
- Stops repository-controlled pnpm-workspace.yaml from expanding
  ${...} placeholders in the registry setting.
- Preserves env expansion for trusted user/global/CLI/env config so
  existing token and registry setup flows continue to work.

Backport of pnpm/pnpm#12291 (CAND-PNPM-122 / GHSA-3qhv-2rgh-x77r) to v10.

* fix(security): verify npm registry signature before spawning a package-manager binary

The packageManager field (and pnpm self-update) makes pnpm download and
run a specific pnpm version. The staged install's bytes were trusted
based on lockfile integrity alone, which proves nothing when the inputs
are repository-controlled.

pnpm now verifies the npm registry signature of the engine it is about
to spawn, over the installed integrity, against npm's public signing
keys embedded in the pnpm CLI (exactly as corepack does):

- verifyPnpmEngineIdentity() checks pnpm/@pnpm/exe and the materialized
  platform binaries of the staged install before it is linked into the
  tools directory.
- Fails closed: any verification failure, including an unreachable
  registry, refuses the version switch rather than running an unverified
  binary. Runs only on a tools-directory cache miss (an actual
  download).
- The embedded keys live in a generated file kept in sync with npm's
  keys endpoint by scripts/update-npm-signing-keys.mjs; the release
  workflow runs the check as a gate so a key rotation cannot silently
  break verification.

Backport of pnpm/pnpm#12292 (CAND-PNPM-097) to v10.

* fix: harden package-manager bootstrap metadata

Resolve package-manager bootstrap traffic through trusted user/CLI
registries and trusted network config, defaulting to the public npm
registry instead of project/workspace registry settings:

- getConfig() now computes packageManagerRegistries and
  packageManagerNetworkConfig from trusted config sources only (CLI
  options, env config, user and global .npmrc) — never the repository's
  project/workspace .npmrc or pnpm-workspace.yaml.
- switchCliVersion() applies that bootstrap config when installing and
  verifying the wanted pnpm version, so repository .npmrc
  proxy/TLS/registry values cannot steer package-manager bootstrap
  traffic.

Backport of pnpm/pnpm#12296 to v10. The v11 env-lockfile validation
parts do not apply: v10 bootstraps the wanted version through a staged
child install instead of an env lockfile.

* fix(security): verify Node.js runtime SHASUMS OpenPGP signature

When a repository requests a Node.js runtime (useNodeVersion or an
execution env), pnpm downloads and then executes a Node binary. The
download mirror is repository-configurable via node-mirror:<channel> in
project .npmrc, and the integrity came from SHASUMS256.txt fetched from
that same mirror — a circular check a malicious mirror can satisfy with
a tampered binary and matching hashes.

pnpm now fetches SHASUMS256.txt.sig and verifies its detached OpenPGP
signature against the Node.js release team's public keys, embedded in
the pnpm CLI, before trusting the hashes:

- @pnpm/crypto.shasums-file: new fetchVerifiedNodeShasums /
  fetchVerifiedNodeShasumsFile verify the signature via openpgp against
  the embedded keys (generated src/nodeReleaseKeys.ts, mirrored from
  the canonical nodejs/release-keys list).
- @pnpm/node.fetcher verifies the configurable-mirror SHASUMS for the
  release channel; pre-release channels (rc, nightly, ...) are unsigned
  by Node and remain unverified.
- scripts/update-node-release-keys.mjs keeps the keys current
  (pnpm run check:node-release-keys / update:node-release-keys), and
  the release workflow runs the check as a gate.

Backport of pnpm/pnpm#12295 to v10 (without the pacquet Rust port,
which does not exist on this branch).

* test(env): sign the SHASUMS fixture for Node.js download tests

The Node.js download tests exercise the release channel, whose
SHASUMS256.txt is now signature-verified. Sign the fixture with a
generated OpenPGP key and trust it through the new
trustedNodeReleaseKeys test seam (threaded from plugin-commands-env via
@pnpm/node.fetcher to fetchVerifiedNodeShasums), so the tests keep
exercising the verification path instead of bypassing it.

* fix(self-updater): redact registry credentials from engine identity errors

Registry URLs may legally embed basic-auth credentials
(https://user:pass@host/). verifyPnpmEngineIdentity() interpolated the
packument URL and registry URL into PnpmError messages, and the
unreachable-registry path surfaced fetch-layer error messages that embed
the request URL — all of which land in terminal output and CI logs.
Strip URL credentials from every error message and truncate the non-200
response body.

* fix: update vulnerable transitive dependencies

Override shell-quote to >=1.8.4 (GHSA-w7jw-789q-3m8p, critical, pulled
in via concurrently) so the audit workflow passes again. The advisory
was published after the last release/10 audit run; it is unrelated to
the security backports on this branch.
2026-06-10 08:01:14 +02:00
Zoltan Kochan
3937b441cb chore(release): 10.34.1 (#12008) v10.34.1 2026-05-28 00:51:17 +02:00
Zoltan Kochan
b8196b82d1 fix: require integrity for tarball-shaped lockfile resolutions (backport #11966 to v10) (#12007)
A tampered `pnpm-lock.yaml` that strips the `integrity` field from a
tarball resolution let the worker download the URL contents and mint a
fresh integrity from the unverified bytes. An attacker who could both
alter the lockfile (e.g. via a pull request that drops `integrity:`)
and serve modified content at the referenced tarball URL could install
a tampered package without any error — including under
`--frozen-lockfile`.

`pkgSnapshotToResolution` now fails closed at lockfile-read time with
`ERR_PNPM_MISSING_TARBALL_INTEGRITY` whenever a tarball-shaped
resolution (no `type` field — covers plain remote, registry-derived,
`file:`, and `gitHosted` entries) lacks integrity. Git-hosted
tarballs and `file:` tarballs remain exempt: the commit SHA in a
git-host URL and the user-controlled local path already anchor the
bytes.

The fix sits at the lockfile-read chokepoint every install path flows
through (deps-resolver, deps-restorer, graph-builder), so both
isolated and hoisted node-linkers are covered.

Credit to AutoFyn for finding and reporting the issue.
2026-05-28 00:44:13 +02:00
Zoltan Kochan
df3b5065be ci(release): pin Node 20 for v10 release workflow (#11997)
The `pnpm/bundle` step runs `ts-node bundle.ts`, which transitively
loads `esm@3.2.25` (via `publish-packed` → `all-module-paths` → `esm`).
That package monkey-patches Node internals removed in Node 22+. The
release workflow runs on `ubuntu-latest` and was relying on the
runner's default Node; GitHub bumping that default between 10.33.4
and 10.34.0 silently broke the publish phase.

Pin Node 20 — the last release line `esm@3.2.25` still works on — and
drop `continue-on-error` from the Publish step so future failures fail
the workflow instead of hiding behind a green check.
v10.34.0
2026-05-27 16:52:39 +02:00
Zoltan Kochan
8be85cbbd6 ci(release): clone ldid from ProcursusTeam GitHub mirror (#11990)
The upstream `https://gitlab.com/opensource-saurik/ldid.git` is no
longer publicly accessible (returns 401 / redirects to sign-in), so
`git clone` prompts for a username and fails non-interactively with
`could not read Username for 'https://gitlab.com'`. This blocks every
v10 release.

`ProcursusTeam/ldid` is a maintained GitHub fork that contains the
exact commit the workflow pins (c2f8abf), and the libplist submodule
already points at github.com, so a one-line URL swap is the full fix.

---
Written by an agent (Claude Code, claude-opus-4-7).
2026-05-27 15:23:42 +02:00
Zoltan Kochan
1de167838c chore(release): 10.34.0 (#11988) 2026-05-27 14:58:46 +02:00
Zoltan Kochan
99cdedc235 fix(config): pin unscoped per-registry settings to their source's registry at load time (backport #11953 to v10) (#11986)
* fix(config): pin unscoped per-registry settings to their source's registry at load time

Each .npmrc / auth.ini / CLI source's unscoped per-registry settings
(_authToken, _auth, username/_password, tokenHelper, inline cert/key)
are rewritten to their URL-scoped equivalent during load, using the
same source's registry= value (or the npmjs default if none). After
this rewrite the merged config contains only URL-scoped settings, so a
later layer overriding registry= (workspace .npmrc, pnpm-workspace.yaml,
CLI --registry) cannot rebind a credential or client certificate to a
different host.

Each rescope emits a deprecation warning naming the source and the URL
the setting was pinned to. ca/cafile are intentionally not rescoped —
they're trust anchors, not credentials, and corporate MITM-proxy setups
rely on them applying globally.

Ported from #11953. Reported by JUNYI LIU.

---
Written by an agent (Claude Code, claude-opus-4-7).

* chore(changeset): bump @pnpm/network.auth-header to major

Removing the unscoped credential fallback in getAuthHeadersFromConfig
is a breaking change to the package's contract — callers passing
unscoped _authToken/_auth/username+_password/tokenHelper alongside a
default registry no longer get a header back. Matches the major bump
in #11953.

---
Written by an agent (Claude Code, claude-opus-4-7).
2026-05-27 14:37:42 +02:00
Zoltan Kochan
6c0f2940c3 fix: fail by default when a tarball does not match the locked integrity (#11985)
Backport of #11968 to release/10. Treats tarball-integrity mismatches
against the lockfile as a hard failure by default; `--update-checksums`
is the only opt-in. `--force` and `pnpm update` deliberately do not
bypass the integrity check.

🤖 Written by an agent (Claude Code, claude-opus-4-7).
2026-05-27 14:25:11 +02:00
Zoltan Kochan
19f7df1461 fix(git-fetcher): reject non-SHA commit values before invoking git
The git fetcher passed the lockfile-controlled `resolution.commit` value
to `git fetch --depth 1 origin <commit>` and `git checkout <commit>`
without a `--` separator and without validating that the value is a
SHA. A malicious lockfile could smuggle a value such as
`--upload-pack=touch /tmp/pwned`, which `git` parses as an option. On
SSH and local-file transports `--upload-pack` runs the supplied
command. HTTPS ignores it, but the SSH/file paths are enough to
execute code as the user running `pnpm install`.

Validate `resolution.commit` against `/^[0-9a-f]{40}$/i` at the entry
of the fetcher and throw `INVALID_GIT_COMMIT` otherwise. The check is
strictly stronger than adding a `--` separator: a validated value
cannot start with `-` or contain shell-significant characters at all.

Backport of #11967 to release/10.

---
Written by an agent (Claude Code, claude-opus-4-7).
2026-05-27 14:13:35 +02:00
Zoltan Kochan
455cfcd84b fix: reject path-traversal segments in dependency aliases
A transitive registry package can use a dependency-alias key like
`@x/../../../../../.git/hooks` to make `pnpm install` create a symlink
outside the intended `node_modules` directory, since pnpm passes the
alias straight into `path.join(modulesDir, alias)` without checking
that the joined path stays inside `modulesDir`.

Reject aliases that aren't valid npm package names at manifest-read
time (both the importer's manifest and every transitive package
manifest) and re-check at the symlink layer as defense in depth.

Backport of #11954 to the release/10 line.

---
Written by an agent (Claude Code, claude-opus-4-7).
2026-05-27 13:19:21 +02:00
Dharma Kanneganti
e4fa829765 fix: update vulnerable transitive dependencies (#11979) 2026-05-27 12:50:10 +02:00
Zoltan Kochan
6481f6c161 fix(patching/apply-patch): reject patch paths that escape the patched directory (#11952)
* fix(patching/apply-patch): reject patch paths that escape the patched directory

A malicious .patch file with `diff --git a/../../X` headers could otherwise
write, delete, or rename files outside the patched package as the user
running `pnpm install`.

* refactor(patching/apply-patch): narrow caught errors via util.types.isNativeError

Drops the `any`-typed catch + eslint-disable in favor of the cross-realm-safe
narrowing pattern documented in CLAUDE.md.

* refactor(patching/apply-patch): replace error helper with PatchPathEscapesError class

* chore(patching/apply-patch): reword comment to satisfy cspell
2026-05-26 12:52:59 +02:00
Zoltan Kochan
4a04433833 fix(npm-resolver): minimumReleaseAge handling for cached abbreviated metadata (#11630)
Backport of #11622 to release/10. The npm registry returns abbreviated
package metadata (without per-version `time`) by default, which made the
maturity check throw ERR_PNPM_MISSING_TIME whenever cached abbreviated
metadata was reused under `publishedBy`/`minimumReleaseAge`. pnpm now
upgrades cached abbreviated metadata to the full document via a follow-up
fetch, persists the upgrade to the on-disk cache so subsequent installs
skip the extra fetch, and lets ERR_PNPM_MISSING_TIME from the cache fast
path fall through to the network fetch even under strict mode.

Adapted to release/10's simpler pickPackage shape (no 304/etag plumbing,
no `pickMatchingVersionFast`/`pickMatchingVersionFinal` split, no
`ignoreMissingTimeField`), so the unconditional helper triggers the
upgrade whenever the cached meta lacks `time`.
2026-05-14 16:04:35 +02:00
Zoltan Kochan
80306c4043 fix: honor --prefix when resolving workspace dir (#11550)
The `--prefix` option (renamed to `dir`) was applied to options after
`getWorkspaceDir` had already run, so workspace detection fell back to
`process.cwd()` and missed the manifest at the prefix dir. As a result,
running pnpm from outside the project (e.g. `pnpm --prefix=child install`
from the parent dir) silently skipped settings declared in
`child/pnpm-workspace.yaml`.

Backport of #11549 to release/10. Closes #11535.
2026-05-12 10:15:59 +02:00
Zoltan Kochan
ff3304fcb3 chore(release): 10.33.4 v10.33.4 2026-05-06 15:00:18 +02:00
Zoltan Kochan
edbe2a7c9b fix: pin integrity of git-hosted tarballs in lockfile (#11491)
Cherry-pick of #11481 from main, adapted to the v10 layout.

For git-hosted tarballs (codeload.github.com / gitlab.com / bitbucket.org)
the fetcher dropped the integrity it computed while downloading, so the
lockfile only stored the URL. A compromised git host or man-in-the-middle
could serve a substituted tarball on subsequent installs and pnpm would
install it without lockfile changes.

This pins the SHA-512 SRI of the raw tarball in the lockfile in the same
sha512-<base64> form npm-registry tarballs use; subsequent installs verify
the download against that integrity in the worker.

A new optional gitHosted: boolean field is recorded on TarballResolution
so every store-key consumer can route by a single typed read instead of
re-deriving the routing from the URL. Lockfiles written by older pnpm
versions are enriched on load (URL fallback) so the field can be relied
on uniformly.

🤖 Cherry-picked by Claude (claude-opus-4-7) on behalf of @zkochan
2026-05-06 14:28:42 +02:00
Zoltan Kochan
c39f677b6e chore: update dependencies 2026-05-06 14:13:13 +02:00
Zoltan Kochan
216dab5e52 fix: keep workspace-root excluded when --recursive --filter uses only negative filters (#11480)
* fix: include workspace root only when --filter is positive

PR #10465 stopped the implicit workspace-root exclusion for `pnpm -r
run/exec/test/add` whenever any --filter was provided. That gate was
too broad: with a negative-only filter set (e.g. `--filter '!a'`) the
workspace root started showing up in the matched projects, contradicting
the documented default behavior.

Only suppress the implicit root exclusion when the user provided at
least one positive (non-`!`) filter that could have been intended to
select the root. Negative-only filter sets keep the documented default,
and `--include-workspace-root` continues to opt the root in explicitly.

Close #11341.

* test: fix root-exclusion assertion to match --stream prefix

The --stream reporter prefixes output lines with the project's relative
directory, so the workspace root appears as `. which$`, not
`root which$`. The original assertion would have passed even if the
regression were still present. Verified the corrected assertion fails
against the buggy code and passes against the fix.
2026-05-06 14:07:50 +02:00
Zoltan Kochan
198fb7c7b6 chore: update branch references after v10 → release/10 rename 2026-05-05 00:18:03 +02:00
Zoltan Kochan
b4ee05e680 chore: publish library packages with latest-10 dist-tag
Now that v11 is stable, library packages from the v10 branch should
publish to the latest-10 tag instead of overwriting latest.
2026-05-04 23:50:54 +02:00
Zoltan Kochan
d4f0fffaaf chore(release): 10.33.3 v10.33.3 2026-05-04 23:07:56 +02:00
Zoltan Kochan
3349432a0f fix(self-update): do not downgrade when latest dist-tag is older (#11462)
`pnpm self-update` defaults to the `latest` dist-tag, but `latest` on the
registry can lag the installed version when a new major has shipped
without being tagged. Refuse to downgrade in that case. Users can still
run `pnpm self-update latest` (explicit) to force the downgrade.

Closes #11418
2026-05-04 23:01:07 +02:00
Zoltan Kochan
c54bcfd424 fix(self-update): swap to pnpm package on Intel Mac when upgrading to v11+ (#11458)
When self-updating from a v10 @pnpm/exe install on Intel macOS
(darwin-x64) to a v11+ version, transparently install the JS-only
"pnpm" npm package instead of @pnpm/exe.

@pnpm/exe v11+ no longer ships a darwin-x64 binary because Node.js
SEA injection produces a binary that segfaults on Intel Macs (upstream
nodejs/node#62893; pnpm/pnpm#11423). Without this swap, self-update
would resolve @pnpm/exe@11.x, install a manifest with no platform
binary, and leave the user with a broken bin shim.

The new install requires Node.js on PATH; a globalWarn explains the
swap when it happens. All other host/version combinations are
unchanged. The targetPkgName is also threaded through getToolDirPath,
the `pnpm add` command, and the linkExePlatformBinary guard so the
new install lives under the correct tool directory and the
@pnpm/exe-specific platform-binary linking is skipped for the JS path.
2026-05-04 21:27:49 +02:00
Zoltan Kochan
bebe10647c feat(self-update): point users at the migration guide across major bumps (v10) (#11355)
* feat(self-update): point users at the migration guide across major bumps

When `pnpm self-update <version>` crosses a pnpm major (upward) from
the version being upgraded from, print a one-line pointer to the
versioned migration guide on pnpm.io.

The "from" version is the project's `packageManager` pin when present
(via the existing `managePackageManagerVersions` gate), falling back
to the running binary's version otherwise. No-op updates
(target === previous) are silent.

v11 points at https://pnpm.io/11.x/migration. Future majors register
an entry in the in-file `MAJOR_UPGRADE_HINTS` table.

This is the v10-line backport of the corresponding main-branch change
in #11354.

* fix: update lockfile
2026-04-28 01:21:13 +02:00
Zoltan Kochan
2a1ffe1956 chore(release): 10.33.2 v10.33.2 2026-04-23 13:39:20 +02:00
Zoltan Kochan
275838ba77 fix: ignore unrelevant vulnerability 2026-04-23 13:37:44 +02:00
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
Zoltan Kochan
a288b79ece chore: update pnpm to v10.33.1 2026-04-22 14:41:51 +02:00
Zoltan Kochan
78951f2adb fix: global bin shim invokes pnpm instead of Node when installed via @pnpm/exe (#11335)
* fix(installation): skip pnpm exe when no Node.js is on PATH

When pnpm is installed as @pnpm/exe (a Single Executable Application
that bundles Node.js into the pnpm binary) and the user has no separate
Node.js on PATH, `which('node')` fails and `getNodeExecPath` used to
fall back to `process.execPath` - which in @pnpm/exe is the pnpm binary
itself, not a Node binary. That path got baked into generated global
bin shims via nodeExecPath, so running any globally-installed CLI
invoked pnpm with the target script as its first positional arg,
producing `ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND` from the current
working directory.

Detect the @pnpm/exe case via `detectIfCurrentPkgIsExecutable` and
return undefined from the fallback so the shim emits a plain
`exec node <target>` instead.

Closes #11291
Refs #4645

* Update pkg-manager/plugin-commands-installation/src/nodeExecPath.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-22 14:41:12 +02:00
Zoltan Kochan
89e3ac5249 chore(release): 10.33.1 v10.33.1 2026-04-22 11:24:38 +02:00
Zoltan Kochan
510a7ae500 test(tarball-fetcher): point "fetch a big repository" at an existing repo
Same reason as the sibling git-fetcher test: sveltejs/action-deploy-docs
was deleted/privated. Matches the repo used on v11.
2026-04-22 02:17:07 +02:00
Zoltan Kochan
98374b81b7 test(git-fetcher): point "fetch a big repository" at an existing repo
sveltejs/action-deploy-docs was deleted/privated, causing git clone to
prompt for credentials and fail in non-interactive CI. Matches the repo
already used on the v11 branch.
2026-04-22 01:54:05 +02:00
Zoltan Kochan
63150183a2 fix: scope minimatch overrides to their major version
The previous overrides `minimatch@<3.1.4: '>=3.1.4'` and
`minimatch@>=9.0.0 <9.0.7: '>=9.0.7'` were unbounded on the upper end
and resolved to minimatch@10.x, which breaks consumers built against
v3's CJS export shape (e.g. @verdaccio/utils fails with
`Cannot read properties of undefined (reading 'makeRe')`).
2026-04-22 00:50:58 +02:00
Zoltan Kochan
e5281992a9 fix: update dependencies 2026-04-22 00:11:49 +02:00
Zoltan Kochan
a6c24bdeb9 fix: defer npm-passthrough commands to main() when packageManager wants pnpm v11+ (#11331)
* fix: defer npm-passthrough commands to main() when packageManager wants pnpm v11+

The argv[0] passthrough in pnpm.ts runs before main() loads config and calls
switchCliVersion, so users with packageManager=pnpm@11 on a v10 host hit npm
directly for commands like `version`, bypassing v11's native implementations.
Walk up from cwd to the nearest package.json; if its packageManager field
selects pnpm v11 or newer, skip the passthrough and let main() switch.

Fixes #11328.

* test: add e2e test for v11 deferral, and only read package.json on passthrough

Move the shouldSkipNpmPassthrough check inside the passthrough switch case
so non-passthrough commands (install, add, run, etc.) don't pay for the
cwd-to-/ walk when their own config loading will discover packageManager
anyway.

Add an e2e test that pairs packageManager=pnpm@11.0.0-rc.3 with
manage-package-manager-versions=false so the assertion verifies main()
took over (command falls through to the `run` fallback and fails with
'Command "version" not found') without actually downloading pnpm v11 —
that keeps the test fast and network-independent.

* fix: walk up past workspace leaf manifests when detecting wanted pnpm major

- Continue walking up when package.json lacks a string packageManager field,
  so workspace leaves (which typically omit it) correctly pick up the root's
  pnpm@11+ declaration.
- Only treat ENOENT/ENOTDIR as "keep walking" — other fs errors now surface
  as null instead of silently consulting an ancestor manifest.
- Lazy-load readWantedPnpmMajor in pnpm.ts to keep cold start unaffected for
  non-passthrough commands.

* fix: honor manage-package-manager-versions when deciding to skip npm passthrough

When a user pinned pnpm v11 in packageManager but disabled version switching
(via .npmrc or an explicit `npm_config_manage_package_manager_versions` env
var), pnpm v10's main() won't switch to the wanted pnpm — so bypassing the
legacy argv[0] npm passthrough would leave commands like `version` stranded
with "Command not found". Fall back to the passthrough in that case.

Also:
- Treat a non-string `packageManager` field as malformed (return null) rather
  than conflating it with "missing" and walking up to an ancestor manifest.
- Extract the decision into a testable `shouldSkipNpmPassthrough(env, cwd)`
  helper and unit-test the full matrix.

* test: add e2e coverage for the v11+ switching-enabled passthrough skip

Covers the primary #11328 fix scenario end-to-end: when packageManager pins
pnpm v11+ and switching is enabled, the argv[0] passthrough is skipped and
main() hands off to switchCliVersion, which installs pnpm v11 and runs its
native `version` command (not npm's). Also updates the stale comment on the
v10 test that referenced a now-different adjacent test.

The test uses the real npmjs registry for this one install; the registry
mock's proxy is slow and flaky for pnpm's full dep tree, and the
end-to-end assertion only needs a real v11 tarball to prove the handoff.

* test: assert pnpm v11's version header in the switch-e2e test

* test: use v11's ERR_PNPM_INVALID_VERSION_BUMP marker and assert on combined streams

The --help-based assertion worked locally but failed in CI with stdout="", which
obscured the cause. Switching to `pnpm version` (no args) triggers v11's native
ERR_PNPM_INVALID_VERSION_BUMP error — a marker that only pnpm v11 can produce —
and asserting on stdout+stderr combined makes any install/switch failure
visible in the diff instead of a silent "expected substring not found".

* test: assert tool-dir presence instead of v11 executing in CI

pnpm v11 requires Node.js >= 22.13 but the GH Actions workflow runs on 22.12,
so v11 errors out at its own Node-version check before reaching the `version`
command. The tool dir's presence is a lower-level but direct proof that
installPnpmToTools ran — which is reached only from main() → switchCliVersion,
so the argv[0] passthrough must have been skipped. Combined with asserting
the absence of npm's `Bump a package version` help line, the test covers
the #11328 fix without depending on v11 actually executing a command.
2026-04-22 00:06:59 +02:00
Zoltan Kochan
335cd8e96a ci: update action-setup 2026-04-21 20:31:40 +02:00