如何配置NPM默认忽略超过指定时间阈值的软件包版本?
嘿,这个防范供应链攻击的思路挺棒的!针对家庭环境,完全不用搞企业那种复杂的私有镜像,有几个轻量的方案能实现你的需求,我给你梳理一下最简便的两种:
方法一:项目级依赖时间检查(推荐)
这个方案针对单个项目配置,灵活可控,不会影响其他项目。核心思路是利用npm的preinstall钩子,在每次执行npm install前自动检查所有依赖的发布时间,超过阈值且未审核的包会阻止安装。
步骤1:准备检查脚本
在项目根目录创建一个名为check-dep-ages.js的Node.js脚本,内容如下:
const { execSync } = require('child_process'); const semver = require('semver'); const moment = require('moment'); // 自定义时间阈值(这里设为60天,即2个月) const THRESHOLD_DAYS = 60; // 加载已审核通过的旧版本包列表 let allowedPackages = new Set(); try { allowedPackages = new Set(require('./allowed-old-packages.json')); } catch (err) { // 如果文件不存在,初始化空集合 } // 获取项目的依赖列表(包括生产和开发依赖) const packageJson = require('./package.json'); const allDependencies = { ...(packageJson.dependencies || {}), ...(packageJson.devDependencies || {}) }; let hasUnapprovedOldPackages = false; for (const [pkgName, versionRange] of Object.entries(allDependencies)) { try { // 获取该包的所有版本及发布时间 const timeData = JSON.parse(execSync(`npm view ${pkgName} time --json`).toString()); // 过滤出有效的版本号 const validVersions = Object.keys(timeData).filter(v => semver.valid(v)); // 找到符合当前版本范围的最新版本 const matchedVersion = semver.maxSatisfying(validVersions, versionRange); if (!matchedVersion) { console.warn(`⚠️ 无法找到${pkgName}符合版本范围${versionRange}的有效版本`); continue; } // 计算版本发布至今的天数 const publishDate = new Date(timeData[matchedVersion]); const daysSincePublish = moment().diff(publishDate, 'days'); // 检查是否超过阈值且未审核 if (daysSincePublish > THRESHOLD_DAYS && !allowedPackages.has(`${pkgName}@${matchedVersion}`)) { console.error(`❌ 未审核的旧版本依赖:${pkgName}@${matchedVersion}`); console.error(` 发布时间:${publishDate.toLocaleString()},已超过${THRESHOLD_DAYS}天`); hasUnapprovedOldPackages = true; } } catch (err) { console.warn(`⚠️ 无法获取${pkgName}的版本时间信息:${err.message}`); } } // 如果存在未审核的旧包,终止安装流程 if (hasUnapprovedOldPackages) { console.error('\n请将需要保留的旧版本包添加到allowed-old-packages.json文件中,或调整依赖版本后重试'); process.exit(1); } else { console.log('✅ 所有依赖版本均符合时间要求,开始安装...'); }
步骤2:配置package.json
在项目的package.json中添加preinstall脚本,并安装所需的依赖:
{ "scripts": { "preinstall": "node check-dep-ages.js" }, "devDependencies": { "moment": "^2.29.4", "semver": "^7.5.4" } }
步骤3:初始化审核列表
在项目根目录创建allowed-old-packages.json文件,初始内容为空数组:
[]
当遇到必须使用的旧版本包时,将其格式如"pkg-name@version"添加到数组中即可,比如:
["lodash@4.17.21", "express@4.18.2"]
使用方式
每次执行npm install时,脚本会自动运行检查。如果存在未审核的旧版本包,安装会被终止;所有包符合要求或已审核时,才会继续安装。也可以手动触发检查:
npm run preinstall
方法二:全局生效的检查方案
如果你希望所有本地项目都默认应用这个规则,可以配置全局的npm钩子:
在用户目录下创建全局脚本(比如
~/.npm-global/scripts/check-global-dep-ages.js),内容和方法一中的check-dep-ages.js基本一致,只需修改allowedPackages的加载路径为全局文件(比如~/.npm-global/allowed-old-packages.json)。配置npm全局钩子:
npm config set preinstall "node ~/.npm-global/scripts/check-global-dep-ages.js"
不过这个方案灵活性稍差,不同项目的阈值或审核需求可能不同,所以更推荐项目级的方案。
内容的提问来源于stack exchange,提问作者Tomáš Zato

