莫潇羽 @ 源码七号站(www.fuyuan7.com)原创出品,转载请注明出处。
🔍 快速摘要
如果你只想看核心结论,这里是重点:
OSV-Scanner 是 Google 安全团队开源的依赖漏洞扫描命令行工具,基于全球最大的开源漏洞数据库 OSV.dev,支持 11+ 种编程语言和 19+ 种包管理器锁文件格式,一条命令即可完成整个项目所有依赖的安全扫描。 2025 年 3 月发布的 V2.0 版本进一步引入了容器镜像分层扫描、Maven 漏洞修复引导、交互式 HTML 报告等重磅新功能。工具完全免费,支持离线模式保护代码隐私,可无缝集成进 GitHub Actions CI/CD 流水线。无论你是前端、后端还是运维工程师,这款工具都值得纳入你的常规开发工作流。
往下看有更详细的原理剖析、安装配置与实战操作拆解。
一、依赖漏洞,开发者最难以察觉的安全炸弹
任何有过实际项目经验的开发者,都绕不开"第三方依赖"这个话题。
写一个前端页面,npm install 一下,几十上百个依赖包悄然涌入项目目录。写一个 Python 脚本,pip install 一轮,依赖树可能延伸出几十层。Java 项目的 pom.xml 更是洋洋洒洒写个不停——这些依赖背后,隐藏着你无法完全掌控的代码,而这些代码里可能早就存在已知的安全漏洞。
这并不是危言耸听。举一个非常典型的例子:2021 年底爆发的 Log4Shell 漏洞(CVE-2021-44228),影响的正是 Java 生态中被几乎所有后端项目直接或间接引用的 log4j-core 日志库。这个漏洞的 CVSS 评分高达 10.0(满分),攻击者只需要在日志中构造一段特殊字符串,就能在服务器上远程执行任意代码。而在漏洞曝光的第一时间,绝大多数使用了该库的开发者根本不知道自己的项目已经处于威胁之中——因为 log4j-core 往往不是直接依赖,而是作为某个框架的传递性依赖被悄悄带进来的,在 pom.xml 里根本找不到它的踪迹。
这种情况在软件开发中非常普遍。你写的代码只占整个项目的一小部分,更大的那部分是由无数第三方库组成的,这些库又各自依赖了更多的库,层层叠叠,形成一棵庞大的依赖树。现代前端项目动辄拥有上千个传递性依赖,其中任何一个出现漏洞,理论上都可能成为攻击入口。
这种风险有多真实?根据 Synopsys 2024 年发布的《开源安全与风险分析报告》(OSSRA),被抽查的商业代码库中有 96% 包含开源组件,而其中大量存在已知但未修复的安全漏洞。换言之,几乎所有的线上商业项目,都存在某种程度的依赖安全隐患。
问题的根源不在于"用了开源依赖"本身,而在于漏洞信息的不对称。这个不对称体现在几个层面:
第一,发现滞后。库作者修复了一个漏洞并发布了新版本,但你可能几个月内都不会升级,因为你甚至不知道这件事发生了。CVE 数据库和 GitHub Advisory 每天都在新增条目,但没有人会主动推送通知给每一个使用了某个库的开发者。
第二,传递性依赖难以追踪。直接依赖还好,你至少知道自己引用了哪些包。但传递性依赖(即你的依赖的依赖)的漏洞,在没有自动化工具的情况下,几乎不可能靠人工发现。
第三,修复成本估算困难。即使知道了某个漏洞,升级到修复版本也不一定简单——它可能与你项目里另一个依赖的版本约束冲突,牵一发而动全身。
当问题暴露,补救往往已来不及。这就是漏洞扫描工具存在的意义。而今天我要聊的这款工具,是这个领域里目前综合能力最强、覆盖范围最广、且完全开源免费的选择之一——Google OSV-Scanner。
二、OSV-Scanner 是什么,从哪里来
OSV-Scanner 是由 Google 开源安全团队(Google Open Source Security Team)于 2022 年 12 月正式发布的命令行漏洞扫描工具,使用 Go 语言编写,项目托管在 GitHub 上,目前已积累超过 9,800 个 Star。
它的核心工作逻辑很简单:读取你项目中的依赖清单文件(如 package-lock.json、go.mod、requirements.txt、pom.xml 等),提取出所有直接与间接依赖的版本信息,然后批量查询 OSV.dev 数据库,返回哪些依赖存在已知的安全漏洞。
这个项目从诞生之初就定位清晰:不是要做一个大而全、什么都能扫的安全平台,而是专注于把"开源依赖漏洞扫描"这一件事做到极致——数据准、覆盖广、好接入、能修复。在这个定位下,它选择以 Go 语言编写(天然跨平台、单二进制文件、无依赖运行),以 OSV.dev 作为唯一的漏洞数据源(权威、开放、机器可读),以命令行工具作为核心交互形式(易于在 CI/CD 中调用)。
理解 OSV-Scanner,有两个关键概念需要先搞清楚。
OSV.dev 是什么
OSV(Open Source Vulnerabilities) 是 Google 主导建立的开源漏洞数据库,也是 OSV-Scanner 背后的核心数据来源。
与传统的 CVE 数据库不同,OSV.dev 不只是一个"漏洞登记处",它的设计目标是让漏洞信息真正对开发者可用。它做了这样几件事:
聚合了来自多个权威渠道的漏洞公告,包括 GitHub Security Advisories、NVD(美国国家漏洞数据库)、RustSec Advisory Database、PyPI 安全公告、Ubuntu 安全通知等,目前覆盖超过 30 个生态系统来源,且这个数字还在持续增长。与此同时,统一使用 OSV Schema(一种基于 JSON 的结构化格式)存储漏洞信息,避免跨生态系统的信息割裂——同一个漏洞影响了多个语言生态时,只会有一条标准记录,而非散落在各处的碎片。
OSV Schema 最关键的设计是版本范围的精确描述。漏洞记录里不只是写"这个包有漏洞",而是精确标注"在 X 版本之前受影响、在 Y 版本中修复",这种机器可读的格式让工具能够自动比对你的依赖版本,无需人工逐条对照。
OSV.dev 完全开源,数据和代码都托管在 GitHub,任何安全研究者、库的维护者或社区成员都可以提交和修正漏洞信息,这种开放协作的模式保证了数据库的高质量和持续更新。
正因为数据来源权威、格式标准化,OSV-Scanner 才能做到扫描结果精准、误报率低。
OSV Schema:漏洞数据的"通用语言"
要真正理解 OSV-Scanner 为什么准确,就必须了解 OSV Schema 的设计。传统的 CVE 条目往往是一段非结构化的文字描述,里面夹杂着受影响版本的信息,但格式因来源而异,机器解析困难。
OSV Schema 用了一套标准化的 JSON 结构来描述漏洞,关键字段包括:
id:唯一标识符,如OSV-2021-1、CVE-2021-44228、GHSA-xxx-xxx-xxxaffected:受影响的包列表,每个条目包含包名、生态系统(如npm、PyPI、Go)、以及受影响的版本范围(ranges)ranges:分为SEMVER(语义版本范围)、ECOSYSTEM(生态系统特定版本范围)、GIT(受影响的 git commit 范围)三种类型severity:使用 CVSS 评分体系的严重程度references:漏洞相关的参考资料链接(补丁提交、公告、PoC 等)
一条典型的 OSV 漏洞记录(简化版)看起来是这样的:
{
"id": "GHSA-p6mc-m468-83gw",
"summary": "Prototype Pollution in lodash",
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "lodash"
},
"ranges": [
{
"type": "SEMVER",
"events": [
{ "introduced": "0" },
{ "fixed": "4.17.21" }
]
}
]
}
],
"severity": [
{
"type": "CVSS_V3",
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"
}
]
}
当 OSV-Scanner 读取到你项目里的 lodash@4.17.19 时,它会查到这条记录,发现 4.17.19 落在 [0, 4.17.21) 的受影响范围内,立即标记为存在漏洞,并告知修复版本是 4.17.21。整个过程完全是结构化的机器匹配,无需任何模糊搜索或人工判断。
OSV-SCALIBR 是什么
2025 年发布 V2 版本时,Google 同步开源了另一个底层库 OSV-SCALIBR(Software Composition Analysis Library),它是 OSV-Scanner 的"提取引擎",负责从各种项目格式(源码、制品、容器镜像等)中解析依赖信息。
在 V1 时代,OSV-Scanner 的依赖提取逻辑直接内置在工具自身,支持的格式有限。引入 OSV-SCALIBR 之后,依赖提取和漏洞匹配两个职责被清晰拆分:SCALIBR 专注于"从各种地方找到依赖",OSV-Scanner 专注于"把找到的依赖和漏洞数据库比对"。这种架构让生态系统扩展更加容易——如果你想支持一个新的包管理器格式,只需要在 SCALIBR 里新增一个提取器插件即可,无需改动漏洞匹配的核心逻辑。
OSV-Scanner V2 是 OSV-SCALIBR 官方的命令行前端工具,两者共同构成了 Google 开源安全生态的核心基础设施。除了 OSV-Scanner 之外,SCALIBR 也作为独立的 Go 库开放,供其他安全工具集成。
三、核心原理:OSV-Scanner 到底是怎么工作的
很多工具"能用但不知道为什么能用",实际上理解原理才能用对、用好。下面我尽量通俗地说清楚 OSV-Scanner 的完整工作流程。
第一步:依赖信息提取(Dependency Extraction)
OSV-Scanner 首先需要知道你的项目用了哪些库、各自是什么版本。
它不会去分析你的业务代码,而是专注于依赖清单文件——这类文件在不同生态系统里名称不同,但职责一样:精确记录项目所有依赖及其版本锁定信息。比如:
|
语言/生态 |
清单文件 |
|
Node.js |
|
|
Python |
|
|
Go |
|
|
Java |
|
|
Rust |
|
|
Ruby |
|
|
PHP |
|
|
.NET |
|
|
Haskell |
|
特别值得一提的是,许多锁文件不只记录了直接依赖,也会记录所有传递性依赖的精确版本。例如 package-lock.json 里会把整棵依赖树里所有节点的版本都锁住,OSV-Scanner 会把这些全部纳入扫描范围——这正是它能发现"藏在深处的传递性漏洞"的原因所在。
对于容器镜像,OSV-Scanner 还能解析镜像每个分层里的操作系统软件包(apt/apk/rpm 等包管理器安装的系统库)以及语言层面的依赖。
V2 版本引入 OSV-SCALIBR 之后,还能直接解析编译制品,包括 Node modules 目录、Python wheel 包、Java uber jar、甚至 Go 二进制文件——这意味着即使你没有源码清单(比如扫描一个从第三方获取的 jar 包),也能对制品进行扫描。这在供应链安全审计场景下非常有用。
第二步:哈希匹配与版本比对(Vulnerability Matching)
提取完依赖列表后,OSV-Scanner 会将每个包的名称 + 版本号与 OSV.dev 数据库进行匹配。
匹配过程并不是简单的字符串查找,而是一个版本范围比对的过程。OSV Schema 中的每条漏洞记录都包含受影响版本的精确范围描述(如上一节展示的 JSON 结构),OSV-Scanner 将你的依赖版本代入这些范围进行判断,从而确定是否命中。
这种范围比对能处理语义版本(SemVer)的所有情况:>= 1.0.0, < 1.2.3、>= 2.0.0-beta.1、<= 3.4.5 等复杂约束都能正确解析。对于不使用语义版本的生态系统(如某些 Python 包),OSV.dev 也有对应的生态系统特定版本范围格式。
对于 C/C++ 项目,由于没有统一的包管理格式,OSV-Scanner 还支持通过代码哈希的方式识别被"vendored"进来的第三方代码(即直接复制进项目的第三方源码),匹配已知漏洞,这是其他很多扫描工具不具备的能力。具体来说,它会计算 vendored 代码文件的哈希值,与 OSV.dev 中记录的已知漏洞文件特征进行比对。
第三步:调用链分析(Call Analysis,可选)
有一个很常见的抱怨:扫描工具报了一堆漏洞,但大多数根本不影响我的项目,全是噪音。
这种情况确实存在。很多漏洞存在于某个库里某个特定函数中,但如果你的代码压根没有调用那个函数,这个漏洞在实践中就不会被利用。可大多数扫描工具不管这些,只要你引用了该包的任何版本,就报给你看,让你自己判断。
OSV-Scanner 提供了一个可选的调用链分析(Call Analysis)功能来解决这个问题。它会对你的代码进行静态分析,建立一张函数调用图,然后将这张调用图与漏洞记录里的"受影响函数"列表进行比对——只有当漏洞函数确实在你的代码调用链上时,才会触发告警。
举个例子:假设 lodash 的某个版本在 mergeWith 函数中存在原型污染漏洞,但你的项目只用了 lodash.get 和 lodash.set,调用链分析就会判定该漏洞对你的项目没有实际影响,将其标记为"不可达"(unreachable),而非直接告警。
这一机制大幅降低了误报率,让扫描结果更聚焦、更有价值。开启调用链分析的命令参数为 --call-analysis,目前对 Go 和 Java 生态的支持较为完整,其他生态正在逐步推进。需要注意的是,调用链分析会增加扫描时间,对于大型项目可能需要数分钟,这是一个时间与精准度的权衡取舍。
第四步:输出报告
匹配完成后,OSV-Scanner 会生成一份完整的漏洞清单,其中每一条记录包含:受影响的包名和当前版本、漏洞唯一标识符(CVE 编号、GHSA 编号或 OSV ID)、CVSS 漏洞严重程度评分、修复该漏洞所需升级到的最低版本号,以及该依赖来自哪个清单文件(方便定位问题所在的子项目或模块)。
输出格式支持多种选择:终端表格(默认,适合人类阅读)、Markdown、JSON(适合脚本解析)、SARIF(Security Artifact Result Interchange Format,适合与 GitHub Security 代码扫描集成)、SPDX 和 CycloneDX(用于生成标准 SBOM 软件物料清单),以及 V2 新增的交互式 HTML 报告(适合团队共享和可视化呈现)。
整体数据流示意
为了让流程更直观,可以用以下示意图来理解:
你的项目文件夹
│
▼
[依赖提取] OSV-SCALIBR
│ 解析 package-lock.json / go.mod / pom.xml 等
│ 输出:{ 包名: 版本号 } 列表
▼
[版本比对] OSV.dev API 查询
│ 每个包名+版本 → 查询受影响版本范围
│ 网络模式:实时查询 OSV.dev
│ 离线模式:查询本地缓存数据库
▼
[调用链分析] (可选)
│ 分析代码调用图
│ 过滤"不可达"漏洞
▼
[报告生成]
└→ 终端表格 / JSON / SARIF / HTML ...
整个过程对你的业务代码是只读的,不会修改任何文件(除非你主动运行 fix 命令),也不会向外发送你的源代码——只会发送包名和版本号去查询漏洞数据库。
四、安装方式:三种途径,总有一款适合你
OSV-Scanner 是一个单一二进制文件的命令行工具,没有运行时依赖,安装过程非常干净。根据你的系统环境,有三种安装方式可以选择。
方式一:直接下载预编译二进制(推荐,最简单)
这是官方推荐的安装方式,不需要任何额外的开发环境,下载即可运行。访问 GitHub Releases 页面下载对应平台的可执行文件:
https://github.com/google/osv-scanner/releases/latest
不同操作系统对应的文件名格式为:
osv-scanner_linux_amd64 # Linux 64位(大多数云服务器)
osv-scanner_linux_arm64 # Linux ARM(如部分 ARM 架构服务器)
osv-scanner_darwin_amd64 # macOS Intel 芯片
osv-scanner_darwin_arm64 # macOS Apple Silicon(M系列芯片)
osv-scanner_windows_amd64.exe # Windows 64位
Linux / macOS 安装步骤:
# 以 Linux amd64 为例,下载最新版本
curl -fsSL https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 -o osv-scanner
# 赋予执行权限
chmod +x osv-scanner
# 移动到系统 PATH 目录,使其全局可用
sudo mv osv-scanner /usr/local/bin/osv-scanner
# 验证安装
osv-scanner --version
Windows 安装步骤:
下载 osv-scanner_windows_amd64.exe 后,将其重命名为 osv-scanner.exe,然后将所在目录添加到系统环境变量 PATH 中,或直接在下载目录下通过命令提示符运行。
方式二:通过包管理器安装
如果你的系统有对应的包管理器,这是最简便的方式,后续升级也更方便。
macOS 用户(推荐 Homebrew):
brew install osv-scanner
Windows 用户(推荐 Scoop):
scoop install osv-scanner
使用包管理器安装的好处是,后续执行 brew upgrade osv-scanner 或 scoop update osv-scanner 即可一键升级到最新版本,无需手动下载替换。
方式三:通过 Go 工具链从源码编译安装
如果你本地已经安装了 Go 1.21 或更高版本的开发环境,可以直接用 go install 命令从源码编译并安装:
go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
编译完成后,可执行文件会被放到 Go 的 bin 目录(通常是 $GOPATH/bin 或 ~/go/bin),确保该目录在你的 PATH 里:
# 检查 Go bin 目录是否在 PATH 中
echo $PATH | grep -i go
# 如果不在,可以临时添加(或写入 ~/.bashrc / ~/.zshrc 永久生效)
export PATH="$PATH:$(go env GOPATH)/bin"
安装完成后,验证安装成功:
osv-scanner --version
# 输出示例:osv-scanner version v2.3.5
如果正确输出版本号(当前最新为 v2.3.x),即安装成功,可以开始扫描。
五、基础用法:五个最常用的扫描场景
场景一:扫描整个项目目录(最常用)
这是日常最频繁使用的命令。-r 参数代表递归扫描,OSV-Scanner 会自动识别目录及子目录中所有支持的依赖清单文件:
# 扫描当前目录
osv-scanner scan source -r .
# 扫描指定目录
osv-scanner scan source -r /path/to/your/project
扫描完成后,终端会以表格形式输出所有发现的漏洞,包括:
- 受影响的包名称和当前版本
- 漏洞 ID(如
CVE-2023-12345或GHSA-xxxx-xxxx-xxxx) - 漏洞严重程度(Critical / High / Medium / Low)
- 修复该漏洞所需升级到的最低版本
- 漏洞所在的依赖文件路径
场景二:扫描单个锁文件
如果你只想针对某一个特定的锁文件进行扫描,而不是整个目录:
# 扫描 npm 项目的锁文件
osv-scanner scan -L package-lock.json
# 扫描 Python 项目
osv-scanner scan -L requirements.txt
# 扫描 Go 项目
osv-scanner scan -L go.mod
# 扫描 Rust 项目
osv-scanner scan -L Cargo.lock
-L 参数的含义是"指定锁文件路径"(lockfile),OSV-Scanner 会根据文件名自动识别其所属的生态系统。如果文件名不是标准命名(比如你做了自定义重命名),可以手动指定类型:
osv-scanner scan -L --lockfile=requirements.txt:/path/to/my-deps.txt
场景三:扫描 Docker 容器镜像
OSV-Scanner V2 支持对容器镜像进行分层感知扫描(Layer-aware Scanning),这在容器化部署场景下非常实用:
osv-scanner scan image my-image-name:latest
运行此命令需要系统中已安装并运行 Docker 守护进程。
分层扫描的意义在于,它不只是告诉你"这个镜像里有漏洞",而是精确到哪一层引入了漏洞——是基础镜像里的 OS 软件包问题,还是你在 Dockerfile 里 apt-get install 的某个库,还是应用代码的 Python 依赖?来源不同,修复策略也完全不同。
目前支持分层扫描的基础系统为 Alpine OS、Debian 和 Ubuntu,支持的语言层依赖为 Go、Java、Node 和 Python。
容器扫描还有一项贴心功能:它会过滤掉不太可能影响该容器的漏洞,例如某个漏洞存在于 OS 层的一个库,但该库在这个容器的运行环境下根本不会被调用,扫描器会将其标记为低影响,减少无效告警。
场景四:启用交互式 HTML 报告
V2 版本引入了一个非常实用的可视化功能,可以在本地启动一个 HTML 报告页面:
osv-scanner scan --serve -r .
执行后,扫描器会在本地 8000 端口启动一个 Web 服务,打开浏览器访问 http://localhost:8000 即可看到图形化报告。报告支持按严重程度筛选、按包名或漏洞 ID 搜索、查看完整漏洞公告详情等交互操作。如果需要将报告分享给不熟悉命令行的团队成员(比如产品经理或安全合规团队),这个方式非常友好。
如果只想将结果导出为 HTML 文件而不启动 Web 服务:
osv-scanner scan --format html -r . --output-file report.html
场景五:多种结构化格式输出,对接下游工具
在 CI/CD 流水线中,扫描结果通常需要被其他系统消费,JSON 和 SARIF 是最常用的两种格式:
# 输出 JSON 格式,适合脚本解析
osv-scanner scan -r . --format json --output-file results.json
# 输出 SARIF 格式,适合上传到 GitHub Security 代码扫描
osv-scanner scan -r . --format sarif --output-file results.sarif
# 输出 Markdown 格式,适合写入 PR 评论或文档
osv-scanner scan -r . --format markdown --output-file results.md
# 输出 SPDX SBOM(软件物料清单)
osv-scanner scan -r . --format spdx-json --output-file sbom.spdx.json
六、进阶功能一:引导式修复建议(Guided Remediation)
扫描出漏洞只是第一步,更难的是"怎么修"。在依赖关系错综复杂的现代项目里,盲目升级一个依赖往往会引发一连串的兼容性问题。OSV-Scanner 的引导式修复功能正是为了解决这个问题而生。
它是怎么工作的
修复引擎会分析整个依赖图(dependency graph),而不是孤立地看每一个有漏洞的包。
理解依赖图的重要性可以用一个简单的例子来说明。假设你的项目直接依赖了包 A,包 A 又依赖了包 B,包 B 又依赖了包 C,而漏洞就出现在包 C 的某个旧版本里。如果你只是被告知"包 C 有漏洞,升级到 C@2.0",但你的 package.json 里根本没有直接声明包 C,你要怎么操作?升级 A?升级 B?还是在锁文件里直接锁定 C 的版本?不同的策略有不同的风险和代价。
引导式修复引擎综合考虑以下几个维度来计算最优方案:
依赖深度决定了操作难度,直接依赖比传递性依赖更容易升级,风险更低,修改直接依赖版本约束即可;漏洞严重程度决定了优先级,Critical 和 High 级别的漏洞优先推入修复队列;修复的投资回报率(ROI)非常关键——升级某一个包,能同时解决多少个漏洞?引擎会优先推荐"牵一发而动全局"的升级方案,让你用最少的改动解决最多的安全问题;最后是修复策略的选择,支持 in-place(原地修改锁文件,不改 manifest 声明的版本约束)和 relock(放宽 manifest 中的版本约束后重新解析整棵依赖树)两种不同侵入性的策略。
最终输出是一个最小化的版本升级方案,而不是"把所有有漏洞的包全升到最新"——那种做法在大型项目里通常是灾难性的。
npm 项目的引导式修复
对于 npm 项目,引导式修复支持两种运行模式。
非交互式(自动化,适合 CI):
# 原地修改锁文件(不改 package.json)
osv-scanner fix \
--non-interactive \
--strategy=in-place \
-L path/to/package-lock.json
# 放宽约束后重新解析(同时修改 package.json 和锁文件)
osv-scanner fix \
--non-interactive \
--strategy=relock \
-M path/to/package.json \
-L path/to/package-lock.json
交互式(适合本地开发):
osv-scanner fix \
-M path/to/package.json \
-L path/to/package-lock.json
交互式模式会以对话形式引导你逐步确认每个升级建议,你可以选择接受、跳过或查看详情,过程更可控。这种方式适合第一次处理一个积累了大量漏洞的老项目,你可以逐一评估每个升级建议,而不是全盘接受。
指定过滤条件,让修复更精准:
osv-scanner fix \
--max-depth=3 \ # 只处理依赖深度 ≤ 3 的问题(越浅越安全)
--min-severity=5 \ # 只处理 CVSS 评分 ≥ 5 的漏洞(过滤低危噪音)
--ignore-dev \ # 忽略 devDependencies(开发工具的漏洞通常影响较小)
--strategy=in-place \
-L path/to/package-lock-json
Maven 项目的引导式修复
V2 版本新增了对 Maven pom.xml 的引导式修复支持,覆盖直接依赖和传递性依赖:
# 非交互式修复(使用 override 策略,通过 dependencyManagement 覆盖传递性依赖版本)
osv-scanner fix \
--non-interactive \
--strategy=override \
-M path/to/pom.xml
# 将所有依赖更新到最新版本(实验性功能,谨慎使用)
osv-scanner fix \
--non-interactive \
--strategy=latest \
-M path/to/pom.xml
Maven 的 override 策略不会修改 <dependencies> 中的直接依赖版本声明,而是在 <dependencyManagement> 块中添加版本锁定条目,这样做的侵入性更低,对项目的其他依赖影响更小。
如果你想先预览修复建议而不实际写入文件,可以加上 --dry-run 参数:
osv-scanner fix --dry-run --strategy=in-place -L package-lock.json
这会输出修复计划,但不会真正修改任何文件,适合在正式执行前做评估。
七、进阶功能二:完全离线模式(Offline Mode)
对于某些企业环境,安全合规要求可能禁止将项目依赖信息传输到外部网络,或者生产环境服务器本身就没有互联网访问权限,也可能是企业内网的 CI/CD 系统出于安全策略完全隔离了外网访问。OSV-Scanner 的离线模式可以完美应对这种场景。
离线模式的工作原理很直观:事先把 OSV.dev 的漏洞数据库下载到本地,之后扫描时直接查询本地数据库,整个过程不产生任何出站网络请求。
下载离线数据库
首先需要在一台有网络访问权限的机器上,将 OSV 数据库下载到本地:
# 将数据库下载到指定目录
osv-scanner --offline --download-offline-databases /path/to/local/db
# 也可以用相对路径
osv-scanner --offline --download-offline-databases ./osv-db
下载过程会按照生态系统分类拉取数据,每个生态系统的漏洞数据存储在单独的压缩文件中。完整数据库的大小因覆盖的生态系统数量而异,通常在数百 MB 到数 GB 不等。下载完成后,可以用以下命令检查数据库内容:
ls -lh /path/to/local/db/
# 会列出类似 all.zip、npm.zip、PyPI.zip 等文件
将数据库传输到隔离环境
在实际的企业场景中,下载机器和扫描机器往往是不同的。可以通过以下方式将数据库传输到隔离环境:
# 打包数据库目录
tar -czf osv-db.tar.gz /path/to/local/db/
# 通过内网文件服务器、FTP、U 盘等方式传输到目标机器后解压
tar -xzf osv-db.tar.gz -C /opt/osv-db/
使用离线模式扫描
数据库就位后,在任何网络环境(包括完全断网的环境)中都可以进行扫描:
osv-scanner --offline \
--local-db-path /path/to/local/db \
scan source -r ./your-project
也可以扫描单个锁文件:
osv-scanner --offline \
--local-db-path /opt/osv-db \
scan -L package-lock.json
离线模式下,扫描器不会发出任何网络请求,你的代码结构、包名和版本信息完全不会离开本地环境。对于有信息安全合规要求(如 等保三级、ISO 27001、SOC 2 等)的组织,这一特性尤为重要。
离线数据库的更新策略
离线数据库不会自动更新,这是需要主动管理的一个运维事项。一旦数据库内容过时,新曝出的漏洞就无法被扫描发现。
建议的做法是建立一个定期更新机制:在有网络访问权限的"数据中转机器"上,设置定时任务(cron job)每周自动重新下载数据库,然后通过内网文件共享机制同步到各个隔离的 CI 节点或开发机器上。以下是一个简单的 shell 脚本示例:
#!/bin/bash
# 每周一凌晨 2 点自动更新 OSV 离线数据库
DB_PATH="/opt/osv-db"
BACKUP_PATH="/opt/osv-db-backup"
# 备份旧数据库,以防下载失败时回滚
cp -r $DB_PATH $BACKUP_PATH
# 下载新数据库
osv-scanner --offline --download-offline-databases $DB_PATH
if [ $? -eq 0 ]; then
echo "OSV 数据库更新成功"
rm -rf $BACKUP_PATH
else
echo "OSV 数据库更新失败,回滚到备份"
rm -rf $DB_PATH
mv $BACKUP_PATH $DB_PATH
fi
将这个脚本配置进 cron(crontab -e)即可实现自动化更新:
30 2 * * 1 /opt/scripts/update-osv-db.sh >> /var/log/osv-db-update.log 2>&1
离线模式下的扫描速度通常比联网模式快很多,因为省去了网络请求的往返时间,尤其是在扫描有大量依赖的项目时,速度差异非常明显。
八、进阶功能三:许可证合规扫描(License Scanning)
除了安全漏洞之外,开源许可证合规同样是企业开发中需要关注的问题。使用了 GPL 授权的库,可能要求你的商业项目也必须开源;使用了某些限制性许可证的库,可能与你的商业授权冲突。
OSV-Scanner 内置了许可证扫描功能,底层利用 deps.dev 数据库的许可证数据:
# 输出所有依赖的许可证摘要
osv-scanner scan --licenses -r ./your-project
# 检查是否存在不符合白名单的许可证
# 如果发现不在白名单内的许可证,命令会返回非零退出码(可用于 CI 卡口)
osv-scanner scan --licenses="MIT,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC" -r ./your-project
扫描报告会列出每个依赖包所使用的许可证类型,你可以据此判断是否存在合规风险。这对于需要满足 IP 合规要求的企业项目非常实用。
九、将 OSV-Scanner 集成到开发工作流
工具本身的价值是有限的,真正的价值在于将其融入日常开发流程,让安全检查从"事后补救"变成"事前预防"。
方式一:集成到 Git Pre-Commit 钩子
Pre-commit 钩子会在你每次执行 git commit 之前自动运行,确保有漏洞的依赖不会被提交进仓库。
使用 pre-commit 框架配置:
在项目根目录创建或编辑 .pre-commit-config.yaml 文件:
repos:
- repo: https://github.com/google/osv-scanner/
rev: v2.2.4 # 建议锁定版本,避免意外更新
hooks:
- id: osv-scanner
安装 pre-commit 框架并初始化钩子:
pip install pre-commit
pre-commit install
之后每次 git commit 前都会自动触发扫描,如果发现漏洞,提交会被中断,并展示漏洞详情。
方式二:集成到 GitHub Actions CI/CD 流水线
这是团队协作场景下最推荐的接入方式。OSV-Scanner 提供了官方的 GitHub Action,可以在 PR 和定时任务中自动运行。
PR 差异扫描(只报告本次 PR 新引入的漏洞,不影响历史问题):
在项目中创建 .github/workflows/osv-scanner-pr.yml:
name: OSV-Scanner PR Check
on:
pull_request:
merge_group:
permissions:
actions: read
security-events: write
contents: read
jobs:
scan-pr:
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.3.4"
这个工作流会比较目标分支和功能分支之间的漏洞差异,只在 PR 引入新漏洞时才会失败,不会因为历史遗留漏洞而阻塞所有 PR。
定期全量扫描(每周定时扫描整个项目):
创建 .github/workflows/osv-scanner-scheduled.yml:
name: OSV-Scanner Scheduled Scan
on:
schedule:
- cron: "30 12 * * 1" # 每周一 12:30 UTC
push:
branches: [main]
permissions:
actions: read
security-events: write
contents: read
jobs:
scan-scheduled:
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.3.4"
扫描结果会以 SARIF 格式上传到 GitHub 仓库的 Security > Code Scanning 标签页,团队成员可以在 GitHub 界面上直观查看所有漏洞详情,无需下载报告文件。
值得一提的是,包括 TensorFlow 和 Flutter 这样的大型 Google 开源项目,都在使用 OSV-Scanner GitHub Action 进行持续依赖安全扫描。
可配置的扫描参数(scan-args):
jobs:
scan-scheduled:
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.3.4"
with:
scan-args: |-
--recursive
--skip-git
./
fail-on-vuln: true # 发现漏洞时让工作流失败(可用于卡住发布)
upload-sarif: true # 上传到 GitHub Security
方式三:与发布流程联动,防止带漏洞上线
一个成熟的 DevSecOps 实践是在发布流程前增加安全门禁:只有通过了漏洞扫描,才允许触发部署。
下面是一个简单示例,将 OSV 扫描作为发布工作流的前置依赖:
name: Release
on:
push:
tags:
- 'v*'
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: OSV-Scanner Full Scan
uses: google/osv-scanner-action@v2
with:
scan-args: "--recursive ."
deploy:
needs: security-check # 必须在安全检查通过后才能部署
runs-on: ubuntu-latest
steps:
- name: Deploy
run: echo "Deploying..."
方式四:集成到 GitLab CI/CD
如果你的团队使用 GitLab 而非 GitHub,OSV-Scanner 同样可以集成进 GitLab CI/CD 流水线。目前官方没有提供现成的 GitLab 模板,但手动配置并不复杂。
在项目根目录创建或编辑 .gitlab-ci.yml,添加以下 job:
stages:
- security
- build
- deploy
osv-scan:
stage: security
image: golang:1.21-alpine
before_script:
- go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
- export PATH="$PATH:$(go env GOPATH)/bin"
script:
- osv-scanner scan source -r . --format json --output-file osv-results.json
- osv-scanner scan source -r . # 再跑一次终端输出,便于在 CI 日志里直接查看
artifacts:
when: always
paths:
- osv-results.json
expire_in: 30 days
allow_failure: false # 发现漏洞时让该 stage 失败,阻止后续 build/deploy
如果你不想每次都从源码编译安装(会增加 CI 耗时),可以改用预编译二进制的方式:
osv-scan:
stage: security
image: ubuntu:22.04
before_script:
- apt-get update -qq && apt-get install -y curl
- curl -fsSL https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 -o /usr/local/bin/osv-scanner
- chmod +x /usr/local/bin/osv-scanner
script:
- osv-scanner scan source -r .
对于 GitLab 的 Security Dashboard,如果你使用的是 GitLab Ultimate 版本,还可以将 SARIF 格式的结果上传,以便在 GitLab 的安全仪表盘中可视化查看:
osv-scan:
stage: security
image: ubuntu:22.04
before_script:
- apt-get update -qq && apt-get install -y curl
- curl -fsSL https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 -o /usr/local/bin/osv-scanner
- chmod +x /usr/local/bin/osv-scanner
script:
- osv-scanner scan source -r . --format sarif --output-file gl-dependency-scanning-report.json || true
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
方式五:结合 SBOM 生成,满足合规审计要求
在一些企业场景(如金融机构、政府采购项目、大型甲方的供应商审计),你可能需要向客户或监管机构提交软件物