The recurring npm attack pattern is boring by now: a legitimate package publishes a malicious version, projects install it, and an install-time lifecycle script steals tokens and CI secrets before anyone reads a line of code. The 2026 worm campaigns ran on exactly this loop.

pnpm v11 ships real defenses against it. Build scripts do not run without approval. A release-age window keeps brand-new versions out until they have survived in the wild for a while. Exotic transitive sources, git URLs and tarballs where a registry package should be, get blocked. This is the strongest default posture any mainstream package manager ships today.

All of it is configured in pnpm-workspace.yaml. Which lives in the repository. Which means one committed line weakens the defense for every developer and every CI runner that clones the project:

packages:
  - 'packages/*'
dangerouslyAllowAllBuilds: true   # every dependency now runs install scripts

The setting name is honest about it. Plenty of repos ship it anyway, because a dependency's build step failed once and the quick fix stuck.

The 9 things worth flagging

Aguara's new pnpm-policy analyzer reads pnpm-workspace.yaml and flags settings weakened below the pnpm v11 defaults. The full set, with the reasoning:

RuleSeverityWhat it means
PNPM_DANGEROUS_BUILDS_001HIGHdangerouslyAllowAllBuilds: true: every dependency runs install-time lifecycle scripts without approval. The exact entry point of the worm pattern.
PNPM_STRICT_DEP_BUILDS_DISABLED_001MEDIUMstrictDepBuilds: false downgrades unapproved build scripts from a hard failure to a warning nobody reads.
PNPM_EXOTIC_SUBDEPS_DISABLED_001MEDIUMblockExoticSubdeps: false lets transitive dependencies resolve from git and tarball URLs instead of the registry.
PNPM_TRUST_LOCKFILE_001MEDIUMtrustLockfile: true skips supply-chain verification for entries already in the lockfile.
PNPM_BUILD_APPROVAL_PENDING_001MEDIUMAn allowBuilds entry left undecided: a build script is still pending review, and the decision will probably be made under deadline pressure.
PNPM_MIN_RELEASE_AGE_DISABLED_001LOWThe release-age window is disabled: brand-new versions install immediately.
PNPM_MIN_RELEASE_AGE_NON_STRICT_001LOWThe window exists but is not strictly enforced.
PNPM_TRUST_POLICY_OFF_001LOWtrustPolicy: off set explicitly.
PNPM_LEGACY_BUILD_POLICY_001INFOpnpm v10 build settings that v11 silently ignores. You think you have a policy; pnpm disagrees.

A missing setting never fires. pnpm's v11 defaults are good; the analyzer only speaks up when a repo opts out of them.

The kebab-case trap

The most useful thing we learned building this came from testing against real pnpm versions instead of trusting documentation. In pnpm-workspace.yaml, pnpm 11 only honors camelCase keys. Write min-release-age instead of minReleaseAge and pnpm silently ignores the line. No warning, no error: the kebab-case spelling belongs to .npmrc, not to the workspace file.

That cuts both ways. A kebab-case hardening line gives you nothing but the feeling of safety. And a kebab-case weakening line is equally inert, which is why Aguara only matches the exact keys pnpm honors: flagging dangerously-allow-all-builds: true would be a false positive, because pnpm never reads it. We verified this against pnpm 8, 10, and 11.5.2; notably, pnpm 10 echoes config keys it does not implement, which is exactly how a wrong assumption survives a casual check.

The lockfile side of the same story

Posture is one half; the lockfile is the other. npm-family lockfiles support aliases, so a dependency can be installed under a local name that points at a different registry package: safe-ipc@npm:node-ipc@9.2.3. A scanner that matches advisories against the local name misses the compromised package behind the rename.

Aguara resolves npm: aliases to the real registry package in pnpm-lock.yaml, and the same resolution is rolling out across the rest of the npm lockfile family. For pnpm specifically this is hardening rather than a gap: pnpm itself normalizes aliased installs to real-name lockfile keys, so the protection matters for hand-edited or poisoned lockfiles and historical shapes.

Audit it in one command

$ aguara audit . --ci

HIGH  pnpm-workspace.yaml:3  PNPM_DANGEROUS_BUILDS_001
      dangerouslyAllowAllBuilds: true lets every dependency
      run install-time lifecycle scripts without approval

audit: exit 1

Each finding points at the exact line and resolves with aguara explain <ID>. The check runs offline, before install, in CI or on a fresh clone.

Is your pnpm posture what you think it is?

One scan covers the workspace policy, the lockfile, and everything else the repo ships.