Skip to content

chore: pnpm onlyBuiltDependencies allow-list (supply-chain hardening)#17

Merged
andresdefi merged 1 commit into
mainfrom
chore/pnpm-onlybuilt
May 22, 2026
Merged

chore: pnpm onlyBuiltDependencies allow-list (supply-chain hardening)#17
andresdefi merged 1 commit into
mainfrom
chore/pnpm-onlybuilt

Conversation

@andresdefi
Copy link
Copy Markdown
Owner

Summary

Locks postinstall script execution to an explicit allow-list. Without this, pnpm runs the postinstall script of any package in the tree — which is the vector behind every npm supply-chain attack in the May 2026 wave:

  • TanStack (May 11) — credential-stealing postinstall payload across 42 @tanstack/* packages
  • node-ipc (May 14) — 80 KB obfuscated payload in versions 9.1.6 / 9.2.3 / 12.0.1
  • Mini Shai-Hulud / @AntV (May 19) — 639 compromised versions, propagated through echarts-for-react

Even though pnpm audit is currently clean for this repo (none of the affected packages are installed transitively), the vector itself is open until we restrict it.

How it works

pnpm.onlyBuiltDependencies in package.json whitelists which packages may run install scripts. Any new dep with a postinstall script will be deferred by pnpm until it's added to this list — forcing a conscious review before arbitrary code runs.

Allow-list contents

Package Why
sharp + @img/sharp-* (per-platform variants) image processing in web-preview
esbuild Vite/vitest internal native binary
fsevents mac-only file watcher (no-op elsewhere)
@vitest/mocker dev tool internal

The @img/sharp-* are listed by platform (darwin-arm64 / linux-x64 / win32 / etc.) because pnpm resolves a different optional dep on each platform.

Defense in depth

This sits alongside the protections already in place:

  • --frozen-lockfile in CI — fresh-compromised versions can't reach the runner without a Dependabot PR
  • Dependabot security updates — auto-PRs the patched version when a CVE drops
  • Branch protection requiring CI green — behavioural malice usually breaks tests
  • Secret scanning + push protection — even if a postinstall harvests creds, they don't reach GitHub

Test plan

  • Clean install (rm -rf node_modules && pnpm install --frozen-lockfile) succeeds
  • pnpm build succeeds across all 3 packages
  • pnpm test — 534 / 534 passing
  • pnpm typecheck clean
  • pnpm audit clean

🤖 Generated with Claude Code

Locks postinstall script execution to a known set of packages. Without
this, pnpm 9.15 still defaults to running postinstall scripts of any
package added to the tree — the vector behind the May 2026 Mini
Shai-Hulud / @AntV / TanStack / node-ipc supply-chain attacks (each
shipped a credential-stealing payload via postinstall).

The list covers what genuinely needs native builds: sharp (image
processing in web-preview), esbuild (Vite/vitest internal), fsevents
(mac file watcher), @vitest/mocker. The @img/sharp-* variants are
listed by platform because pnpm resolves a different one on Linux CI
vs macOS dev vs Windows.

Any new dep added later that ships a postinstall script will be
deferred by pnpm until it's added to this list — forcing a conscious
review before arbitrary code runs at install time. CI's
--frozen-lockfile already prevents fresh-compromised versions from
reaching the runner before they're flagged in the GitHub Advisory
Database; this is the second layer for the case where an advisory
hasn't landed yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@andresdefi andresdefi force-pushed the chore/pnpm-onlybuilt branch from 0249271 to a11d910 Compare May 22, 2026 13:26
@andresdefi andresdefi merged commit 9ccc7ce into main May 22, 2026
4 checks passed
@andresdefi andresdefi deleted the chore/pnpm-onlybuilt branch May 22, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant