跳到正文
>_ITDITDWeb 安全平台

安全指南

osv-scanner 实战:pnpm 支持、忽略 CVE、离线模式与常见错误

装好之后才会遇到的问题:pnpm-lock.yaml 是否支持、用 osv-scanner.toml 忽略单个 CVE、三个离线参数及其引发的错误、CI 中的退出码 0/1/127/128,以及从 v1 到 v2 改名的参数。

发布于 2026-09-04 更新于 2026-10-06 最后核实 2026-10-06 4 分钟阅读

适用对象:已经安装了 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 等)记录的是你要求的版本范围,而不是实际安装的版本。锁文件记录的是解析后的确切版本,所以扫描结果与依赖树中实际存在的内容一致。根据版本范围来判断,证据要弱得多。

如何检查自己的环境

结果看起来不对时,先做下面五项检查,再考虑添加参数。每一项都只检查你自己的项目。

1

确认版本

运行 osv-scanner --version。2025 年发布的 v2 改变了命令形式和多个参数名,所以为 v1 写的说明照做往往会失败。官方文档现在以 v2 为准。
2

确认读取了什么

运行日志会显示读取的每个文件以及在其中找到的包数量。确认预期的锁文件都在列表中,且数量没有少得可疑。要查看完整清单,使用 --format json --all-packages(--all-packages 只在 JSON 输出时有效)。
3

确认退出码

运行后立即在 bash 中用 echo $?,在 PowerShell 中用 $LASTEXITCODE。含义见下表。
4

确认配置确实生效

osv-scanner.toml 只对所在目录中的文件生效,不会传递到子目录。在用 -r 扫描的 monorepo 中,要在每个锁文件旁边放一个,或者用 --config 把一个文件应用到全部。
5

确认离线数据库的位置和新旧

本地数据库如果设置了 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"

三个离线参数。--download-offline-databases 单独使用不起作用,这就是大家搜索的那个错误的原因。

错误及其原因

  • 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 写的。官方迁移指南列出的主要变化:

v1v2
--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-scannerOSV.dev跨生态系统、以锁文件为单位、可以离线运行
npm audit / pnpm auditnpm 公告数据库npm 项目立即可用,无需额外安装
DependabotGitHub Advisory自动创建更新 PR,不只报告问题,还提出修复
Trivy多个(包括 OSV)除应用依赖外,还覆盖容器镜像和 OS 包

只选一个才是错误。 数据库不同,发现的东西也不同。本站每天用 pnpm audit 做自动检查,用 osv-scanner 做跨锁文件的检查。

本站的看法:扫描器不会告诉你固定的版本已经变旧

有一个问题只有在实际运维中才会出现。用 overrides 把传递依赖固定到确切版本后,这个包就不再接收补丁更新,而扫描器只在存在已知漏洞时才会提醒。所以「你冻结的版本现在已经旧了」是一种没有人报告的状态。本站遇到过三次,之后才加上了自己的每日检查,把每个固定版本与同一主版本线上的最新补丁进行比较。扫描器显示的是今天的已知漏洞;将来容易让你暴露的状况,需要另外的机制。

参考资料

接下来阅读

FAQ

Qosv-scanner 支持 pnpm-lock.yaml 吗?
A

支持。npm、yarn、pnpm 的锁文件都受支持。用 `osv-scanner scan source -L pnpm-lock.yaml` 直接指定文件,或者用 `-r` 遍历目录。

Q怎样忽略单个 CVE?
A

在被扫描的目录里放一个 `osv-scanner.toml`,添加带 `id` 的 `[[IgnoredVulns]]` 条目。还可以设置 `ignoreUntil`(期限)和 `reason`,这样例外会记录何时、为何被忽略,而不是永远消失。传入 `--config=/path/to/config.toml` 会覆盖各目录的文件并全局生效。

Q为什么会出现 'databases can only be downloaded when running in offline mode'?
A

因为 `--download-offline-databases` 只有在同时设置了离线参数时才生效,不能单独使用。想在运行时一并获取本地数据库,就把它和离线参数组合使用。

Q已经有 npm audit 和 Dependabot 了,还需要 osv-scanner 吗?
A

它们读取不同的数据库,覆盖的范围也不同。npm audit 使用 npm 的公告数据库,只适用于 npm;Dependabot 主要在 GitHub 上提出更新建议;osv-scanner 读取跨生态系统(npm、PyPI、Go、Maven 等)的 OSV.dev,以锁文件为单位工作,还能完全离线运行。一个工具抓到另一个漏掉的东西很正常,所以应把它们当作不同的工作,而不是只选一个。

QCI 应该如何处理 osv-scanner 的退出码?
A

按官方文档:0 表示找到了包且没有匹配到已知漏洞,1 表示发现了漏洞,127 是一般错误,128 表示完全没有找到包。只把 0 当作成功。如果规则是「只要不是 1 就通过」,128(什么都没扫描)也会变成绿色的通过。

Q为什么旧文章里的 --experimental-offline 或 --docker 用不了?
A

它们在 v2 中被改名或移动了。官方迁移指南的对应关系是:--experimental-offline 改为 --offline,--experimental-download-offline-databases 改为 --download-offline-databases,--docker 改为 `osv-scanner scan image` 命令,--json 改为 `--format=json`。先运行 `osv-scanner --version` 确认自己用的是哪个主版本。