Keeping Supply Chain Exclusions Honest
Your pnpm security config decays over time. minimumReleaseAgeExclude entries pile up, trustPolicyExclude entries become stale, and allowBuilds lists drift from reality. Here's how to catch it.
The first post in this series covered setting up pnpm’s supply chain controls. The second covered automated updates with cooldowns. This post covers the part I kept postponing: keeping the config from rotting.
The Problem
When I first set up minimumReleaseAgeExclude, trustPolicyExclude, and allowBuilds, each entry had a clear reason. But dependency trees change. Packages get upgraded, new ones get added, old ones get removed. The exclusion lists don’t clean themselves up — they only grow.
I found this while applying the same security setup to a second project. The first project had 31 overrides that weren’t doing anything. The second had minimumReleaseAgeExclude entries for packages published months ago, well past the age window. The configs looked hardened but were full of dead weight.
The Three Lists That Decay
minimumReleaseAgeExclude
This list exempts packages from the minimumReleaseAge check. It grows in two ways:
- Manual additions — I need a fresh release and add an entry to unblock it
- Automatic additions —
pnpm audit --fix=updateadds entries when patching vulnerabilities, so the patched version can install without waiting for the age window
The problem: entries are never removed. There’s no built-in cleanup mechanism (pnpm#11668). A minimumReleaseAgeExclude entry for a package published months ago is doing nothing — the package is already past the age window, so the exclusion is pure noise.
There’s also a bug where pnpm audit --fix update adds entries even for packages that wouldn’t be blocked (pnpm#11563). The list grows faster than it should.
trustPolicyExclude
This list exempts packages from the trustPolicy: no-downgrade check. It’s needed when a package doesn’t ship with provenance attestations but is known-safe.
The entries age out as upstream packages adopt provenance. A trustPolicyExclude for chokidar@4.0.3 was justified six months ago — the pinned version lacked provenance. But the latest release now ships with provenance, so the exclusion is holding back a security control for no reason.
allowBuilds
This list whitelists packages that may run install scripts. It should match the packages in your dependency tree that actually have postinstall scripts. But dependency trees change — packages get removed, new ones get added, and the allowlist drifts.
An allowBuilds entry for a package that’s no longer installed is harmless but confusing. An entry that’s missing for a newly added package causes pnpm install to fail, which is the correct behavior with strictDepBuilds: true.
The Audit Workflow
There’s no automated tool that cleans up these lists. The practical approach is a manual review, which takes minutes when done regularly.
Step 1: Check what’s installed
For allowBuilds, I verify each entry against the actual dependency tree:
pnpm ls @parcel/watcher
pnpm ls core-js
pnpm ls esbuild
If pnpm ls returns nothing, the package isn’t installed and doesn’t need an allowBuilds entry.
Step 2: Check publish dates
For minimumReleaseAgeExclude, I check whether entries are still needed:
pnpm info astro@6.3.8 --json | grep -A2 '"time"'
If the package was published well past the minimumReleaseAge window (months ago), the exclude is doing nothing. Remove it.
Step 3: Check provenance
For trustPolicyExclude, I check whether the excluded package now ships with provenance:
pnpm info chokidar@4.0.3 --json | grep -A5 '"attestations"'
If provenance has a URL, the package now has attestations and the exclude can be removed (after upgrading to the version that has provenance).
Step 4: Run pnpm audit
pnpm audit --prod
This catches known vulnerabilities regardless of exclusion lists. If audit finds issues, the exclusions aren’t protecting from everything.
When to Schedule Reviews
The review doesn’t need to be frequent. Good triggers:
- When upgrading packages — exclusions for old versions may no longer apply
- When
pnpm auditfinds new advisories — check whether exclusions are blocking fixes - When adding new dependencies — verify
allowBuildscovers any new install scripts - Quarterly — a standing calendar reminder to check the lists even if nothing else changed
Partial Automation
Renovate’s customManagers can detect version strings in pnpm-workspace.yaml and create PRs to update them. This keeps exclusion versions current but can’t decide whether an entry should be deleted. It’s a useful supplement, not a replacement for manual review.
The pnpm team is considering a pnpm cleanup command that would auto-prune stale entries (pnpm#11668). Until that lands, the workflow is manual.
What I’d Do Differently
I’d run this review quarterly from the start instead of discovering the staleness when applying the config to a second project. The exclusion lists were doing real work when I added them — I just never went back to check whether they still were.
References
- pnpm#11668 — feature request for
pnpm cleanupto auto-prune stale entries - pnpm#11563 — bug where
pnpm audit --fix updateadds unnecessary entries - pnpm
minimumReleaseAgeExclude— exemption list for age-based blocking - pnpm
trustPolicyExclude— exemption list for trust downgrade checks - pnpm
allowBuilds— install script allowlist - Renovate
customManagers— partial automation for version detection