Conversation
Block silent execution of dependency lifecycle scripts and add a release cooldown to mitigate hijacked-publish attacks. * strictDepBuilds: fail install when any dep wants to run scripts that are not explicitly handled, so newly added transitives can't sneak in postinstall code. * allowBuilds: deny esbuild and @parcel/watcher build scripts. Both ship prebuilt platform binaries via optionalDependencies, so the postinstall is redundant. Setting them to `false` means even a hijacked future release cannot run code at install time. * minimumReleaseAge: 7-day quarantine on freshly published versions. Also fixes the failing Docker build on dependency PRs since pnpm 11 (ERR_PNPM_IGNORED_BUILDS for esbuild and @parcel/watcher). Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
There was a problem hiding this comment.
Code Review
This pull request introduces supply-chain hardening for pnpm by enabling strict dependency builds and setting a minimum release age for packages. Feedback suggests correcting the configuration for blocking lifecycle scripts by using the "ignoredBuilds" array instead of a map in "allowBuilds". Additionally, it is recommended to update the pnpm version requirement in package.json to ensure compatibility with these new features.
There was a problem hiding this comment.
Pull request overview
Updates the pnpm workspace configuration to explicitly handle dependency lifecycle scripts and add supply-chain safety constraints, addressing CI failures introduced by pnpm v11’s stricter build-script handling.
Changes:
- Add
strictDepBuilds: trueto fail installs when dependencies request lifecycle scripts that aren’t explicitly handled. - Add
allowBuildsentries to explicitly deny lifecycle scripts foresbuildand@parcel/watcher. - Add
minimumReleaseAge: 10080(7 days) to quarantine newly published versions during dependency resolution.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
allowBuilds was added in pnpm 10.26 (and is the only syntax in v11+). The previous >= 10.0.0 constraint would let pnpm versions install that silently ignore allowBuilds, defeating the supply-chain hardening from the previous commit. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
|
❌ The last analysis has failed. |
New references/npm-pnpm-supply-chain.md covering layered defenses for npm-ecosystem dependencies: - pnpm 10.26+/11+ allowBuilds (per-package, per-version map syntax) with the security upside of denying lifecycle scripts even for hijacked future releases when prebuilt platform binaries make the postinstall redundant (esbuild, @parcel/watcher pattern). - strictDepBuilds default-true behavior in pnpm 11. - minimumReleaseAge for quarantining freshly published versions. - Lockfile + --frozen-lockfile, package-manager pinning via packageManager field, engines.pnpm floor tracking. - Deprecated-fields table (onlyBuiltDependencies, neverBuiltDependencies, ignoredBuiltDependencies -> allowBuilds) plus an explicit note that ignoredBuilds is not a pnpm setting. - npm and yarn equivalents. Source: production hardening of netresearch/timetracker-ui#717. Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Why
Every dependency PR has been failing CI since pnpm 11 landed (last green build on
main: 2026-05-06). The Docker build dies on:pnpm now refuses to silently skip lifecycle scripts. We need to tell it explicitly what to do — and that gives us an opportunity to harden the config rather than just bypass the check.
What this changes
Adds three settings to
pnpm-workspace.yaml:strictDepBuilds: trueallowBuilds: { esbuild: false, "@parcel/watcher": false }optionalDependencies(@esbuild/linux-x64,@parcel/watcher-linux-x64-glibc, …), so the postinstall is redundant. Settingfalsemeans even a hijacked future release cannot execute code at install time — name-based allowlists wouldn't catch that.minimumReleaseAge: 10080minimumReleaseAgeExcludefor hotfixes.Defense layering recap:
--frozen-lockfile— version substitution requires a lockfile diff (visible in PR review).allowBuilds: false— even a tarball with the right integrity hash can't run scripts.minimumReleaseAge— buys time for the community to flag a malicious publish.Verification
Built the Docker image locally with this config —
pnpm install --frozen-lockfilecompletes cleanly (noERR_PNPM_IGNORED_BUILDS),vite buildproduces the expected dist artifacts:esbuild's binary still works because the platform-specific
@esbuild/linux-x64-muslpackage was installed viaoptionalDependencies; same for@parcel/watcher-linux-x64-musl.Follow-ups (not in this PR)
pnpmviapackageManagerfield instead ofcorepack prepare pnpm@latestso future major-version pnpm changes don't silently break CI again.npm audit signatures/ sigstore) as a non-blocking job — most npm packages don't publish provenance yet, so hard-enforcement isn't practical tree-wide today.