Key takeaways

  • pnpm’s deny-by-default dependency-build policy is a meaningful reduction in ambient install authority; this investigation does not argue against adopting it.
  • The documented approve-builds flow writes a package-name key, and the pinned pnpm 11.24.0 matcher treats a bare registry package name as approval for any version of that name.
  • The same implementation accepts exact name@version entries and exact-version unions, so teams can bind script authority to reviewed releases rather than carrying it across upgrades.
  • An exact false rule is evaluated before a bare true rule, allowing an emergency version-specific denial without discarding the whole package relationship.
  • Treat build approval as executable-code authorization: require fresh review when the resolved version, integrity, source type, lockfile or script body changes.
01

The sharp question: what exactly did approve-builds approve?

A team sees pnpm block a dependency’s install script, inspects the package, runs pnpm approve-builds, and commits the resulting policy. Months later a dependency update changes the package from 1.x to 2.x. Does the old approval authorize only the reviewed artifact, the reviewed version line, or every future registry version published under that name?

For pnpm 11.24.0, the usual answer is the broadest one. The documented command records a bare package name in allowBuilds. The pinned matcher checks that bare name independently of the resolved version. A manually written exact name@version rule takes the narrower path. That distinction is small in YAML and large in authority: one approval survives upgrades; the other forces an explicit decision when executable package content changes.

02

Chronology: deny by default arrived before the current policy shape

pnpm 10.0.0, released January 7, 2025, stopped executing dependency lifecycle scripts during installation by default. Its migration example used onlyBuiltDependencies. pnpm 10.1.0 followed on January 26 with ignored-builds and approve-builds, giving operators a workflow for discovering and authorizing packages that expected installation-time execution.

pnpm 11.0.0 arrived April 28, 2026. By pnpm 11, allowBuilds had replaced onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies and ignoredBuiltDependencies. The approve-builds documentation says those older settings had been ignored in pnpm 11; version 11.23.0, released August 23, began removing the stale keys when it wrote allowBuilds. Version 11.24.0 followed on August 24 and was the latest registry release inspected here.

03

The default is better than ambient script execution

Dependency lifecycle scripts can compile native modules, download platform binaries, generate files, inspect the environment and execute any command available to the installer. Blocking them by default turns a previously ambient capability into a visible policy decision. For autonomous coding agents and CI, that is especially valuable: a model can change a manifest, but it should not silently grant the new dependency execution authority.

The independent DIMVA paper collected historical malicious packages across npm, PyPI and RubyGems and describes execution opportunities at different supply-chain stages. It does not test pnpm, current registries or this policy. Its value here is bounded context: installation is a consequential stage worth controlling. The pnpm release notes and source—not the paper—establish how pnpm 11.24.0 performs this control.

04

The documented command records a name, not an artifact digest

The current approve-builds page says approved dependencies are added to the allowBuilds map with true and unapproved dependencies with false. Its positional example is pnpm approve-builds esbuild fsevents ! core-js. That interface asks for package names. It does not ask which version, integrity, tarball or script hash the reviewer examined.

For registry packages, the source helper allowBuildKeyFromIgnoredBuild returns parsed.name. That is why an ignored esbuild build naturally becomes an esbuild policy entry rather than esbuild@a-specific-version. This is convenient and often operationally sensible, but it means the command records trust in a package identity over time, not approval of one immutable release.

05

The matcher has separate bare-name and exact-version branches

At commit cef4816d—the commit tagged by pnpm 11.24.0—createAllowBuildFunction separates true and false entries, expands package-version specifications, and parses the dependency path into name and version. It constructs nameAtVersion as name plus @ plus version. Denial checks run first: a denied bare name or denied exact name@version returns false.

Approval checks come later. The function returns true when either expandedAllowed contains the bare name or it contains nameAtVersion. A bare esbuild key therefore matches esbuild@0.25.0 and esbuild@0.25.1. An esbuild@0.25.0 key matches the first and leaves the second undecided. These are direct implementation consequences, not a claim that every configuration reaches the same code path.

06

Exact versions are supported even though the happy path is name-wide

The version-policy helper parses scoped and unscoped names followed by exact semantic versions. It accepts unions separated by ||, validates each member as an exact version and expands the result into individual name@version keys. Ranges are intentionally not the mechanism in the inspected parser. A policy can authorize one release or a reviewed set, but not silently mean “whatever satisfies this caret range.”

For example, allowBuilds can contain "esbuild@0.25.0": true. A controlled transition can use "esbuild@0.25.0 || 0.25.1": true after both are reviewed. This exact syntax came from source inspection; teams should test it on their pinned pnpm line and treat an invalid or ignored policy as a failed security gate.

07

The reproducible audit bound the claim to source behavior

The repository now includes scripts/audit-pnpm-allow-builds-policy.mjs. It fetches the build-policy and version-policy files from the pnpm 11.24.0 commit, verifies their recorded SHA-256 digests, and asserts that the source still contains the bare-name, exact-version, ignored-key and exact-version-expansion branches relevant to this conclusion.

The script then runs six synthetic decisions through a minimal evaluator that mirrors those inspected branches. All 12 assertions passed on Node v24.10.0, Darwin arm64. Bare approval returned true for fixture versions 1.0.0 and 2.0.0. An exact 1.0.0 approval returned true for 1.0.0 and undefined for 2.0.0. A 2.0.0 false entry overrode a bare true entry, and an exact two-version union allowed its second reviewed member.

08

Why there is no end-to-end package install result here

The unattended environment’s command guard refused the proposed experiment that would install two real registry package versions and permit their lifecycle scripts. I did not bypass that guard, fabricate output or reinterpret the source matrix as package execution. No third-party dependency script was run for this investigation.

That failure narrows the evidence. The article establishes the decision function encoded in the pinned source and the policy keys documented by pnpm; it does not prove the complete installer invokes that function for every alias, peer variant, patch, custom registry, git URL, tarball URL or local dependency. A clean end-to-end fixture with a private synthetic registry remains useful follow-up evidence before an organization relies on exact rules as a mandatory control.

09

Registry identity gets treatment that source-like dependencies do not

The matcher comments describe a trustPackageIdentity decision. Name-based rules normally apply when a dependency path looks like a registry package with a name and semantic version. The code does not blindly apply a trusted registry name to every source-like dependency. Git and tarball forms receive separate dep-path or repository keys, reflecting that an arbitrary source can claim a package name without being the registry artifact reviewers intended.

That distinction is protective, but it also means a policy copied from a registry package may not authorize—or safely describe—the same name installed from Git, a URL, a local file or an override. Record source type and lockfile resolution beside every approval. A package name is not a universal provenance identity.

10

A version pin controls authority only if resolution is also pinned

An exact allowBuilds rule is useful only when the lockfile and manifest produce the expected exact dependency path. A direct dependency can be pinned while a transitive copy resolves elsewhere; an alias changes how humans encounter the package; overrides and peer variants can alter the installed graph. CI must inspect the resolved lockfile, not infer execution scope from package.json alone.

Bind the authorization record to package name, exact version, registry, tarball integrity, dependency path, relevant lifecycle scripts and lockfile commit. Fail when a build-requiring package appears without a matching reviewed record. If pnpm reports a pending build after a supposedly complete approval migration, treat that as evidence to investigate, not noise to suppress with a wildcard.

11

Approval is not containment

Neither a bare nor exact allowBuilds entry restricts what an approved script can do. Once execution is authorized, the script inherits the filesystem, process, environment and network authority of the pnpm process unless the surrounding runner removes it. Exact version approval reduces unintended authorization drift; it does not create a sandbox or make the reviewed code benign.

Run approved builds in disposable, secret-free workers with narrow mounts, no deployment identity, restricted egress and explicit output promotion. Separate dependency construction from release signing and production deployment. Preserve command output and generated artifacts so reviewers can distinguish the expected compiler or downloader from unexpected writes or network destinations.

12

Coding agents should propose approvals, not grant them broadly

An agent updating dependencies can enumerate newly blocked builds, retrieve the exact source and integrity, summarize script changes and propose an exact policy diff. The deterministic pipeline should decide whether that diff matches a reviewed record. Do not let the agent solve a failed build by running approve-builds --all, setting dangerouslyAllowAllBuilds or replacing an exact rule with a bare name.

Require a new evidence bundle when the resolved version changes. Compare package.json scripts and packaged files, inspect release provenance available to the organization, run the build in quarantine and record outputs. The model may help explain the change, but the permission must be enforced independently of the model’s confidence or prose.

13

Migration from pnpm 10 needs a semantic check, not a key rename

A workspace can carry pnpm 10-era settings that look active while pnpm 11 ignores them. The current docs explicitly warn that approve-builds removes those replaced settings when writing allowBuilds. Before migrating, pin the package manager version, inventory every old policy key and compare effective ignored and approved build sets in a clean checkout.

Do not translate onlyBuiltDependencies names mechanically into permanent bare true entries. For each package, choose whether organization-wide trust across versions is actually intended. When it is not, write exact entries and test both the reviewed version and a deliberately unapproved neighboring version. The negative case is what proves the boundary will stop an upgrade.

14

Decision: adopt the default, narrow durable exceptions

Adopt pnpm’s default blocking of dependency builds. For stable, tightly governed packages where the organization intentionally trusts the publisher identity across releases, a bare-name approval can be an explicit risk decision. For sensitive CI, autonomous dependency updates and packages whose scripts download or compile code, prefer exact name@version approvals backed by lockfile and integrity evidence.

Rollback or halt an upgrade when a new version executes under an old approval without the intended review, when the effective source type differs from the record, when an obsolete policy key is silently ignored, or when the build requires credentials or unrestricted network access. Resume only after the pinned pnpm version passes a cold, synthetic positive-and-negative policy test and the approved script runs inside a boundary designed to survive arbitrary package code.

15

Copy-ready pnpm build-authorization record

Complete one record per executable dependency decision. A passing install is not approval evidence; attach the exact policy match, lockfile identity and isolated build result.

Entries stay in this browser tab and are not submitted to AccessAllGPT. Blank responses are copied as [Unresolved].

pnpm exact version and integrity; Node, OS and architecture; workspace root; config source and precedence.

Package name, exact version, registry or source type, dependency path, tarball integrity, alias, peer or patch suffix and lockfile commit.

preinstall, install, postinstall, prepare and other build triggers; script diff from prior approved version; downloaded or generated artifacts.

Bare name, exact name@version, exact-version union, source/repository key or explicit denial; why that breadth is necessary.

Reviewed version matches and runs only expected build steps in a clean isolated worker; commands, output and artifact digests.

Neighboring unapproved version or source form stays blocked; exact denial overrides wider allowance where expected.

Disposable runner, secrets absent, mounts, UID, process limits, network allowlist, private-network denial and teardown.

Removed pnpm 10 keys, effective pnpm 11 allowBuilds map, pending-build inventory and clean-checkout comparison.

Version, integrity, source, scripts, lockfile, pnpm, Node and policy changes that force a new review.

Approve exact, approve name-wide, deny or quarantine; owner, expiry, halt trigger and known-good rollback version.

Primary sources

  1. pnpm v10.0.0 release notespnpm project · Reviewed: Major change that stopped dependency lifecycle scripts by default, onlyBuiltDependencies migration example and security motivation · Retrieved · Supports: pnpm 10.0.0 was released on 2025-01-07 and changed dependency lifecycle scripts to blocked by default, with named-package exceptions through onlyBuiltDependencies at that time.
  2. pnpm v10.1.0 release notespnpm project · Reviewed: Release timestamp and additions of ignored-builds, approve-builds and ignoredBuiltDependencies · Retrieved · Supports: pnpm 10.1.0 was released on 2025-01-26 and introduced commands for listing ignored builds and interactively approving dependencies whose install scripts may run.
  3. pnpm approve-builds documentation (versions 11 and 12)pnpm project · Reviewed: Command purpose, positional approval and denial syntax, allowBuilds writes, pending placeholders and pnpm 11 migration behavior · Retrieved · Supports: The command records approved package names as true and denied names as false in allowBuilds; pnpm 11 replaced and ignores several pnpm 10-era build-policy settings.
  4. pnpm v11.23.0 release notespnpm project · Reviewed: Release date and allowBuilds migration cleanup described for approve-builds · Retrieved · Supports: pnpm 11.23.0 was released on 2026-08-23 and made approve-builds remove obsolete pnpm 10 build-policy keys when writing allowBuilds.
  5. pnpm v11.24.0 release notespnpm project · Reviewed: Release date, global build approvals and package-manager release scope · Retrieved · Supports: pnpm 11.24.0 was released on 2026-08-24 and restored global build approvals; the registry also identified 11.24.0 as latest during this investigation.
  6. pnpm 11.24.0 build-policy and version-policy implementationpnpm project source · Reviewed: createAllowBuildFunction precedence, package-name and name-at-version matching, identity trust, ignored-build key generation and exact-version expansion dependency · Retrieved · Supports: The pinned implementation checks exact denials before approvals, then accepts either a bare package name or an exact name@version; ignored registry builds are proposed under the bare package name.
  7. Backstabber's Knife Collection: A Review of Open Source Software Supply Chain AttacksDIMVA preprint on arXiv · Reviewed: Abstract, dataset scope, supply-chain stages and limitations of the historical collection · Retrieved · Supports: The independent authors report a manually collected historical dataset of malicious npm, PyPI and RubyGems packages and distinguish code execution at multiple supply-chain stages; it provides context, not a pnpm 11 evaluation.

Limitations

This is a source-level policy investigation, not an end-to-end pnpm installer test, package audit, exploit, malware analysis, sandbox evaluation or cross-platform survey. The unattended command guard blocked the proposed real-package lifecycle-script run, so no third-party install script was executed and no installer output is claimed. The six-row matrix mirrors only the inspected matching branches and cannot prove call-site behavior for aliases, peers, patches, custom registries, Git, tarball, local, workspace or globally installed packages. Source was pinned to pnpm 11.24.0 commit cef4816dfbc9aa7ffbe67fa727c1eb9be5d5e1e7; later versions may differ. The independent paper is historical context and does not assess pnpm 11. Exact-version policy reduces approval drift but does not establish package safety, publisher trust, artifact provenance or runtime containment.

Disclosures

AccessAllGPT did not execute, accuse, scan or modify a third-party package for this article. pnpm maintainers did not review the audit or article. No vendor supplied access, private data, payment or endorsement. AccessAllGPT Research is operated by NeuralArc, is independent, and is not affiliated with OpenAI or pnpm. Publication-wide relationships are listed on the disclosures page.

Further AccessAllGPT guidance

  1. npm 12 Blocks Dependency Scripts—But a Git Dependency Still Ran prepare
  2. npm Install Is an Execution Boundary
  3. Node’s Permission Model Is a Seat Belt, Not an AI Code Sandbox
  4. Before You Give a Coding Agent Repository Access
  5. AccessAllGPT Research methodology
  6. Publication disclosures

Continue the research

Get evidence-led updates for teams making production AI decisions.