Trivy Node.js 依赖扫描完全指南:npm、Yarn、pnpm 与 Bun 的锁文件解析、漏洞检测与许可证识别
【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy
Trivy 对 Node.js 生态提供开箱即用的支持:它能够解析npm、Yarn、pnpm、Bun四种包管理器生成的锁文件,完成 SBOM 生成、已知漏洞检测与许可证识别三类扫描。本文以 docs/guide/coverage/language/nodejs.md 为骨架,结合仓库内锁文件解析器与文件系统分析器的实现源码,系统讲解各类锁文件的支持范围、开发依赖处理规则、许可证提取链路以及容器镜像等场景下的扫描方式,帮助你在代码仓库、文件系统与镜像扫描中准确配置并解读 Node.js 依赖扫描结果。
支持范围总览:四种包管理器与三类扫描能力
Trivy 支持四种 Node.js 包管理器:npm、Yarn、pnpm和Bun。它们对应的产物文件均同时支持 SBOM、漏洞(Vulnerability)与许可证(License)三类扫描能力:
| Artifact | SBOM | Vulnerability | License |
|---|---|---|---|
| npm | ✓ | ✓ | ✓ |
| Yarn | ✓ | ✓ | ✓ |
| pnpm | ✓ | ✓ | ✓ |
| Bun | ✓ | ✓ | ✓ |
需要说明的是,Trivy 将编程语言扫描目标分为「构建前(pre-build)」与「构建后(post-build)」两类(详见 语言扫描索引):
- 构建前目标(代码仓库、文件系统):分析构建过程使用的文件,即上述四类锁文件;
- 构建后目标(容器镜像、rootfs):分析已安装包的元数据,即
node_modules下的package.json。
因此下述表格中的功能特性是否生效,取决于扫描目标;Trivy 支持的功能矩阵如下:
| Package manager | File | Transitive dependencies | Dev dependencies | Dependency graph | Position |
|---|---|---|---|---|---|
| npm | package-lock.json | ✓ | 默认排除,可用--include-dev-deps开启 | ✓ | ✓ |
| Yarn | yarn.lock | ✓ | 默认排除,可用--include-dev-deps开启 | ✓ | ✓ |
| pnpm | pnpm-lock.yaml | ✓ | v9 及以上可用--include-dev-deps开启 | ✓ | - |
| Bun | bun.lock | ✓ | 默认排除,可用--include-dev-deps开启 | ✓ | ✓ |
- Transitive dependencies:是否支持解析并上报间接(传递)依赖;
- Dev dependencies:开发依赖默认不纳入报告,需要时通过 --include-dev-deps 显式开启;
- Dependency graph:是否输出依赖图,用于在报告里追溯漏洞来自哪条依赖链——该能力对应 报告中的“Show origins of vulnerable dependencies”;
- Position:是否能在报告中给出依赖在锁文件中的具体位置(行号)。
提示:无论使用哪个包管理器,修改
package.json之后请务必重新生成并保持锁文件是最新的,否则扫描结果可能与实际安装的依赖不一致。
npm:解析 package-lock.json
Trivy 通过解析package-lock.json完成 npm 项目依赖扫描。其解析器位于 pkg/dependency/parser/nodejs/npm/parse.go,会先反序列化锁文件,再根据lockfileVersion字段分派到不同的解析逻辑(参见 parse.go#L101-L108):
- v1(旧版):走
parseV1,读取顶层dependencies树,逐层递归解析嵌套依赖。v1 schema 不含“直接依赖”标记,因此依赖关系(Relationship)被标记为Unknown,同时利用requires字段与上层版本表把间接依赖版本解析出来; - v2 / v3(现代版本):走
parseV2,读取扁平化的packages映射(key 形如node_modules/xxx),先解析工作区link(resolveLinks),再依据根包声明的dependencies/optionalDependencies/devDependencies/peerDependencies判断直接依赖,并利用“就近解析”算法(从内层node_modules向外层查找)构造DependsOn依赖边。每个包会记录其在锁文件中的位置(Locations),对应上表的 Position 能力。
对应的文件系统分析器在 pkg/fanal/analyzer/language/nodejs/npm/npm.go,其中Required逻辑(npm.go#L89-L101)有两个值得注意的细节:
- 跳过
node_modules目录内的package-lock.json,避免与外部锁文件重复扫描; - 保留
node_modules下的package.json,专用于后续许可证查找。
开发依赖默认不报告。需要在扫描时加入--include-dev-deps标志才会把开发依赖纳入结果。
npm 的许可证获取
自 Trivy v2 起,npm 的许可证信息可以直接从锁文件中读取(现代锁文件的packages条目会携带从package.json拷贝来的license字段)。如果锁文件中缺少许可证信息,Trivy 会转而分析与package-lock.json同级的node_modules目录,通过其下的package.json补全许可证。此外,若package.json中的license字段引用的是外部文件——例如SEE LICENSE IN LICENSE或LicenseRef-LICENSE这类写法——Trivy 会读取被引用的文件并对许可证进行分类,机制详见后文 许可证识别的完整链路。
Yarn:解析 yarn.lock 并借助 package.json 补全依赖关系
Trivy 解析yarn.lock,同时还会分析以下附加文件以收集关于已检测依赖的更多信息:
package.jsonnode_modules/**
Yarn 锁文件的解析器在 pkg/dependency/parser/nodejs/yarn/parse.go,按空行切分“块(block)”后逐块正则解析。源码中一个关键设计是协议(protocol)过滤:
- 仅
npm:或空协议(即 registry 上的普通 npm 包)会被纳入扫描(validProtocol); workspace、patch、file、link、portal、github、git等协议对应的条目会被跳过(ignoreProtocol)。
Package relationships(直接/间接依赖)
yarn.lock文件本身不包含包之间的关系信息(谁是直接依赖、谁是间接依赖)。为了补全这些信息,Trivy 会解析与yarn.lock同级的package.json,以及各工作区(workspace)的package.json。对应实现位于 pkg/fanal/analyzer/language/nodejs/yarn/yarn.go 的analyzeDependencies(yarn.go#L163-L195):把锁文件中的版本模式(pattern,例如ajv@^6.5.5)与package.json中声明的依赖约束做匹配,进而把包标记为Direct/Indirect,并为根包、工作区包重建完整的依赖树。
Development dependencies(开发依赖)
yarn.lock同样不含包组信息(生产依赖 vs 开发依赖)。Trivly 通过解析同级package.json及各工作区package.json来识别开发依赖,同时这一机制也用于支持 Yarn 的别名(alias)安装(即用另一个包名安装某个 npm 包)。源码中的walkDependencies(yarn.go#L280-L332)会先遍历开发依赖再遍历生产依赖,避免生产依赖被错误覆盖。
开发依赖默认不报告,同样需要使用--include-dev-deps标志才会纳入结果。
Yarn 的许可证获取
Trivy 对 Yarn 的许可证获取分为两条路径(参见 yarn.go#L394-L406):
- Yarn Classic(v1):分析
yarn.lock同级目录下的node_modules; - Yarn 2+(Berry):分析
.yarn目录——分别遍历.yarn/unplugged中的机器相关产物与.yarn/cache下的.zip包(后者需解压后在 zip 内定位node_modules)。
同时,分析器会跳过位于node_modules与.yarn缓存目录内的yarn.lock(yarn.go#L120-L146),避免重复或错误解析。若package.json的license字段引用了外部文件,Trivy 同样会读取该文件进行分类。
pnpm:解析 pnpm-lock.yaml 并构建漏洞依赖树
Trivy 解析pnpm-lock.yaml,筛选出生产依赖并为存在漏洞的依赖构建依赖树(tree)。解析器位于 pkg/dependency/parser/nodejs/pnpm/,其数据结构(types.go)反映出 pnpm 锁文件特有的复杂性:
- PackageKey(如
lodash@4.17.21):packages段中的条目 key,不含 peer 依赖信息; - SnapshotKey(如
jest-config@30.0.3(@types/node@24.0.7)):pnpm v9+snapshots段的条目 key,包含 peer 依赖信息,用于区分安装了不同 peer 依赖的同一包实例; - v9 锁文件新增字段:
importers与snapshots段; - pnpm 11 会把“包管理器环境锁文件”作为独立文档写入
pnpm-lock.yaml(含configDependencies、packageManagerDependencies),Trivy 会通过isEnvLockfile识别并跳过这类文档(types.go#L52-L63)。
lock file v9 版本与开发依赖
Trivy 支持pnpm-lock.yamlv9 及以后版本中的Dev字段。与 npm/Yarn 需要“从package.json推断 dev 状态”不同,pnpm v9 锁文件能够直接标识开发依赖;默认仍不报告开发依赖,需要时使用--include-dev-deps标志把开发依赖纳入结果。仓库测试数据同时覆盖了 v6(pnpm-lock_v6.yaml)与 v9(pnpm-lock_v9.yaml)格式。
pnpm 的许可证获取
pnpm-lock.yaml不携带许可证信息,因此要识别许可证,需要先安装依赖:Trivy 会分析与pnpm-lock.yaml同级的node_modules目录来检测许可证;若license字段引用外部文件(SEE LICENSE IN LICENSE、LicenseRef-LICENSE等),同样读取文件进行识别与分类。
pnpm 在当前功能矩阵中Position 不支持(对应值为-),即不会在报告中提供锁文件内依赖行号。
Bun:解析文本格式 bun.lock
Trivy 支持扫描由 Bun 生成的bun.lock文本锁文件。生成方式有两种:
- Bun v1.2 及以上:
bun.lock(文本格式)已是默认锁文件,无需额外配置; - Bun v1.1.39 及以上:通过
bun install --save-text-lockfile显式生成文本格式bun.lock。
对于更早的 Bun 版本,可以改用bun install -y生成一份 Yarn 兼容的yarn.lock,再交给 Trivy 按 Yarn 流程扫描。
注意:二进制格式的
bun.lockb不受支持。
Bun 的分析器位于 pkg/fanal/analyzer/language/nodejs/bun/bun.go,它会解析bun.lock并保留node_modules下的package.json供许可证查找使用(bun.go#L88-L100)。
Development dependencies(开发依赖)
与 npm/Yarn 不同,bun.lock本身就包含包组信息(生产依赖与开发依赖)。即便如此,Trivy默认不报告开发依赖,需要加入--include-dev-deps标志才会纳入结果。
Bun 的许可证获取
要识别许可证,需要提前安装依赖:Trivy 分析bun.lock同级目录的node_modules来检测许可证;对引用外部许可证文件的写法(SEE LICENSE IN LICENSE、LicenseRef-LICENSE)同样会读取文件完成分类。
已安装包扫描:容器镜像与 rootfs 中的 package.json
在容器镜像(image)、rootfs 等“构建后”目标扫描中,Trivy 通过package.json识别已安装的 Node.js 包。具体做法是:在node_modules目录下搜索所有package.json文件,识别已安装的包。此场景下仅提取包名、版本与许可证,dependencies字段不会被分析——因为真正的依赖结构应当由镜像中实际存在的包目录反映,而不是声明式的依赖字段。
需要特别注意的是:若要在容器镜像中检测 Node.js 依赖的漏洞,必须确保镜像内存在node_modules;否则请使用锁文件(package-lock.json、yarn.lock等)配合文件系统扫描(filesystem)来完成检测。也就是说,两种路径的适用场景是:
- 有
node_modules的镜像/rootfs → 直接扫描已安装包; - 有锁文件的代码仓库/目录 → 用 filesystem/repository 模式解析锁文件并扫描。
通用package.json解析器(pkg/dependency/parser/nodejs/packagejson/parse.go)也会校验包名是否符合 npm 的 URL 安全字符规范,并对workspaces的两种写法(字符串数组,或{"packages": [...]}映射)做统一解析。
许可证识别的完整链路
针对 npm、Yarn、pnpm、Bun 四类产物,许可证识别遵循一套统一的三级策略:
直接从锁文件读取(仅 npm v2+ 锁文件携带该信息);
扫描同级
node_modules:遍历其中各包根目录的package.json,提取license字段——对应通用实现 pkg/fanal/analyzer/language/nodejs/license/license.go 的Traverse(license.go#L37-L80)。若node_modules不存在,分析器会提示运行对应包管理器的install命令后再收集许可证信息;解析外部许可证文件引用:当
license字段的值不是标准 SPDX 表达式而是一个“文件引用”时,IsLicenseRefToFile(license.go#L84-L101)会识别以下两种写法并取出文件名:SEE LICENSE IN LICENSE(取最后一段作为文件名);LicenseRef-<filename>(取连字符后的内容作为文件名)。
随后
classifyLicense会打开该文件,调用licensing.Classify对许可证文本进行分类(分类置信度阈值由许可证扫描器的ClassifierConfidenceLevel配置决定)。需要理解的是,这套机制对 Yarn 还做了版本差异处理——Yarn Classic 扫描node_modules,Yarn 2+ 则遍历.yarn目录(详见前文各小节)。
如何纳入开发依赖:--include-dev-deps
文档中反复出现的--include-dev-deps标志定义在 pkg/flag/package_flags.go:
--include-dev-deps 包括开发依赖它对应的配置文件键为pkg.include-dev-deps(可通过trivy.yaml预设)。适用情况:
| 包管理器 | 开发依赖信息从何而来 | 默认行为 |
|---|---|---|
| npm | 锁文件 v2+ 的dev字段 | 排除 |
| Yarn | 解析锁文件旁的package.json及工作区package.json | 排除 |
| pnpm | 锁文件 v9+ 的Dev字段 | 排除 |
| Bun | bun.lock内建的分组信息 | 排除 |
实践建议
- 保持锁文件最新:扫描的准确性与锁文件强相关,修改
package.json后务必重新生成锁文件; - 按目标选择扫描路径:代码仓库/filesystem 用锁文件;镜像内检测 Node 漏洞则要求镜像含
node_modules,或用锁文件对镜像内的源码目录做 filesystem 扫描; - 按需开启开发依赖:默认结果聚焦生产依赖,审查开发期供应链风险时用
--include-dev-deps; - 追溯漏洞来源:配合
--show-origins等报告选项(见 报告配置)可查看漏洞来自哪条直接/间接依赖链,而对 pnpm 需注意其 Position(行号定位)能力暂不支持; - 深入验证:如需了解解析细节,可阅读解析器测试用例,例如 npm 解析测试数据(覆盖 v1–v3、workspace、bundled、broken link 等场景)、yarn 测试数据(覆盖 monorepo、alias、workspace、license 目录等)、pnpm 测试数据 与 bun 测试数据;端到端的 golden 文件(如 npm.json.golden、yarn.json.golden、pnpm.json.golden、bun.json.golden)则展示了完整的期望输出结构。
【免费下载链接】trivyFind vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more项目地址: https://gitcode.com/GitHub_Trending/tr/trivy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考