Skip to content
Report library
Purpose / Other

Pnpm Skill Security Audit

What the author says it does (original text)

Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces via pnpm-workspace.yaml, or managing dependencies with catalogs, patches, overrides, config dependencies, or the global virtual store.

Independent security check

Do not install or run it yet

Files checked
18
Risks found
6
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.Risks found: 3
High risk

Bulk approval allows every pending dependency to run installation scripts

Source references: 3
What we found

The documentation offers `pnpm approve-builds --all`. This bypasses package-by-package review, while dependency lifecycle scripts can execute arbitrary code as the user or CI identity running pnpm.

Why this matters

A malicious or compromised dependency could read workspace files and CI environment variables, alter build outputs, or use credentials available to the job.

This is an actionable command, not merely a warning. pnpm normally blocks unreviewed dependency build scripts, while `pnpm approve-builds --all` approves every pending one at once. If an agent runs it without package-by-package review, approved install scripts execute with the current user or CI identity. A user can require an explicit script inventory and individual approvals, prohibit `--all`, and install in a low-privilege environment without credentials.

references/features-supply-chain-security.md:28In the instructionsOpen original file
### Approving builds```bashpnpm approve-builds            # interactive promptpnpm approve-builds --all      # approve all pendingpnpm approve-builds esbuild fsevents !core-js   # ! = denypnpm add --allow-build=esbuild my-bundler       # approve while addingpnpm add -g --allow-build=esbuild esbuild       # global (replaces approve-builds -g)```
Show 2 other places
references/features-supply-chain-security.md:12In the instructionsOpen original file
By default pnpm does **not** run dependency lifecycle scripts (`preinstall`/`install`/`postinstall`). Packages must be explicitly approved. Approval lives in one `allowBuilds` map in `pnpm-workspace.yaml`.
references/features-supply-chain-security.md:30In the instructionsOpen original file
```bashpnpm approve-builds            # interactive promptpnpm approve-builds --all      # approve all pendingpnpm approve-builds esbuild fsevents !core-js   # ! = denypnpm add --allow-build=esbuild my-bundler       # approve while addingpnpm add -g --allow-build=esbuild esbuild       # global (replaces approve-builds -g)```
High risk

Migration and CI examples fetch and execute unpinned remote tools

Source references: 3
What we found

The migration flow directly runs `pnpx codemod` without a version or integrity, and the GitLab example activates `pnpm@latest`. The resolved code can change with registry state and executes on a developer machine or CI runner.

Why this matters

If a name is hijacked, a publisher account is compromised, or `latest` points to a faulty release, remote code receives the current process's file and environment access. CI may also expose publishing or deployment credentials.

Both are executable examples. The migration invokes an unversioned codemod through `pnpx`, and the GitLab example explicitly activates `pnpm@latest`. Consequently, resolved code may change over time and runs with developer or CI permissions. This matches the stated migration/setup purpose and does not show malicious intent, but users can require exact versions and integrity verification, review locked artifacts, and restrict CI credentials and network access.

references/best-practices-migration.md:12In the instructionsOpen original file
v11 changes how configuration is read. Most of it is mechanical — run the codemod:```bashcd /path/to/projectpnpx codemod run pnpm-v10-to-v11```
Show 2 other places
references/features-catalogs.md:160In the instructionsOpen original file
Then update package.json files to use `catalog:`. To migrate an existing workspace automatically:```bashpnpx codemod pnpm/catalog```
references/best-practices-ci.md:105In the instructionsOpen original file
before_script:  - corepack enable  - corepack prepare pnpm@latest --activatecache:
High risk

Config dependencies can auto-load JavaScript hooks before normal dependencies

Source references: 5
What we found

Config dependencies install before regular dependencies, and specially named packages have their pnpmfile loaded automatically. Hooks can alter manifests, lockfiles, settings, package import behavior, and published manifests; `--ignore-scripts` does not disable them.

Why this matters

An unreviewed config package could change what is installed or published, redirect package fetching, and execute hook logic, resulting in supply-chain code execution or artifacts that differ from the user's intent.

Config dependencies are an active extension mechanism: they install before ordinary dependencies, and specially named packages automatically load a `pnpmfile`. The hooks can alter manifests, lockfiles, pnpm settings, package linking, and published manifests; `--ignore-scripts` does not disable them. Thus an untrusted config dependency could influence install or publication decisions. Users can require pinned, reviewed config dependencies and pnpmfiles, prohibit unapproved additions, and use `ignorePnpmfile` for untrusted projects.

references/features-config-dependencies.md:8In the instructionsOpen original file
Config dependencies are npm packages that pnpm installs **before** all regular dependencies, so they can supply hooks, settings, patches, catalogs, and overrides that are reused across many repositories. They let you keep one shared "pnpm config" package and consume it everywhere.## Declaring config dependenciesThey live in `pnpm-workspace.yaml`; their integrity is recorded in a dedicated env-lockfile document inside `pnpm-lock.yaml`.
Show 4 other places
references/features-config-dependencies.md:32In the instructionsOpen original file
## Auto-loaded pluginsA config dependency named `pnpm-plugin-*`, `@*/pnpm-plugin-*`, or `@pnpm/plugin-*` has its `pnpmfile.mjs` (or `.cjs`) loaded automatically from the package root.
references/features-hooks.md:25In the instructionsOpen original file
| Hook | When | Use ||------|------|-----|| `readPackage(pkg, ctx)` | after a dependency manifest is parsed | mutate a dependency's `package.json` (affects resolution) || `afterAllResolved(lockfile, ctx)` | after resolution | mutate the lockfile before it's written || `updateConfig(config)` | before install | mutate pnpm's settings (great with config dependencies) || `beforePacking(pkg)` | before `pnpm pack`/`publish` tarball | customize the **published** manifest only || `preResolution(opts)` | after reading lockfiles, before resolution | inspect/modify lockfile objects || `importPackage(dir, opts)` | when writing to node_modules | change how packages are linked |
references/features-hooks.md:168In the instructionsOpen original file
## Key Points- Prefer `.pnpmfile.mjs` with `export const hooks`/`finders`/`resolvers`/`fetchers`.- New hooks: `updateConfig` (mutate settings), `beforePacking` (published manifest), `preResolution`, `importPackage`.- Pair `updateConfig` with config dependencies to share settings/catalogs across repos.- `--ignore-scripts` does **not** disable the pnpmfile; use `ignorePnpmfile`.
references/features-hooks.md:173In the instructionsOpen original file
- Pair `updateConfig` with config dependencies to share settings/catalogs across repos.- `--ignore-scripts` does **not** disable the pnpmfile; use `ignorePnpmfile`.
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.No risks found
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.Risks found: 1
Medium risk

Migration and rollback commands recursively delete dependency trees and lock configuration

Source references: 3
What we found

Several flows begin with `rm -rf`, removing node_modules and old lockfiles; rollback also deletes the pnpm lockfile and workspace configuration. One command targets `packages/*/node_modules` across a monorepo.

Why this matters

Uncommitted configuration or manual changes can be lost. Re-resolving after deleting the old lockfile can select different versions and make builds irreproducible. A wrong working directory or path assumption can broaden deletion.

These are examples in migration, reinstall, and rollback sections; removing old dependency directories or lockfiles is consistent with that purpose rather than hidden behavior. Nevertheless, the commands immediately and recursively delete files. The monorepo form covers every matching package, and rollback also removes `pnpm-workspace.yaml`, risking uncommitted configuration and reproducibility. Users can require root-path confirmation, an uncommitted-file check, and backups of lockfiles and configuration first.

references/best-practices-migration.md:41In the instructionsOpen original file
### From npm```bash# Remove npm lockfile and node_modulesrm -rf node_modules package-lock.json# Install with pnpmpnpm install```
Show 2 other places
references/best-practices-migration.md:161In the instructionsOpen original file
3. Install:   ```bash   rm -rf node_modules packages/*/node_modules package-lock.json   pnpm install   ```
references/best-practices-migration.md:291In the instructionsOpen original file
## Rollback PlanIf migration causes issues:```bash# Remove pnpm filesrm -rf node_modules pnpm-lock.yaml pnpm-workspace.yaml# Restore npmnpm install# Or restore Yarnyarn install```
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.Risks found: 1
Medium risk

A shared writable virtual store puts separate projects or agents in one trust boundary

Source references: 3
What we found

The recommended global virtual store makes multiple worktrees use symlinks into one writable directory. The documentation acknowledges it is only for mutually trusted projects, users, and jobs, while also recommending it for parallel AI agents.

Why this matters

If a lower-trust job can write the store and a higher-trust job later reuses it, the former may affect package contents or build outputs loaded by the latter. The effect can persist across projects.

When enabled, projects symlink into one global virtual store. The documentation explicitly says this is shared writable state suitable only for mutually trusted projects, users, or jobs; its multi-agent worktree example also assumes one trust boundary. If any project or agent is untrusted, trusted work could consume shared writable content. Users can separate trust levels into distinct stores and OS accounts, restrict directory permissions, or leave this experimental option disabled.

references/features-global-virtual-store.md:25In the instructionsOpen original file
- **Package identity = hash of the dependency graph.** Two projects with the same `lodash@4.17.21` and the same transitive tree point at the exact same directory (NixOS-style). Different peers ⇒ separate entries.- **Near-zero per-project cost** and **instant installs** once a version is in the store.- In **pnpm v11** it is the default for `pnpm dlx`/`pnx` and global installs; for **project** installs it is still **opt-in/experimental**.### Limitations- **CI:** auto-disabled (no warm cache to benefit from).- **Trust:** the store is shared writable state — only for mutually trusting projects/users/jobs; protect the path with filesystem permissions.- **ESM hoisting:** relies on `NODE_PATH`, which Node ignores for ESM imports. If ESM deps import undeclared packages, resolution fails. Fix with `packageExtensions` or the `@pnpm/plugin-esm-node-path` config dependency.
Show 2 other places
references/features-global-virtual-store.md:35In the instructionsOpen original file
## Git worktrees for multi-agent developmentGit worktrees let you check out many branches simultaneously, each in its own directory, sharing one `.git` object store. Combined with the global virtual store, every worktree gets a fully functional `node_modules` that is almost free on disk — ideal for running multiple AI agents in parallel.
references/features-global-virtual-store.md:59In the instructionsOpen original file
Each worktree has its own `node_modules` tree (so agents can install different versions on different branches without conflict), but all package contents come from the one shared store. Remove a worktree with `git worktree remove ./feature-auth`.> The pnpm repo itself uses this setup and ships helper scripts (`pnpm worktree:new <branch|pr>`). Assumes all worktrees/agents share the same trust boundary.
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.No risks found
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.Risks found: 1
Medium risk

Publishing references include bypassing Git checks and changing public package state

Source references: 2
What we found

The command table lists recursive publishing with Git checks disabled, plus deprecation, dist-tag changes, and version withdrawal. These operations use the current registry identity and can immediately affect all consumers.

Why this matters

The wrong package, version, or scope could be published, deprecated, retagged, or withdrawn, causing supply-chain outages, incorrect releases, and reputational or revenue loss.

These are executable registry operations, not merely conceptual descriptions. `publish -r --no-git-checks` can recursively publish while bypassing pnpm's Git-state checks; `deprecate`, `dist-tag`, and `unpublish` change public package metadata or availability. They fit a package-manager reference, but misuse under the current authenticated identity could affect consumers. Users can require per-package confirmation, retain Git checks, use least-privilege tokens, and require human approval for unpublishing or tag changes.

references/core-cli.md:143In the instructionsOpen original file
## Publishing```bashpnpm packpnpm publish -r --no-git-checkspnpm version patch|minor|major|2.0.0    # bump version, commit + tag (v11)pnpm version prerelease --preid betapnpm deprecate <pkg>@<range> "message"pnpm dist-tag add <pkg>@<version> <tag>pnpm unpublish <pkg>@<version>          # discouraged; prefer deprecatepnpm sbom --sbom-format cyclonedx       # SBOM: cyclonedx (1.7) | spdx (2.3)pnpm stage publish ...                  # staged publishing (defer 2FA)```
Show 1 other places
references/core-workspaces.md:182In the instructionsOpen original file
Use `--no-git-checks` for publishing from CI:```bashpnpm publish -r --no-git-checks```

Inside this skill

3 instruction sections

This is a pnpm 10/11 reference Skill covering dependency installation, workspaces, configuration, patches, hooks, shared stores, CI, and publishing; the main file routes these operations to reference documents.

View source
SKILL.md:20In the instructionsOpen original file
|-------|-------------|-----------|| CLI Commands | install/add/remove/update, run, dlx/pnx, workspace, runtime, publishing (version, view, sbom, stage) | [core-cli](references/core-cli.md) || Configuration | pnpm-workspace.yaml settings (camelCase), global config.yaml, packageConfigs, .npmrc auth | [core-config](references/core-config.md) || Workspaces | Monorepo support: filtering, workspace protocol, shared lockfile, packageConfigs | [core-workspaces](references/core-workspaces.md) || Store | Content-addressable store, virtual store, node linker modes, frozen/read-only store | [core-store](references/core-store.md) |
SKILL.md:31In the instructionsOpen original file
| Overrides | Force versions (incl. transitive & peer deps); packageExtensions | [features-overrides](references/features-overrides.md) || Patches | Modify third-party packages; patchedDependencies in pnpm-workspace.yaml | [features-patches](references/features-patches.md) || Aliases | Install under custom names (npm:) and registry aliases (namedRegistries) | [features-aliases](references/features-aliases.md) || Hooks | .pnpmfile.mjs hooks (readPackage, updateConfig, beforePacking), finders, resolvers/fetchers | [features-hooks](references/features-hooks.md) || Peer Dependencies | Auto-install, strict mode, rules, dedupePeers, peers check | [features-peer-deps](references/features-peer-deps.md) || Config Dependencies | Share hooks/settings/catalogs/patches across repos via configDependencies | [features-config-dependencies](references/features-config-dependencies.md) || Global Virtual Store | Shared node_modules, git-worktree multi-agent setups, isolated global packages | [features-global-virtual-store](references/features-global-virtual-store.md) || Supply-Chain Security | Build approval (allowBuilds), minimumReleaseAge, trustPolicy, lockfile integrity | [features-supply-chain-security](references/features-supply-chain-security.md) |

The Skill directs project settings to pnpm-workspace.yaml and says credential-bearing .npmrc files should be excluded from version control. This can bring an agent near authentication files during configuration work, but the supplied text does not instruct it to read or transmit token values.

View source
SKILL.md:12In the instructionsOpen original file
**Configuration model (important):** pnpm settings now live in `pnpm-workspace.yaml` (and the global `config.yaml`) using **camelCase** keys. `.npmrc` is used **only** for authentication/registry credentials, and the `pnpm` field of `package.json` is no longer read. When working in a pnpm project, check `pnpm-workspace.yaml` for settings/workspace structure and `.npmrc` only for auth. Always use `--frozen-lockfile` (or `pnpm ci`) in CI.
references/core-config.md:93In the instructionsOpen original file
## .npmrc — authentication onlyKeep auth tokens out of the repo (gitignore the project `.npmrc`). Auth files, highest priority first:1. `<workspace root>/.npmrc` (project, gitignored)2. `<pnpm config>/auth.ini` (written by `pnpm login`)3. `~/.npmrc` (fallback for npm compatibility)

The documentation describes pnpm security defaults, including blocking unapproved dependency build scripts, failing lockfile-integrity mismatches, and sharing caches only among mutually trusted jobs. These are protective claims whose actual effect depends on the installed pnpm version and project configuration.

View source
references/features-supply-chain-security.md:12In the instructionsOpen original file
By default pnpm does **not** run dependency lifecycle scripts (`preinstall`/`install`/`postinstall`). Packages must be explicitly approved. Approval lives in one `allowBuilds` map in `pnpm-workspace.yaml`.```yaml title="pnpm-workspace.yaml"allowBuilds:  esbuild: true  core-js: false  # version selectors are supported  nx@21.6.4 || 21.6.5: true```- Packages **not listed** are unreviewed and blocked by default.- `strictDepBuilds: true` (default) ⇒ unreviewed builds make install exit non-zero (`ERR_PNPM_IGNORED_BUILDS`). Set `false` to warn instead.- During install, unreviewed packages with build scripts are auto-added to `pnpm-workspace.yaml` with a placeholder so you can set `true`/`false`.
references/features-supply-chain-security.md:78In the instructionsOpen original file
## Lockfile integritySince v11, a downloaded tarball whose hash doesn't match `pnpm-lock.yaml` is a hard error (`ERR_PNPM_TARBALL_INTEGRITY`) — protecting committed lockfiles from a compromised registry/proxy. `--force` and `pnpm update` do **not** bypass it.```bashpnpm install --update-checksums   # narrow opt-in after verifying the new bytes```
references/best-practices-ci.md:68In the instructionsOpen original file
> **Trust:** only cache/restore the pnpm store and cache dir between *trusted* jobs. A store an untrusted job can write to must not be reused by trusted jobs — it is part of pnpm's trust domain.
Start here · InstructionsSKILL.md
pnpm
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source.

File reference map

References: 16
Files making referencesReferenced content
Lines show actual file references, not execution order. Select a node to highlight its connections and inspect the files and source locations. Dashed lines include files that still need locating.
Files and check records18 files

Coverage and gaps

Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included
  • references/best-practices-ci.mdFull text included
  • references/best-practices-migration.mdFull text included
  • references/best-practices-performance.mdFull text included
  • references/core-cli.mdFull text included
  • references/core-config.mdFull text included
  • references/core-store.mdFull text included
  • references/core-workspaces.mdFull text included
  • references/features-aliases.mdFull text included
  • references/features-catalogs.mdFull text included
  • references/features-config-dependencies.mdFull text included
  • references/features-global-virtual-store.mdFull text included
  • references/features-hooks.mdFull text included
  • references/features-overrides.mdFull text included
  • references/features-patches.mdFull text included
  • references/features-peer-deps.mdFull text included
  • references/features-supply-chain-security.mdFull text included
  • GENERATION.mdFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • GENERATION.mdSupporting file
  • SKILL.mdInstructions
  • references/best-practices-ci.mdSupporting file
  • references/best-practices-migration.mdSupporting file
  • references/best-practices-performance.mdSupporting file
  • references/core-cli.mdSupporting file
  • references/core-config.mdSupporting file
  • references/core-store.mdSupporting file
  • references/core-workspaces.mdSupporting file
  • references/features-aliases.mdSupporting file
  • references/features-catalogs.mdSupporting file
  • references/features-config-dependencies.mdSupporting file
  • references/features-global-virtual-store.mdSupporting file
  • references/features-hooks.mdSupporting file
  • references/features-overrides.mdSupporting file
  • references/features-patches.mdSupporting file
  • references/features-peer-deps.mdSupporting file
  • references/features-supply-chain-security.mdSupporting file

Operations mentioned in code and instructions

Connect to websites
SKILL.md:7In the instructionsOpen original file
  version: "2026.6.22"  source: Generated from https://github.com/pnpm/pnpm, scripts located at https://github.com/antfu/skills---
references/best-practices-ci.md:293In the instructionsOpen original file
Source references:- https://pnpm.io/continuous-integration- https://pnpm.io/docker
references/best-practices-ci.md:294In the instructionsOpen original file
- https://pnpm.io/continuous-integration- https://pnpm.io/docker- https://pnpm.io/cli/ci
Read keys or account settings
SKILL.md:12In the instructionsOpen original file
**Configuration model (important):** pnpm settings now live in `pnpm-workspace.yaml` (and the global `config.yaml`) using **camelCase** keys. `.npmrc` is used **only** for authentication/registry credentials, and the `pnpm` field of `package.json` is no longer read. When working in a pnpm project, check `pnpm-workspace.yaml` for settings/workspace structure and `.npmrc` only for auth. Always use `--frozen-lockfile` (or `pnpm ci`) in CI.
references/core-config.md:13In the instructionsOpen original file
| **All pnpm/install settings** (`nodeLinker`, `hoistPattern`, `autoInstallPeers`, `overrides`, `catalog`, …) | `pnpm-workspace.yaml` (project) and `config.yaml` (global) | YAML, **camelCase** keys || **Auth & registry credentials** (`_authToken`, `cert`, `key`, …) | `.npmrc` (project, gitignored) and global `rc` | INI |
references/core-config.md:15In the instructionsOpen original file
> **Important changes:** pnpm no longer reads settings from the `pnpm` field of `package.json`, and `.npmrc` is now used **only** for authentication/registry credentials. Everything else belongs in `pnpm-workspace.yaml`. Keys in YAML are **camelCase** (e.g. `nodeLinker`), not the kebab-case used by old `.npmrc` files.
Install extra software packages
references/best-practices-ci.md:36In the instructionsOpen original file
      - run: pnpm install --frozen-lockfile   # or: pnpm ci      - run: pnpm test
references/best-practices-ci.md:41In the instructionsOpen original file
> `pnpm ci` (aliases `clean-install`, `install-clean`) = `pnpm clean` + `pnpm install --frozen-lockfile`, ideal for fully reproducible CI builds.
references/best-practices-ci.md:65In the instructionsOpen original file
- run: pnpm install --frozen-lockfile```
Run commands
references/best-practices-ci.md:53In the instructionsOpen original file
- name: Get pnpm store directory  shell: bash  run: |
references/best-practices-ci.md:203In the instructionsOpen original file
```bashpnpm install --frozen-lockfile
references/best-practices-ci.md:211In the instructionsOpen original file
```bashpnpm install --frozen-lockfile --prefer-offline
Change files
references/best-practices-migration.md:45In the instructionsOpen original file
# Remove npm lockfile and node_modulesrm -rf node_modules package-lock.json
references/best-practices-migration.md:55In the instructionsOpen original file
# Remove yarn lockfile and node_modulesrm -rf node_modules yarn.lock
references/best-practices-migration.md:138In the instructionsOpen original file
# Or reinstallrm -rf node_modulespnpm install
Read files
references/features-hooks.md:31In the instructionsOpen original file
| `beforePacking(pkg)` | before `pnpm pack`/`publish` tarball | customize the **published** manifest only || `preResolution(opts)` | after reading lockfiles, before resolution | inspect/modify lockfile objects || `importPackage(dir, opts)` | when writing to node_modules | change how packages are linked |
Lines read
3,152
File checksum (to compare versions)
4833fcad7a1fc18ab7a5aa123d0ce11b1a31101352c5dde5274d227e6ca956b4