教程

在 pnpm 中设置 minimumReleaseAge,远离供应链攻击

2026-05-19 #开发者安全#pnpm

近来针对开发者的供应链攻击越来越频繁,且规模也在变大,比如最近的 Mini Shai-Hulud 供应链攻击事件 中,顶级项目的开发者也难以避免,事后攻击发起者黑客团队 TeamPCP 居然将攻击代码开源了(现已被 GitHub 下架),使得攻击更为容易。

不过一般攻击窗口时间都很短暂,只要不在相关时间内安装对应软件包即可避免。 pnpm 就提供了这一特性,minimumReleaseAge 是 pnpm 用来降低 npm 供应链攻击风险的配置。它的作用是:一个包的新版本发布后,必须经过指定分钟数,pnpm 才允许解析和安装它。

pnpm 官方说明该配置适用于所有依赖,包括传递依赖;它从 pnpm v10.16.0 开始加入,在 pnpm v11 中默认值变为 1440,也就是 1 天。

1. 为什么需要 minimumReleaseAge

很多供应链攻击的套路是:攻击者盗取维护者账号或发布流程,立刻发布一个恶意版本。恶意版本通常会在社区、平台或安全工具发现后被下架。pnpm 官方也建议通过 “延迟更新依赖” 降低安装受污染版本的概率,并说明延迟 24 小时通常可以避开许多高风险窗口。

1
minimumReleaseAge: 1440

表示只安装发布时间至少超过 1440 分钟,也就是 24 小时 的版本:

2. 前置条件:确认 pnpm 版本

minimumReleaseAge 从 pnpm v10.16.0 开始支持。

先检查当前版本:

1
pnpm -v

目前 pnpm 已发布 v11.1.3 版本,该配置默认就是 1440 。

如果项目里还没有固定 pnpm 版本,可以在 package.json 中添加:

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

注意 pnpm v11 要求 Node.js 22+

3. 把时间设置为一周

pnpm v11 在项目根目录会创建 pnpm-workspace.yaml 文件,可以直接修改该文件进行配置。

我们可以把时间设置为一周的时间 1440*7 = 10080

1
minimumReleaseAge: 10080

4. 常见取值

minimumReleaseAge 的单位是 分钟

场景 配置值 含义
不启用延迟 0 立即允许安装新版本
保守开发环境 60 延迟 1 小时
推荐默认值 1440 延迟 1 天
更严格生产环境 2880 延迟 2 天
高安全项目 10080 延迟 7 天

5. 为特定依赖设置例外

有些包可能需要紧急升级,比如安全修复、内部包、框架核心包。这时可以使用 minimumReleaseAgeExclude

5.1 排除指定包

1
2
3
4
5
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- webpack
- react

这表示:大多数依赖必须发布满 1 天才能安装,但 webpack 和 react 可以立即安装。pnpm 官方文档 说明,minimumReleaseAgeExclude 会按包名排除,并作用于该包的所有版本。

5.2 排除组织作用域包

从 pnpm v10.17.0 开始, minimumReleaseAgeExclude 支持模式匹配。

例如,允许公司内部包立即安装:

1
2
3
4
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- "@myorg/*"

5.3 只排除特定版本

从 pnpm v10.19.0 开始,minimumReleaseAgeExclude 支持精确版本,也支持用 || 写多个版本。

1
2
3
4
5
minimumReleaseAge: 1440

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

这比直接排除整个包更安全,适合临时放行某个已验证版本。

6. 推荐的项目配置模板

下面是一份适合团队项目直接落地的配置:

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 是 pnpm v11 新增的行为控制项。官方文档说明:当没有版本满足 minimumReleaseAge 约束时, true 会让解析失败, false 则允许回退到不满足约束的版本;如果你显式配置了 minimumReleaseAge ,严格模式默认开启。

7. 在 CI 中引入

CI 里建议同时做两件事:

第一,固定 pnpm 版本:

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

第二,使用锁文件安装:

1
pnpm install --frozen-lockfile

这样 CI 不会随意解析新版本, minimumReleaseAge 主要会在新增依赖、更新依赖、刷新 lockfile 时发挥作用。

典型 GitHub Actions 示例:

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. 如何验证配置是否生效

可以尝试安装一个刚发布不久的版本:

1
pnpm add 某个刚发布的包@latest

如果该包版本发布时间没有达到配置的分钟数,pnpm 会避免解析该版本,或者在严格模式下直接失败。

更常见的验证方式是在更新依赖时观察 lockfile:

1
pnpm update

如果最新版本太新,pnpm 不会马上把它写入 pnpm-lock.yaml

9. 常见问题

Q1minimumReleaseAge 会影响传递依赖吗?

会。pnpm 官方文档明确说明该配置适用于所有依赖,包括传递依赖。

Q2 :pnpm v11 还需要手动配置吗?

pnpm v11 默认 minimumReleaseAge: 1440,也就是默认延迟 1 天。
如果你想更严格,需要手动改成 2880 或 10080;如果你想关闭,可以设置为:

1
minimumReleaseAge: 0

pnpm 官方供应链安全文档也说明,在 pnpm v11 中该值默认是 1440,如需退出该行为,可在 pnpm-workspace.yaml 中设置为 0。

Q3 :急需升级安全补丁怎么办?

推荐只临时放行具体版本:

1
2
3
4
minimumReleaseAge: 1440

minimumReleaseAgeExclude:
- [email protected]

处理完成后,再删除这个例外。

Q4:私有 registry 没有发布时间怎么办?

pnpm v11 提供了 minimumReleaseAgeIgnoreMissingTime。当为 true 时,如果 registry 元数据没有 time 字段,pnpm 会跳过该包的 minimumReleaseAge 检查;如果设为 false,则在缺失时间信息时失败。

更严格的配置:

1
2
minimumReleaseAge: 1440
minimumReleaseAgeIgnoreMissingTime: false

10. 最佳实践总结

推荐从这个配置开始:

1
2
minimumReleaseAge: 1440
minimumReleaseAgeStrict: true

如果是 monorepo 或企业项目,可以加上内部包例外:

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

minimumReleaseAge: 1440
minimumReleaseAgeStrict: true

minimumReleaseAgeExclude:
- "@your-company/*"

11. 其他包管理器如何设置

  • npm : 在 .npmrc 中, min-release-age=X 。X 是 的数量。需要 npm v11.10.0 或更高版本。
  • Yarn : 在 .yarnrc.yml 中,设置 npmMinimalAgeGate: X
    X 是持续时间(支持的日期单位是 ms, s, m, h, d, w,例如 7d)。如果没有指定持续时间,则解析为分钟(即 npmMinimalAgeGate: 1440 等于 npmMinimalAgeGate: 1440m)。需要 Yarn v4.11 或更高版本
  • Bun : 在 bunfig.toml 中,设置:
    1
    2
    3
    [install]

    minimumReleaseAge = X
    X 是秒数。需要 Bun v1.3.0 或更高版本。

参考链接

评论
分享

评论