npm 终于动手了。作为 Node.js 默认软件包管理器,npm 在最新发布的 12 版本中,将allowScripts 默认设为关闭。这意味着依赖项的 preinstall、install 和 postinstall 脚本不再自动运行,除非项目明确允许。
这项变化早在今年 6 月就已公布,并从 npm 11.16.0 开始以警告形式逐步推进,给团队留出了升级缓冲期。现在,它正式成为默认行为。
不只是 postinstall 被禁
这次封杀的范围比想象中更广。除了显式声明的安装脚本,包含 binding.gyp 文件的软件包所隐式执行的 node-gyp 构建同样被阻止——即使该软件包并未声明任何安装脚本。来自 Git、文件和链接依赖项的 prepare 脚本也以相同方式被拦下。
开发者并非毫无办法。你可以审查等待运行的脚本,批准自己信任的那些,然后将生成的允许列表提交到 package.json 中。这套流程相当于给依赖安装加了一道人工审核关卡。
另外两条默认收紧
针对非注册表来源,npm 12 还做了两处调整:
- --allow-git 默认为 none:关闭了一条代码执行路径——即使设置了 --ignore-scripts,Git 依赖项自己的 .npmrc 仍可能覆盖 Git 可执行文件。
- --allow-remote 默认为 none:用于阻止 HTTPS tarball 依赖项。
相关的 --allow-file 和 --allow-directory 标志则保持不变。另外,全局安装和 npx 场景无法使用 approve-scripts,需要改用 npm config set allow-scripts=canvas,sharp --location=user 这类配置方式。
社区吵翻了:支持者说是毒瘤,反对者说没作用域
Hacker News 上的讨论拿到了 484 分和 200 多条评论。支持者 atraac 的发言很有代表性:
"postinstall 脚本早就应该被移除了,它就是 NPM 软件包的毒瘤。依赖深处存在大量不受控制的 postinstall,只要拉取一些东西,它们就会随机运行,简直荒谬。"
反对的声音同样存在。gear54rus 提到了 patch-package 这类合理使用场景;cookiengineer 则指出允许列表"没有作用域",导致"你的依赖项所依赖的任何软件包是否需要脚本都变得不可预测"。
安全研究人员的担忧:审批疲劳
OpenSourceMalware 的一位分析师在文章中指出,esbuild、sharp、core-js、puppeteer 和 bcrypt 都依赖生命周期脚本。他警告,反复出现的构建失败会让默认拒绝机制变成"一个直接点过去的提示框",同时把攻击者的活动推向可见性更低的攻击面。
JFrog 的报告给出了一个值得注意的数字:在过去一年观察到的恶意 npm 攻击中,这三种攻击途径涉及的事件约占 53%。
npm 是最后一个动手的主流包管理器
在安装脚本控制这件事上,npm 其实是追赶者。pnpm 多年来一直提供安装脚本允许列表;pnpm 10.16 引入了 minimumReleaseAge,Yarn 4.10.0 增加了 npmMinimalAgeGate,Bun 在 1.3 版本中跟进——这些都早于 npm 自己在 11.10.0 中推出的 min-release-age。
npm 12 的这次收紧,本质上是在补课。至于开发者买不买账,还得看实际使用中的摩擦有多大。至少从社区讨论的热度来看,这件事戳中了不少人的痛点。
热门跟贴