# pnpm: Repository-controlled configDependencies can select a pacquet native install engine

Repository-declared configDependencies named pacquet made pnpm spawn a registry-supplied native binary as the install engine, executing code as the developer or CI user.

- Payout: Research
- Severity: high
- Program: pnpm
- Found by: massif-01, G-Rath, gabe-gfm
- Disclosed: 2026-06-26
- CVE: CVE-2026-55697
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-gj8w-mvpf-x27x
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-gj8w-mvpf-x27x
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:chained technique:source-review vuln:dependency-confusion vuln:misconfiguration vuln:rce

**Target:** pnpm configDependencies / pacquet install-engine delegation
**Root cause:** installDeps() treated the mere presence of `pacquet` or `@pnpm/pacquet` in repository-controlled pnpm-workspace.yaml configDependencies as authorization to delegate install materialization, and runPacquet() then resolved and spawn()ed the platform binary from the installed config dependency tree. Integrity/version filters proved which bytes were selected but not that the repository was trusted to choose a native engine.
**Steps:**
1. Add to repository `pnpm-workspace.yaml`: `configDependencies:\n  pacquet: 0.2.2`
2. Publish `pacquet@0.2.2` with `optionalDependencies: {"@pacquet/darwin-arm64": "0.2.2"}`
3. Platform package ships `pacquet` binary payload, e.g. `#!/bin/sh` writing `$PWD` and `env` to /tmp
4. Victim runs `pnpm install` in the repo; pnpm installs the config dependency and host-compatible optional platform dep into `node_modules/.pnpm-config`
5. `installDeps()` sees `configDependencies.pacquet` and delegates; `runPacquet()` resolves `@pacquet/${process.platform}-${process.arch}/pacquet` and spawns it in the lockfile directory
**How found:** Code-reading of the config-dependency lifecycle: traced getOptionsFromRootManifest copying workspace configDependencies into config, through resolveAndInstallConfigDeps/installConfigDeps, to installDeps() selecting pacquet delegation and runPacquet() spawn(). Noted that exact-version and integrity checks only prove which bytes were fetched, not that the repository is trusted to pick a native install engine.
**Exploitation:** Attacker controls a repository (and a registry package named pacquet with a platform-specific optional dependency). Victim clones and runs any dependency-management command such as `pnpm install`; pnpm installs the config dependency tree under node_modules/.pnpm-config, treats the name as an install-engine opt-in, and spawns the native binary with the victim's filesystem, environment, registry and git/SSH credentials, and network access.
**Insight:** When a package manager lets repo config opt into alternate execution engines or plugins, the trust boundary is the config source, not the artifact's integrity: any repo-writable setting that names a binary to spawn is a code-execution primitive. Look for settings where the same file that supplies the value also grants the permission.

## Proof of concept (copied exactly from the original report)

**Malicious pnpm-workspace.yaml declaring pacquet config dependency**

```
packages:
  - .
configDependencies:
  pacquet: 0.2.2
```

**Registry package with platform optional dependency**

```
{
  "name": "pacquet",
  "version": "0.2.2",
  "optionalDependencies": {
    "@pacquet/darwin-arm64": "0.2.2"
  }
}
```

**Native payload script executed by spawned pacquet binary**

```
#!/bin/sh
echo "$PWD" > /tmp/pacquet-engine-ran
env > /tmp/pacquet-engine-env
```

