* 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.
Global Virtual Store (#10939)
Content-verified skip in GVS mode, tolerate EPERM during
bin creation on Windows, handle EPERM in DLX cache symlink.
(cherry picked from commit 62f760ec3d)
* fix(dlx): print help message on calling pnpm dlx without arguments
Running `pnpm dlx` with no arguments would crash Node.js with a
TypeError as it attempted to call `.indexOf()` on an undefined variable.
This commit adds a guard clause and displays the help message instead
and exits gracefully.
Fixes#10633
* refactor: dlx
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
* fix(run): fail when no packages have script in filtered recursive run
Previously, `pnpm run -r <script>` and `pnpm run --filter <filter> <script>`
would silently succeed with exit code 0 when no packages had the specified
script, as long as a filter was used. This was inconsistent with the
documentation which states "If none of the packages have the command, the
command fails."
This change makes the command fail with ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT in
all cases where no packages have the script, regardless of whether a filter
is used. The `--if-present` flag can be used to suppress this error.
close#6844