安全指南
osv-scanner 实战:pnpm 支持、忽略 CVE、离线模式与常见错误
装好之后才会遇到的问题:pnpm-lock.yaml 是否支持、用 osv-scanner.toml 忽略单个 CVE、三个离线参数及其引发的错误、CI 中的退出码 0/1/127/128,以及从 v1 到 v2 改名的参数。
适用对象:已经安装了 osv-scanner、在使用中遇到问题的人。这里讲的是装好之后才会遇到的问题,不讲安装。所有内容都依据官方文档。
指定锁文件
# a single lockfile
osv-scanner scan source -L pnpm-lock.yaml
# walk a directory
osv-scanner scan source -r ./my-project/当无法从文件名推断格式时,在路径前加上要使用的解析器:
# parse an oddly named file as requirements.txt
osv-scanner scan source -L 'requirements.txt:/path/to/extra-requirements.txt'
# path containing a colon — lead with a bare ':' to keep filename inference
osv-scanner scan source -L ':/path/to/my:projects/package-lock.json'为什么用锁文件而不是清单文件
清单文件(package.json 等)记录的是你要求的版本范围,而不是实际安装的版本。锁文件记录的是解析后的确切版本,所以扫描结果与依赖树中实际存在的内容一致。根据版本范围来判断,证据要弱得多。
如何检查自己的环境
结果看起来不对时,先做下面五项检查,再考虑添加参数。每一项都只检查你自己的项目。
确认版本
osv-scanner --version。2025 年发布的 v2 改变了命令形式和多个参数名,所以为 v1 写的说明照做往往会失败。官方文档现在以 v2 为准。确认读取了什么
--format json --all-packages(--all-packages 只在 JSON 输出时有效)。确认退出码
echo $?,在 PowerShell 中用 $LASTEXITCODE。含义见下表。确认配置确实生效
osv-scanner.toml 只对所在目录中的文件生效,不会传递到子目录。在用 -r 扫描的 monorepo 中,要在每个锁文件旁边放一个,或者用 --config 把一个文件应用到全部。确认离线数据库的位置和新旧
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY 就从那里读取,否则依次是用户缓存目录、系统临时目录。其中的文件路径形式为 osv-scanner/<ecosystem>/all.zip。如果这些文件的修改日期很旧,你的判定结果也同样是旧的。退出码及 CI 中的处理
| 退出码 | 官方含义 | CI 中的处理 |
|---|---|---|
0 | 找到了包,没有匹配到已知漏洞 | 通过 |
1 | 找到了包,匹配到了漏洞 | 失败(这是检出结果:修复,或设定期限后忽略) |
127 | 一般错误 | 失败(扫描本身出了问题) |
128 | 没有找到包(没有读取到任何可读内容) | 失败(修正路径或添加锁文件) |
文档把 1–126 保留给与结果相关的情况,把 129–255 保留给与结果无关的错误。把 CI 的处理分成三类:0 通过、1 是检出结果、其他都是扫描故障,排查失败原因会快得多。
忽略特定漏洞
在被扫描的目录中放一个 osv-scanner.toml:
[[IgnoredVulns]]
id = "GHSA-xxxx-xxxx-xxxx"
ignoreUntil = 2026-12-31
reason = "Feature is not used and the code path is unreachable; re-evaluate at the December inventory"id(必填):漏洞标识符ignoreUntil(可选):过了这个日期会再次报告reason(可选):也一定要写,给下一个看的人,而那个人通常就是你自己
--config=/path/to/config.toml 会覆盖各目录的文件并在所有地方生效。注意,忽略一个漏洞,也会同时忽略被视为其别名的漏洞。
每条忽略都设期限,而不是永久删除
没有 ignoreUntil 的条目会永久消失,也没有人会再去看它。本站的规则是:每条忽略都带期限和理由。日期一过它就会重新出现,所以被推迟的判断不会被悄悄遗忘。为了让 CI 安静而加上的无期限忽略,正是半年后变成事故的那种东西。
离线模式:三个参数,三种作用
大部分困惑都出在这里。
| 参数 | 作用 |
|---|---|
--offline | 完全离线。 用之前下载的本地数据库进行判定,不更新数据库,也不向任何地方发送项目或依赖信息 |
--offline-vulnerabilities | 只有漏洞匹配是离线的;其他处理(如传递依赖的解析)仍可能使用网络 |
--download-offline-databases | 允许获取或更新本地数据库,只有同时设置了离线参数时才生效 |
--offline
完全离线;不更新数据库,不向外发送任何内容
--offline-vulnerabilities
只有匹配在本地进行;其他处理可能使用网络
--download-offline-databases(单独使用无效)
需要与上面某一个一起使用 → 单独使用会出现 "databases can only be downloaded when running in offline mode"
错误及其原因
databases can only be downloaded when running in offline mode→ 单独传入了--download-offline-databases;它需要和离线参数一起使用- 使用
--offline但还没有本地数据库 → 会出现提示数据库不存在的错误;第一次运行需要先获取数据库 no package sources found→ 该路径下没有任何内容被识别为锁文件或清单文件;用-L指定文件或用-r遍历
不靠猜测来选择
- 完全隔离网络的运行 →
--offline(第一次运行时加上--download-offline-databases) - 只要求漏洞数据留在本地 →
--offline-vulnerabilities - 固定数据库的位置 → 环境变量
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY - 排查问题 → 先不加任何参数运行,再逐步收窄
v1 文章里的参数用不了时
很多搜索结果是为 v1 写的。官方迁移指南列出的主要变化:
| v1 | v2 |
|---|---|
--experimental-offline | --offline |
--experimental-download-offline-databases | --download-offline-databases |
--experimental-call-analysis | --call-analysis |
--experimental-licenses 等 | 合并为 --licenses |
--docker / -D(容器) | osv-scanner scan image <image> |
--json | --format=json |
--skip-git | --include-git-root(含义相反) |
osv-scanner <dir> 作为 osv-scanner scan source <dir> 的简写仍然可用。在脚本中写出 scan source,下一个读的人更容易看懂意图。
常见错误及修正方法
常见错误
- CI 把「除 1 以外的任何退出码」都当作成功
- 加上
|| true让失败消失 - 只在仓库根目录放一个
osv-scanner.toml,以为它覆盖子目录 - 只提交了
package.json,没有锁文件 - 以为
--offline时仍会解析pom.xml的传递依赖 - 写忽略规则时不加
ignoreUntil
修正方法
- 只有 0 才通过,这样 128(什么都没读取)就不会变成绿色
- 改用带期限的忽略来处理个别检出结果
- 在每个目录放一个文件,或用
--config把一个文件应用到全部 - 提交锁文件(JavaScript 的话是
package-lock.json、pnpm-lock.yaml、yarn.lock或bun.lock) - 离线时传递依赖解析被禁用;如果允许联网,使用
--offline-vulnerabilities - 一定写上日期和理由,到期时重新判断
Maven 的 pom.xml 不像锁文件那样列出每个解析后的版本。默认情况下,osv-scanner 使用 deps.dev 解析器(加上 --data-source=native 时使用 Maven Central)计算传递依赖图。这需要网络,在 --offline 和 --no-resolve 下会被跳过。在完全隔离网络的运行中,要预期结果只覆盖 pom.xml 中直接写出的依赖。
真实案例:为什么 Log4Shell 难以回答
2021 年 12 月 Log4Shell(CVE-2021-44228) 出现时,很多组织无法迅速说清自己是否受影响。他们从未自己添加过 Log4j,它是通过框架和其他库进来的。CISA 的指南要求各组织全面列出已安装的软件,并与受影响软件的列表进行对照。
用到 osv-scanner 上,就是要确认两件事:
- 传递依赖确实被读取了:生态系统有锁文件的就用锁文件,Maven 则要在启用解析的状态下运行(不离线,不加
--no-resolve) - 清单中确实包含间接引入的库:用
--format json --all-packages输出并查看
在漏洞公开当天能否在几分钟内回答「我们是否受影响」,取决于事先是否把这份清单做对。
与相近工具的区别
| 工具 | 读取的数据 | 擅长的方面 |
|---|---|---|
| osv-scanner | OSV.dev | 跨生态系统、以锁文件为单位、可以离线运行 |
npm audit / pnpm audit | npm 公告数据库 | npm 项目立即可用,无需额外安装 |
| Dependabot | GitHub Advisory | 自动创建更新 PR,不只报告问题,还提出修复 |
| Trivy | 多个(包括 OSV) | 除应用依赖外,还覆盖容器镜像和 OS 包 |
只选一个才是错误。 数据库不同,发现的东西也不同。本站每天用 pnpm audit 做自动检查,用 osv-scanner 做跨锁文件的检查。
本站的看法:扫描器不会告诉你固定的版本已经变旧
有一个问题只有在实际运维中才会出现。用 overrides 把传递依赖固定到确切版本后,这个包就不再接收补丁更新,而扫描器只在存在已知漏洞时才会提醒。所以「你冻结的版本现在已经旧了」是一种没有人报告的状态。本站遇到过三次,之后才加上了自己的每日检查,把每个固定版本与同一主版本线上的最新补丁进行比较。扫描器显示的是今天的已知漏洞;将来容易让你暴露的状况,需要另外的机制。
参考资料
- OSV-Scanner 文档 — Usage / Offline Mode / Configuration
- Supported Artifacts and Manifests
- OSV-Scanner 文档 — Output(输出格式与退出码) / Migration Guide(v1 到 v2)
- CISA — Apache Log4j Vulnerability Guidance
接下来阅读
- 入门:osv-scanner 的安装与使用
- 排优先级:决定先修补哪个(CVSS、EPSS、KEV)
- 事件:Log4Shell(传递依赖让人看不清用了什么)
- 实务:漏洞应对的实务 / 防御 npm 供应链蠕虫(英文)
FAQ
Qosv-scanner 支持 pnpm-lock.yaml 吗?
支持。npm、yarn、pnpm 的锁文件都受支持。用 `osv-scanner scan source -L pnpm-lock.yaml` 直接指定文件,或者用 `-r` 遍历目录。
Q怎样忽略单个 CVE?
在被扫描的目录里放一个 `osv-scanner.toml`,添加带 `id` 的 `[[IgnoredVulns]]` 条目。还可以设置 `ignoreUntil`(期限)和 `reason`,这样例外会记录何时、为何被忽略,而不是永远消失。传入 `--config=/path/to/config.toml` 会覆盖各目录的文件并全局生效。
Q为什么会出现 'databases can only be downloaded when running in offline mode'?
因为 `--download-offline-databases` 只有在同时设置了离线参数时才生效,不能单独使用。想在运行时一并获取本地数据库,就把它和离线参数组合使用。
Q已经有 npm audit 和 Dependabot 了,还需要 osv-scanner 吗?
它们读取不同的数据库,覆盖的范围也不同。npm audit 使用 npm 的公告数据库,只适用于 npm;Dependabot 主要在 GitHub 上提出更新建议;osv-scanner 读取跨生态系统(npm、PyPI、Go、Maven 等)的 OSV.dev,以锁文件为单位工作,还能完全离线运行。一个工具抓到另一个漏掉的东西很正常,所以应把它们当作不同的工作,而不是只选一个。
QCI 应该如何处理 osv-scanner 的退出码?
按官方文档:0 表示找到了包且没有匹配到已知漏洞,1 表示发现了漏洞,127 是一般错误,128 表示完全没有找到包。只把 0 当作成功。如果规则是「只要不是 1 就通过」,128(什么都没扫描)也会变成绿色的通过。
Q为什么旧文章里的 --experimental-offline 或 --docker 用不了?
它们在 v2 中被改名或移动了。官方迁移指南的对应关系是:--experimental-offline 改为 --offline,--experimental-download-offline-databases 改为 --download-offline-databases,--docker 改为 `osv-scanner scan image` 命令,--json 改为 `--format=json`。先运行 `osv-scanner --version` 确认自己用的是哪个主版本。