标签 pnpm 下的文章

在过去一年中,我们见证了多起重大攻击事件,例如 Shai-Hulud 蠕虫攻击、Nx 构建系统被攻破,以及通过 tj-actions/changed-files 漏洞导致机密信息泄露到公开的 GitHub Actions 日志中。但仅仅是罗列各种攻击事件,就足以占据本文的全部篇幅,更不用说深入探讨了。

作为一个行业和生态系统,我们能感受到攻击频率的日益增加。仅在 2024 年,报告的恶意软件包数量就同比增长了 156%。鉴于 Mend 托管的 Renovate Cloud 平台受信于超过 130 万个代码仓库,我们在保护开源软件消费者方面处于非常有利的地位,同时也为自托管 Renovate 的用户提供了更强的安全默认设置。在一系列备受瞩目的 npm 供应链安全攻击之后,Mend Renovate 的维护者们决定,对于选择采纳“最佳实践”配置的用户,默认启用这项安全功能是最佳选择。

为了帮助客户更好地应对这些日益增多的攻击,维护团队正在 Mend Renovate 现有的“最佳实践”配置之上进行构建。该团队一直致力于提供更多“默认即安全”的配置,并首先从 npm 生态系统着手。

在最新的 Mend Renovate 42 版本中,使用“最佳实践”配置的用户将会发现,npm 生态系统中的依赖项更新现在需要通过一个“最短发布时间”的检查,即某个更新发布后必须经过 3 天的窗口期,Mend Renovate 才会提议进行更新。通过这种方法,组织可以确保只有经过验证的、稳定的和值得信赖的依赖项更新才能进入生产环境,从而在保持开发者效率的同时,最终降低供应链攻击的风险。

这有何帮助?
尽管影响广泛,但这些攻击通常利用了两种常见情况:

  • 依赖项的精确版本未被锁定。
  • 依赖项的精确版本已被锁定,但我们在其发布后极短时间内就尝试更新。

不锁定依赖项版本可能有其合理的原因。例如,在 npm 生态系统中,当你发布一个拥有若干依赖项且被许多其他包所依赖的包时。

如果每次你提升一个依赖版本都需要发布自己的包,那么所有依赖于你的包也同样需要提升版本并发布新版,从而在整个生态系统中引发连锁效应。

其中一些过程可以通过自动化来简化——自然是使用像 Mend Renovate 或 GitHub 的 Dependabot 这样的工具,但这仍然需要一定程度的人工审查。

与此同时,不锁定我们的依赖项可能会导致问题,用户可能会立即开始下载一个包的新版本。
在推荐锁定依赖版本后,下一个问题是我们应该多久更新一次。许多工具中现有的默认设置是“一旦有新版本就立即更新”,这可能导致一个恶意升级在其发布几分钟内就被创建为拉取请求 (Pull Request)。

尽管那个恶意的依赖项可能不会进入您开发人员的机器——但它有可能从您的自动化构建管道中窃取机密或其他特权信息——或者利用您 AI 驱动的代码审查工具中的提示注入漏洞。

如果我们增加软件包发布与它出现在您项目的拉取请求中的时间间隔,这就为安全研究人员和自动化安全工具提供了更多时间来发现软件包中的恶意意图,从而减少供应链攻击的可能性。
Mend Renovate 如何助力保障整个生态系统的安全

如上所述,在 Mend Renovate 的最新版本中,我们为所有使用“最佳实践”配置的用户启用了“最短发布时间”检查的强制执行。这适用于更新任何使用 npm 数据源的包,无论其使用的是何种 JavaScript/TypeScript 包管理器。

这项强制执行将:

  • 确保给定的依赖项更新包含其发布时间的元数据(“发布时间戳”)。
  • 确保在该版本发布后未满最少 3 天之前,不会创建任何分支。

如果发现不满足此要求的包更新,Mend Renovate 的依赖项仪表板中将包含一个“等待状态”的条目,并且需要人工明确请求才能更新——从而确保只有“安全”的包更新才会被提出。

(这里的一个告诫是,增加等待时间并不一定意味着所有问题都能被发现——由于针对性攻击或复杂的规避技术,所有问题可能无法都被捕获。)

通过将此功能直接添加到我们的“最佳实践”配置中,那些已经选择遵循行业最佳实践的用户将默认受到保护。而其他所有人也能够添加此功能,例如:
codeJSON

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["security:minimumReleaseAgeNpm"]
}

此外,还可以调整此行为——将等待窗口设置得任意长或短——或者对受信任的内部开发包绕过“最短发布时间”功能。

纵深防御
除了让 Mend Renovate 在满足特定条件(即经过一个给定的窗口期)前不发起更新之外,我们还建议建立多层防御:

在可能的情况下,在您的包管理器中启用此功能,以保护开发人员的机器;和/或在您的自动化构建管道中启用此功能,在发布窗口期过去之前使构建失败。

在撰写本文时,pnpm 10.6 和 yarn 4.2.0 已添加了对这些功能的支持,我们也看到其他包管理器正在考虑添加类似功能。

下一步计划?
继此版本的工作之后,维护团队将继续研究其他包生态系统,以便为我们的“最佳实践”配置启用相应功能,从而进一步保障面向消费者的产品和内部开发环境的安全!

在过去一年中,我们见证了多起重大攻击事件,例如 Shai-Hulud 蠕虫攻击、Nx 构建系统被攻破,以及通过 tj-actions/changed-files 漏洞导致机密信息泄露到公开的 GitHub Actions 日志中。但仅仅是罗列各种攻击事件,就足以占据本文的全部篇幅,更不用说深入探讨了。

作为一个行业和生态系统,我们能感受到攻击频率的日益增加。仅在 2024 年,报告的恶意软件包数量就同比增长了 156%。鉴于 Mend 托管的 Renovate Cloud 平台受信于超过 130 万个代码仓库,我们在保护开源软件消费者方面处于非常有利的地位,同时也为自托管 Renovate 的用户提供了更强的安全默认设置。在一系列备受瞩目的 npm 供应链安全攻击之后,Mend Renovate 的维护者们决定,对于选择采纳“最佳实践”配置的用户,默认启用这项安全功能是最佳选择。

为了帮助客户更好地应对这些日益增多的攻击,维护团队正在 Mend Renovate 现有的“最佳实践”配置之上进行构建。该团队一直致力于提供更多“默认即安全”的配置,并首先从 npm 生态系统着手。

在最新的 Mend Renovate 42 版本中,使用“最佳实践”配置的用户将会发现,npm 生态系统中的依赖项更新现在需要通过一个“最短发布时间”的检查,即某个更新发布后必须经过 3 天的窗口期,Mend Renovate 才会提议进行更新。通过这种方法,组织可以确保只有经过验证的、稳定的和值得信赖的依赖项更新才能进入生产环境,从而在保持开发者效率的同时,最终降低供应链攻击的风险。

这有何帮助?
尽管影响广泛,但这些攻击通常利用了两种常见情况:

  • 依赖项的精确版本未被锁定。
  • 依赖项的精确版本已被锁定,但我们在其发布后极短时间内就尝试更新。

不锁定依赖项版本可能有其合理的原因。例如,在 npm 生态系统中,当你发布一个拥有若干依赖项且被许多其他包所依赖的包时。

如果每次你提升一个依赖版本都需要发布自己的包,那么所有依赖于你的包也同样需要提升版本并发布新版,从而在整个生态系统中引发连锁效应。

其中一些过程可以通过自动化来简化——自然是使用像 Mend Renovate 或 GitHub 的 Dependabot 这样的工具,但这仍然需要一定程度的人工审查。

与此同时,不锁定我们的依赖项可能会导致问题,用户可能会立即开始下载一个包的新版本。
在推荐锁定依赖版本后,下一个问题是我们应该多久更新一次。许多工具中现有的默认设置是“一旦有新版本就立即更新”,这可能导致一个恶意升级在其发布几分钟内就被创建为拉取请求 (Pull Request)。

尽管那个恶意的依赖项可能不会进入您开发人员的机器——但它有可能从您的自动化构建管道中窃取机密或其他特权信息——或者利用您 AI 驱动的代码审查工具中的提示注入漏洞。

如果我们增加软件包发布与它出现在您项目的拉取请求中的时间间隔,这就为安全研究人员和自动化安全工具提供了更多时间来发现软件包中的恶意意图,从而减少供应链攻击的可能性。
Mend Renovate 如何助力保障整个生态系统的安全

如上所述,在 Mend Renovate 的最新版本中,我们为所有使用“最佳实践”配置的用户启用了“最短发布时间”检查的强制执行。这适用于更新任何使用 npm 数据源的包,无论其使用的是何种 JavaScript/TypeScript 包管理器。

这项强制执行将:

  • 确保给定的依赖项更新包含其发布时间的元数据(“发布时间戳”)。
  • 确保在该版本发布后未满最少 3 天之前,不会创建任何分支。

如果发现不满足此要求的包更新,Mend Renovate 的依赖项仪表板中将包含一个“等待状态”的条目,并且需要人工明确请求才能更新——从而确保只有“安全”的包更新才会被提出。

(这里的一个告诫是,增加等待时间并不一定意味着所有问题都能被发现——由于针对性攻击或复杂的规避技术,所有问题可能无法都被捕获。)

通过将此功能直接添加到我们的“最佳实践”配置中,那些已经选择遵循行业最佳实践的用户将默认受到保护。而其他所有人也能够添加此功能,例如:
codeJSON

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["security:minimumReleaseAgeNpm"]
}

此外,还可以调整此行为——将等待窗口设置得任意长或短——或者对受信任的内部开发包绕过“最短发布时间”功能。

纵深防御
除了让 Mend Renovate 在满足特定条件(即经过一个给定的窗口期)前不发起更新之外,我们还建议建立多层防御:

在可能的情况下,在您的包管理器中启用此功能,以保护开发人员的机器;和/或在您的自动化构建管道中启用此功能,在发布窗口期过去之前使构建失败。

在撰写本文时,pnpm 10.6 和 yarn 4.2.0 已添加了对这些功能的支持,我们也看到其他包管理器正在考虑添加类似功能。

下一步计划?
继此版本的工作之后,维护团队将继续研究其他包生态系统,以便为我们的“最佳实践”配置启用相应功能,从而进一步保障面向消费者的产品和内部开发环境的安全!

解决什么问题

想要使用本地 monorepo 开发 + 纯 npm 管理的项目,不想对项目进行 pnpm 改造的开发者

项目地址

https://github.com/alamhubb/mono

Mono

零侵入式 Monorepo 开发工具

直接使用 TypeScript 源码开发,无需构建,无需改造项目


什么是 Mono?

Mono 是一套零侵入式 monorepo 开发工具。它允许你在开发期间直接使用 TypeScript 源码,无需构建包或重构项目。

pnpm workspace 的「链式污染」问题

使用 pnpm workspace 意味着整条依赖链都被迫使用 pnpm

项目 A (pnpm) → 必须用 pnpm
  └── 项目 B → 必须用 pnpm(被污染)
        └── 项目 C → 必须用 pnpm(被污染)
              └── 项目 D → 必须用 pnpm(被污染)
  • 所有相关项目都必须改成 pnpm
  • 所有开发人员都必须安装 pnpm
  • 所有 CI/CD 环境都必须配置 pnpm
  • 新成员 npm install 直接报错:workspace:*

详细分析请阅读:「链式污染」—— 为什么一个 pnpm 项目会逼着整条依赖链都改成 pnpm

Mono 的解决方案

使用 Mono,你只需:

  • 运行 mono ./src/index.ts - 就这么简单!
  • 无需重构项目,无需 workspace:*
  • 无需构建包,直接使用源码
  • 修改立即生效,npm/yarn/pnpm 都兼容

pnpm workspace vs Mono

方面pnpm workspaceMono
安装必须安装 pnpm可选
配置文件需要 pnpm-workspace.yaml不需要
package.json需要修改为 workspace:*不需要修改
克隆后使用必须 pnpm installnpm/yarn/pnpm 都可以
依赖包需要先构建直接使用源码
团队协作所有人必须用 pnpm不强制


包列表

本仓库包含两个协同工作的包:

用途安装
mono-mjsNode.js CLI - 用于构建工具、Vite 插件npm install -g mono-mjs
vite-plugin-monoVite 插件 - 用于浏览器运行时npm install -D vite-plugin-mono

何时使用哪个?

场景工具
运行脚本、构建工具mono
Vite 插件、编译器mono
浏览器端导入vite-plugin-mono
Vue/React 组件vite-plugin-mono


快速开始

1. 全局安装 CLI

npm install -g mono-mjs

2. 运行项目

# 直接运行 TypeScript
mono ./src/index.ts

# 使用本地包运行 Vite
mono ./node_modules/vite/bin/vite.js

3. (可选)添加 Vite 插件用于浏览器端

npm install -D vite-plugin-mono
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { viteMono } from 'vite-plugin-mono' export default defineConfig({
  plugins: [
    viteMono(),  // 放第一个! vue()
  ]
})


特性

  • 零侵入 - 无需重构项目,无需配置文件
  • 自动发现 - 递归查找所有本地包
  • 即时生效 - 修改立即生效
  • 包管理器无关 - 支持 npm、yarn、pnpm、bun
  • 零配置 - 默认 ./src/index.ts,可选 local 字段


工作原理

包发现

直线向上查找距离最远的项目根目录 (.idea/.vscode/.git/package.json)
  └── 递归扫描
      └── 查找所有 package.json
          └── 根据 "name" 字段注册

导入拦截

// 你的代码 import { utils } from 'my-utils' // Mono 重定向到源码 // → /path/to/my-utils/src/index.ts 


配置

零配置(默认)

所有包默认使用 ./src/index.ts。无需任何配置!

自定义入口(可选)

package.json 中添加 local 字段:

{ "name": "my-package", "local": "./src/main.ts" } 


环境要求

  • Node.js >= 18.19.0
  • ESM 项目 - package.json 中需要 "type": "module"



📌 转载信息
转载时间:
2026/1/14 18:00:12