GitHub Advisoryadvisory · npm
GHSA-gj8w-mvpf-x27x pnpm: Repository-controlled configDependencies can select a pacquet native install engine
HIGH
Official page ↗
- Aliases
- CVE-2026-55697
- Ecosystem
- npm
- Packages
- pnpm
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
- Weaknesses
- CWE-494, CWE-78, CWE-829
- Reviewed
- 2026-06-26
Affected packages
• npm pnpm: < 10.34.2 → fixed in 10.34.2
• npm pnpm: >= 11.0.0, < 11.5.3 → fixed in 11.5.3
Description
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.
• Advisory: `CAND-PNPM-097` / `GHSA-gj8w-mvpf-x27x`
• Advisory URL: https://github.com/pnpm/pnpm/security/advisories/GHSA-gj8w-mvpf-x27x
• Shared patch PR: https://github.com/pnpm/pnpm-ghsa-j2hc-m6cf-6jm8/pull/1
• Shared patch branch: `security/ghsa-batch-2026-06-09`
• Patch commit: `a93449314f398cf4bdf2e28d033c02d37395ad22`
• Base commit: `origin/main` `55a4035abf1ae3fe7208ba1f5ef43c5eff58ccec`
• Maintainer priority: `start-here`
• Component: `pnpm configDependencies / pacquet delegation`
• Patch area: pacquet/configDependency lifecycle execution is not used as install engine without trust
• Affected packages: `npm:pnpm`, `npm:@pnpm/config.reader`, `npm:@pnpm/installing.commands`
• CWE IDs: `CWE-829`, `CWE-78`, `CWE-494`
• Conservative CVSS: `7.5` / `CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H`
• Next action: review the shared patch branch for this component, set the final affected version range, merge and release the fix, then publish or close the advisory.
Expected Patched Behavior
config-dependency pacquet install engines are not selected unless the trusted allowlist is set outside the repository; the marker file is not created.
Files And Tests To Review
• `config/reader/src/Config.ts`
• `config/reader/src/types.ts`
• `config/reader/src/configFileKey.ts`
• `config/reader/src/index.ts`
• `config/reader/test/index.ts`
• `installing/commands/src/installDeps.ts`
• `installing/commands/test/runPacquet.ts`
• `pnpm/test/install/pacquet.ts`
• `.changeset/lucky-config-plugin-pnpmfiles.md`
Focused Validation
Run these from a checkout of the shared patch branch. They are the useful maintainer commands with machine-local artifact paths removed.
```bash
./node_modules/.bin/tsgo --build config/reader/tsconfig.json
./node_modules/.bin/tsgo --build installing/commands/tsconfig.json
./node_modules/.bin/tsgo --build pnpm/tsconfig.json
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/runPacquet.ts --runInBand
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/index.ts -t "config dependency code allowlists|user-level preference settings" --runInBand
./node_modules/.bin/eslint config/reader/src/Config.ts config/reader/src/types.ts config/reader/src/configFileKey.ts config/reader/src/index.ts config/reader/test/index.ts installing/commands/src/installDeps.ts installing/commands/test/runPacquet.ts pnpm/test/install/pacquet.ts
git diff --check
```
The full patched replay for the shared branch passed with all 20 candidates marked fixed. This candidate's replay evidence is `results/CAND-PNPM-097-patched-result.json`.
Summary
pnpm can install `configDependencies` declared in `pnpm-workspace.yaml` before command dispatch. Before the patch, a repository could declare `pacquet` or `@pnpm/pacquet` as a config dependency and pnpm treated that repository-controlled dependency as an install-engine opt-in. During install, pnpm resolved a platform-specific `@pacquet/-/pacquet` binary from `node_modules/.pnpm-config/` and spawned it as the developer or CI user.
Details
The vulnerable source-to-sink path was:
• `config/reader/src/getOptionsFromRootManifest.ts` copies repository `pnpm-workspace.yaml` `configDependencies` into config.
• `pnpm/src/getConfig.ts` installs config dependencies before command dispatch.
• `installing/env-installer/src/resolveAndInstallConfigDeps.ts` resolves the repository-declared dependency and its optional platform subdependencies.
• `installing/env-installer/src/installConfigDeps.ts` fetches, imports, and symlinks the config dependency tree under `node_modules/.pnpm-config`.
• `installing/commands/src/installDeps.ts` selected pacquet delegation whenever `configDependencies` contained `pacquet` or `@pnpm/pacquet`.
• `installing/deps-installer/src/install/index.ts` called `opts.runPacquet` from frozen and materialization paths.
• `installing/commands/src/runPacquet.ts` resolved `@pacquet/${process.platform}-${process.arch}/pacquet` from the installed config dependency package and executed it with `spawn()`.
Exact-version, integrity, and platform filters only proved which bytes package resolution selected; they did not establish that the repository was trusted to choose a native install engine.
PoC
Standalone PoC and verification script:
Repository fixture:
```yaml
packages:
• .
configDependencies:
pacquet: 0.2.2
```
Registry package shape:
```json
{
"name": "pacquet",
"version": "0.2.2",
"optionalDependencies": {
"@pacquet/darwin-arm64": "0.2.2"
}
}
```
Platform package payload:
```sh
!/bin/sh
echo "$PWD" > /tmp/pacquet-engine-ran
env > /tmp/pacquet-engine-env
```
Pre-patch exploit model:
1. The victim runs a dependency-management command such as `pnpm install` in the repository.
2. pnpm installs the repository-declared config dependency and its host-compatible optional platform dependency into `.pnpm-config`.
3. `installDeps()` treats the presence of `configDependencies.pacquet` or `configDependencies["@pnpm/pacquet"]` as authorization to delegate install materialization.
4. `runPacquet()` resolves the platform binary from the installed config dependency tree and spawns it in the lockfile directory.
Observed PoC output:
```json
{
"primitive": "repository-selected pacquet config dependency reaches native process execution when selected",
"patchedWithoutAllowlist": "blocked",
"trustedAllowlist": "allows explicit opt-in"
}
```
Focused validation commands:
```bash
./node_modules/.bin/tsgo --build config/reader/tsconfig.json
./node_modules/.bin/tsgo --build installing/commands/tsconfig.json
./node_modules/.bin/tsgo --build pnpm/tsconfig.json
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/runPacquet.ts --runInBand
NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169" ../../node_modules/.bin/jest test/index.ts -t "config dependency code allowlists|user-level preference settings" --runInBand
./node_modules/.bin/eslint config/reader/src/Config.ts config/reader/src/types.ts config/reader/src/configFileKey.ts config/reader/src/index.ts config/reader/test/index.ts installing/commands/src/installDeps.ts installing/commands/test/runPacquet.ts pnpm/test/install/pacquet.ts
git diff --check
```
Validation result:
• The PoC confirmed a selected pacquet config dependency reaches native process execution.
• Patched `getPacquetConfigDependencyName()` returns `undefined` without a trusted allowlist.
• Patched `getPacquetConfigDependencyName()` allows exact `pacquet`, exact `@pnpm/pacquet`, and wildcard `*` trusted opt-in.
• Config reader regressions prove user/global config can set `configDependencyInstallEngineAllowlist`, while `pnpm-workspace.yaml` cannot grant this permission to itself.
• E2E fixtures that intentionally delegate to pacquet now pass the trusted allowlist through environment config.
• TypeScript builds passed for `@pnpm/config.reader`, `@pnpm/installing.commands`, and `pnpm`.
• Focused `installing/commands/test/runPacquet.ts`: 3 passed.
• Focused `config/reader/test/index.ts`: 2 passed, 132 skipped under the focused pattern.
• ESLint passed with warnings only for existing skipped tests in `config/reader/test/index.ts` and `pnpm/test/install/pacquet.ts`.
• `git diff --check`: passed.
Impact
A malicious repository can cause pnpm to execute a registry-selected native binary while handling dependency-management commands. The binary runs with the victim developer or CI user's fi…
Related
Alias · CVE (1)
- CVE-2026-55697 pnpm is a package manager. vulnerability