Tutorial

Prevent Supply Chain Attacks by Setting minimumReleaseAge in pnpm

2026-05-19 #Developer Security#pnpm

Supply chain attacks targeting developers have become increasingly frequent and larger in scale. For instance, in the recent Mini Shai-Hulud supply chain attack, even developers of top-tier projects were not immune. Shockingly, the hacking group TeamPCP, who initiated the attack, open-sourced their attack code (which has since been taken down by GitHub), making such attacks even easier to execute.

However, the attack window is typically short, meaning you can avoid being compromised simply by not installing the affected package version during that period. pnpm provides a built-in feature to address this: minimumReleaseAge is a configuration designed to mitigate npm supply chain attack risks. When set, any newly published package version must wait for a specified number of minutes before pnpm allows it to be resolved and installed.

According to the official pnpm documentation, this configuration applies to all dependencies, including transitive ones. It was introduced in pnpm v10.16.0, and starting with pnpm v11, its default value is set to 1440 (which equates to 1 day).

It is highly recommended for pnpm users to upgrade to v11: pnpm - Migrating from v10 to v11

1. Why minimumReleaseAge is Necessary

A common pattern in supply chain attacks is for attackers to hijack a maintainer's account or publishing workflow, then immediately release a malicious version. These malicious releases are usually taken down quickly once detected by the community, registry platforms, or security tools. The pnpm team suggests "delaying dependency updates" to lower the probability of installing contaminated versions, stating that a 24-hour delay is generally sufficient to avoid most high-risk windows.

1
minimumReleaseAge: 1440

This specifies that pnpm should only install versions that have been published for at least 1440 minutes (24 hours):

2. Prerequisites: Confirm pnpm Version

minimumReleaseAge is supported starting from pnpm v10.16.0.

First, check your current version:

1
pnpm -v

Currently, pnpm has released v11.1.3, where this configuration defaults to 1440.

If your project does not have a pinned pnpm version yet, you can add it to your package.json:

1
2
3
{
"packageManager": "[email protected]"
}

Note: pnpm v11 requires Node.js 22+

3. Setting the Delay to One Week

In pnpm v11, a pnpm-workspace.yaml file is created at the project root. You can configure this setting directly in that file.

We can set the delay to one week (1440 * 7 = 10080):

1
minimumReleaseAge: 10080

4. Common Configuration Values

The unit for minimumReleaseAge is minutes.

Scenario Value Description
No delay 0 Allows immediate installation of new versions
Conservative Dev Environment 60 Delays by 1 hour
Recommended Default 1440 Delays by 1 day
Stricter Production Environment 2880 Delays by 2 days
High Security Projects 10080 Delays by 7 days

5. Setting Exclusions for Specific Dependencies

Some packages may require urgent upgrades, such as security patches, internal packages, or core framework packages. For these cases, you can use minimumReleaseAgeExclude.

5.1 Excluding Specific Packages

1
2
3
4
5
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- webpack
- react

This indicates that while most dependencies must be published for at least 1 day before installation, webpack and react can be installed immediately. As detailed in the pnpm documentation, minimumReleaseAgeExclude filters by package name and applies to all versions of that package.

5.2 Excluding Scoped Packages

Starting with pnpm v10.17.0, minimumReleaseAgeExclude supports pattern matching.

For example, allowing company internal packages to be installed immediately:

1
2
3
4
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- "@myorg/*"

5.3 Excluding Specific Versions

Starting with pnpm v10.19.0, minimumReleaseAgeExclude supports pinning specific versions, as well as using || to list multiple versions.

1
2
3
4
5
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- [email protected]
- [email protected] || 5.102.1

This approach is safer than excluding the entire package, making it ideal for temporarily permitting a verified version.

Below is a production-ready configuration template suitable for team projects:

1
2
3
4
5
6
7
8
9
10
11
12
13
packages:
- "apps/*"
- "packages/*"

# 新发布版本至少等待 24 小时再安装
minimumReleaseAge: 1440

# 显式开启严格模式:如果没有满足发布时间要求的版本,则安装失败
minimumReleaseAgeStrict: true

# 私有包或已验证的紧急修复版本可以临时放行
minimumReleaseAgeExclude:
- "@your-company/*"

minimumReleaseAgeStrict is a behavior control option introduced in pnpm v11. The documentation states that when no package version meets the minimumReleaseAge constraint, setting this to true causes dependency resolution to fail, whereas false allows falling back to versions that do not meet the constraint. If you explicitly configure minimumReleaseAge, strict mode is enabled by default.

7. Incorporating into CI

It is recommended to implement two practices in your CI pipeline:

First, pin the pnpm version:

1
2
3
{
"packageManager": "[email protected]"
}

Second, install using the lockfile:

1
pnpm install --frozen-lockfile

This ensures that CI does not arbitrarily resolve new package versions. Thus, minimumReleaseAge primarily plays a role when adding dependencies, updating packages, or updating the lockfile.

A typical GitHub Actions example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
name: CI

on:
pull_request:
push:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- run: pnpm install --frozen-lockfile
- run: pnpm test

8. Verifying the Configuration

You can test this by trying to install a recently published version of a package:

1
pnpm add <recently-published-package>@latest

If the release time of the package version has not reached the configured duration in minutes, pnpm will avoid resolving that version or fail directly when strict mode is active.

A more common way to verify is to inspect the lockfile during dependency updates:

1
pnpm update

If the latest version is too new, pnpm will not write it to pnpm-lock.yaml.

9. FAQ

Q1: Does minimumReleaseAge affect transitive dependencies?

Yes. The official pnpm documentation explicitly states that this configuration applies to all dependencies, including transitive ones.

Q2: Is manual configuration still required in pnpm v11?

In pnpm v11, minimumReleaseAge defaults to 1440 (a 1-day delay).
If you wish to enforce stricter rules, you must manually change it to 2880 or 10080. If you wish to disable this feature, set it to:

1
minimumReleaseAge: 0

The official pnpm supply chain security documentation also states that in pnpm v11, this value is 1440 by default. To opt out of this behavior, configure it to 0 in pnpm-workspace.yaml.

Q3: What if I need to upgrade an urgent security patch?

It is recommended to temporarily exclude the specific version only:

1
2
3
4
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- [email protected]

Once the update is complete, you can remove this exception.

Q4: What if my private registry does not contain release timestamps?

pnpm v11 provides minimumReleaseAgeIgnoreMissingTime. When set to true, if the registry metadata lacks a time field, pnpm will bypass the minimumReleaseAge check for that package. If set to false, it will fail when time information is missing.

A stricter configuration:

1
2
minimumReleaseAge: 1440
minimumReleaseAgeIgnoreMissingTime: false

10. Summary of Best Practices

We recommend starting with the following configuration:

1
2
minimumReleaseAge: 1440
minimumReleaseAgeStrict: true

For monorepos or enterprise projects, you can add exclusions for internal scoped packages:

1
2
3
4
5
6
7
8
9
packages:
- "apps/*"
- "packages/*"

minimumReleaseAge: 1440
minimumReleaseAgeStrict: true

minimumReleaseAgeExclude:
- "@your-company/*"

11. How to Configure in Other Package Managers

  • npm: In .npmrc, set min-release-age=X, where X is the number of days. This requires npm v11.10.0 or higher.
  • Yarn: In .yarnrc.yml, set npmMinimalAgeGate: X, where X is the duration (supported duration units are ms, s, m, h, d, w, e.g., 7d). If no unit is specified, it defaults to minutes (meaning npmMinimalAgeGate: 1440 is equivalent to npmMinimalAgeGate: 1440m). This requires Yarn v4.11 or higher.
  • Bun: In bunfig.toml, configure the following:
    1
    2
    3
    [install]

    minimumReleaseAge = X
    Where X is the duration in seconds. This requires Bun v1.3.0 or higher.

References

Comments
Share

Comments