pnpm 11.4
pnpm 11.4 closes a cluster of supply-chain holes around lockfile integrity, credential scoping, git resolutions, patch files, and dependency aliases, makes tarball-integrity mismatches a hard install failure by default (with a narrowly-scoped --update-checksums opt-in), and changes pnpm runtime set to write to devEngines.runtime instead of engines.runtime by default.
次要更改
Tarball-integrity mismatches are now a hard failure
Previously, pnpm install (non-frozen) would log ERR_PNPM_TARBALL_INTEGRITY when a downloaded tarball's hash didn't match the lockfile, silently re-resolve from the registry, and overwrite the locked integrity. A compromised registry, proxy, or republished version could therefore substitute attacker-controlled content on a clean machine even though the project shipped a committed lockfile.
pnpm install now exits with ERR_PNPM_TARBALL_INTEGRITY and a hint pointing at the new opt-in flag.
The only opt-in is pnpm install --update-checksums — narrowly scoped to refreshing the locked integrity values from what the registry currently serves. It mirrors yarn's flag of the same name. A warning still prints when the bypass takes effect so the operation is auditable.
--force and pnpm update deliberately do not bypass the integrity check. They are routine refresh operations; silently overwriting a locked integrity in those flows would erase the protection a committed lockfile is supposed to provide. --frozen-lockfile behavior is unchanged. --fix-lockfile keeps its documented purpose (filling in missing lockfile entries) and is also not a bypass.
pnpm runtime set writes to devEngines.runtime by default
pnpm runtime set <name> <version> now saves the runtime to devEngines.runtime by default instead of engines.runtime. Pass --save-prod (or -P) to save it to engines.runtime instead. See #11948.
补丁更改
Security: unscoped credentials no longer leak across registries
An unscoped _authToken (or _auth, or username + _password, or tokenHelper) defined in one source — ~/.npmrc, ~/.config/pnpm/auth.ini, a workspace .npmrc, CLI flags, etc. — would be sent as an Authorization header to whichever registry a different (potentially untrusted) source named. The same exposure extended to client TLS credentials (cert, key).
pnpm now rewrites each unscoped per-registry setting (_authToken, _auth, username, _password, tokenHelper, cert, key) to its URL-scoped form at load time, using the registry= value declared in the same source (or the npmjs default registry if the source declares none). A later layer overriding registry= therefore cannot pull an unscoped credential along, because it is already pinned to the URL its author intended. ca / cafile are intentionally not rescoped — they're trust anchors, not credentials, and corporate MITM-proxy setups rely on them applying globally.
Every rescope emits a deprecation warning telling the user where the setting was pinned and how to write it directly. npm has rejected unscoped credentials outright since npm@9, and pnpm intends to remove support in a future major release. To target a specific registry, write the setting URL-scoped:
//registry.example.com/:_authToken=...
//registry.example.com/:cert=...
Security: lockfile entries without integrity are rejected
Previously, the worker that extracts a downloaded tarball skipped hash verification when no integrity was supplied and minted a fresh one from the unverified bytes. An attacker who could both alter the lockfile (e.g. via a pull request that strips integrity:) and serve modified content at the referenced tarball URL could install a tampered package without any error — including under --frozen-lockfile.
pnpm now fails closed at lockfile-read time with ERR_PNPM_MISSING_TARBALL_INTEGRITY. Git-hosted tarballs (gitHosted: true or a URL on codeload.github.com / bitbucket.org / gitlab.com) and file: tarballs are exempt — the commit SHA in a git-host URL and the user-controlled local path already anchor the bytes.
Security: git resolutions reject non-SHA commit fields
Git resolutions whose commit field is not a 40-character hexadecimal SHA are rejected before git is invoked. A malicious lockfile could otherwise smuggle a value such as --upload-pack=<command> through git fetch / git checkout, which on SSH or local-file transports executes the supplied command.
Security: patch files writing outside the package directory are rejected
Patch files whose diff --git headers reference paths outside the patched package directory are now rejected. Previously a malicious .patch file added via a pull request could write, delete, or rename arbitrary files reachable by the user running pnpm install.
Security: dependency aliases with path-traversal segments are rejected
Dependency aliases that contain path-traversal segments (such as @x/../../../../../.git/hooks) are rejected when read from a package manifest or symlinked into node_modules. A malicious registry package could otherwise use a transitive dependency key to make pnpm install create symlinks at attacker-chosen paths outside the intended node_modules directory.
Trusted-publisher metadata now requires provenance
Trusted publisher metadata is only treated as the strongest trust evidence when provenance is also present.
Other fixes
- Fix
pnpm deploycrashing withENOENT: ... lstat '<deployDir>/node_modules'whenconfigDependenciesdeclares pacquet (pacquetor@pnpm/pacquet). The deploy directory never installs config dependencies, so the install engine they designate isn't on disk to invoke; the nested install now skips them. - Limit concurrent project manifest reads while listing large workspaces to avoid
EMFILEerrors. - Validate
devEngines.runtimeandengines.runtimeversion ranges fornode,deno, andbunwhenonFailis set toerrororwarn. Previously these settings only had an effect withonFail: 'download'— theerrorandwarnmodes silently did nothing #11818. Violations now throwERR_PNPM_BAD_RUNTIME_VERSION. - Improve the log message that pnpm prints after auto-adding entries to
minimumReleaseAgeExcludewhenminimumReleaseAgeis set withoutminimumReleaseAgeStrict. The message previously referred to the internal "loose mode" terminology, which wasn't searchable in the docs; it now tells the user to setminimumReleaseAgeStricttotrueif they want these updates gated behind a prompt instead #11747.
