Skip to content

chore: harden pnpm supply-chain config#717

Merged
CybotTM merged 2 commits intomainfrom
chore/harden-pnpm-supply-chain
May 10, 2026
Merged

chore: harden pnpm supply-chain config#717
CybotTM merged 2 commits intomainfrom
chore/harden-pnpm-supply-chain

Conversation

@CybotTM
Copy link
Copy Markdown
Member

@CybotTM CybotTM commented May 10, 2026

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:

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: @parcel/watcher@2.5.6, esbuild@0.27.2

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:

Setting Purpose
strictDepBuilds: true Fail install when any dependency wants to run a lifecycle script that isn't explicitly handled. Blocks newly added transitives from sneaking in postinstall code.
allowBuilds: { esbuild: false, "@parcel/watcher": false } Deny lifecycle scripts for these two. Both ship prebuilt binaries via platform-specific optionalDependencies (@esbuild/linux-x64, @parcel/watcher-linux-x64-glibc, …), so the postinstall is redundant. Setting false means even a hijacked future release cannot execute code at install time — name-based allowlists wouldn't catch that.
minimumReleaseAge: 10080 7-day quarantine on newly published versions. Reduces the window in which a hijacked publish can land before the community/npm flags it. Use minimumReleaseAgeExclude for hotfixes.

Defense layering recap:

  • Lockfile + --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.
  • Dependabot/Renovate review — humans see the integrity hash change.

Verification

Built the Docker image locally with this config — pnpm install --frozen-lockfile completes cleanly (no ERR_PNPM_IGNORED_BUILDS), vite build produces the expected dist artifacts:

Done in 3.3s using pnpm v11.0.9
✓ 100 modules transformed.
✓ built in 3.19s

esbuild's binary still works because the platform-specific @esbuild/linux-x64-musl package was installed via optionalDependencies; same for @parcel/watcher-linux-x64-musl.

Follow-ups (not in this PR)

  • Once merged, #714 (and the eslint-plugin-vue / renovate vite PRs) will rebase and the Docker build should go green.
  • Consider pinning pnpm via packageManager field instead of corepack prepare pnpm@latest so future major-version pnpm changes don't silently break CI again.
  • Provenance verification (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.

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>
Copilot AI review requested due to automatic review settings May 10, 2026 10:44
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pnpm-workspace.yaml
Comment thread pnpm-workspace.yaml
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: true to fail installs when dependencies request lifecycle scripts that aren’t explicitly handled.
  • Add allowBuilds entries to explicitly deny lifecycle scripts for esbuild and @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>
@sonarqubecloud
Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

@CybotTM CybotTM merged commit 308ce3d into main May 10, 2026
6 of 7 checks passed
@CybotTM CybotTM deleted the chore/harden-pnpm-supply-chain branch May 10, 2026 11:36
CybotTM added a commit to netresearch/enterprise-readiness-skill that referenced this pull request May 10, 2026
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>
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.

2 participants