OSV-Scannerは、Googleが公開しているオープンソースの脆弱性スキャナです。ロックファイルやコンテナイメージから依存パッケージを読み取り、脆弱性データベースOSV.devと照合します。本記事では2026年10月12日公開のv2.7.0を実際に動かした結果をもとに、インストール、scan sourceとscan imageの使い方、終了コード、除外設定、fixコマンド、GitHub Actionsへの組み込み、Trivyとの使い分けを順に説明します。
まとめ:OSV-Scannerの使い方と採用判断の要点
OSV-ScannerはApache 2.0ライセンスで、アカウント登録もスキャン回数の上限もなく商用利用できます。2026年10月14日時点の最新版はv2.7.0です。13言語のロックファイル・マニフェストとSBOM、コンテナイメージを検査でき、脆弱性が見つかると終了コード1を返すため、CIの失敗条件にそのまま使えます。
選び方の結論を先に示します。アプリの依存パッケージの脆弱性検出と、npm・Mavenの修正バージョン提案が目的ならOSV-Scannerが向いています。OSパッケージ中心のイメージ監査で、サポート切れOSの警告や未修正の脆弱性の状態を確認したいなら、Trivyを主に据えるべきです。Trivyも全ての脆弱性を見つけるわけではなく、強みは警告と状態の表示にあります。2026年10月14日に同じイメージを両方で検査したところ、検出件数は大きくずれました。どちらが正しいかではなく、数え方と表示する範囲が違うためです(後述)。
OSV-ScannerとOSV.devの仕組み
OSV.devが集める脆弱性データとIDの種類
OSV-Scannerの照合先であるOSV.devは、各エコシステムの一次情報源をOSV形式という共通のJSON形式にそろえたデータベースです。OSV.devのデータソース一覧には、GitHub Advisory Database、PyPI Advisory Database、Go Vulnerability Database、RustSec、OpenSSF Malicious Packages、Ubuntu、AlmaLinux、Rocky Linuxなどが並びます。DebianとAlpineのセキュリティ情報、NVDのCVEはOSV.dev側で変換して取り込んでいます。
そのため検出結果のIDは、GHSA-(GitHub)、PYSEC-(PyPI)、DLA-(Debian LTS)のように出どころごとに異なります。同じ欠陥に複数のIDがある場合は別名(aliases)としてまとめて表示されます。形式の仕様はOSV Schemaで公開されており、2026年10月14日時点の仕様本文はVersion 1.9.2(2026年10月13日)です。
スキャン時に外部へ送られるデータ
公式READMEの「Data Sources and Privacy」によると、OSV.dev APIへ送るのはパッケージ名・バージョン・エコシステムと、C/C++の同梱コードを特定するためのファイルハッシュです。推移的依存の解決とライセンス情報の取得にはdeps.dev APIを使い、こちらもパッケージ名・バージョン・エコシステムだけを送ります。ソースコードそのものは送信されません。社外への送信自体を止めたい場合は、後述のオフラインモードを使います。
対応する13言語のロックファイルとイメージ内の成果物
対応言語とロックファイルの一覧には13言語が載っています。旧来の解説で「11言語」とされていたものに、Haskellと.NETが加わった数です。
| 言語 | 主な対応ファイル |
|---|---|
| JavaScript | package-lock.json / yarn.lock / pnpm-lock.yaml / bun.lock |
| Python | requirements.txt / poetry.lock / uv.lock / pylock.toml |
| Python(続き) | Pipfile.lock / pdm.lock |
| Java | pom.xml / gradle.lockfile / verification-metadata.xml |
| Go・Rust | go.mod / Cargo.lock |
| .NET | packages.lock.json / deps.json / packages.config |
| PHP・Ruby | composer.lock / Gemfile.lock / gems.locked |
| その他 | C/C++(conan.lock)/ Dart / Elixir / Haskell / R |
コンテナイメージでは、Alpine(apk)とDebian・Ubuntu(dpkg)のOSパッケージに加え、Goバイナリ、cargo-auditableでビルドしたRustバイナリ、Javaのuber jar、node_modules、Pythonのsite-packagesを読み取ります。
OSV-Scannerのインストール手順(macOS・Windows・Linux・Docker)
公式のインストール手順に載っている方法は次のとおりです。
# macOS・Linux(Homebrew)
brew install osv-scanner
# Windows(WinGet または Scoop)
winget install Google.OSVScanner
scoop install osv-scanner
# Arch Linux
pacman -S osv-scanner
# ソースから(モジュールパスに /v2 が入る)
go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
osv-scanner --version
go installの注意点として、インストール手順のページには「Go 1.26.2以上」とありますが、v2.7.0のgo.modはgo 1.27.0を要求しています。手元のGoが古いと、ツールチェーンの自動取得が働くかエラーになります。パッケージマネージャーを使えない環境では、GitHub ReleasesからOS別のバイナリ(Windowsはamd64とarm64の.exe)を取得できます。同じ場所にあるosv-scanner_SHA256SUMSとの照合で、取得したバイナリが公開チェックサムと一致するか確認できます。配布元やビルド由来の検証には、公式手順のSLSA provenance検証も利用できます。
Dockerではghcr.io/google/osv-scanner:v2.7.0(amd64とarm64)が公開されています。macOSのDocker Desktopで/tmp配下をマウントしたところ、コンテナからはファイルが見えず「No package sources found」・終了コード128になりました。ホームディレクトリ配下をマウントし直すと検出できました。0件で終わったときは、まずマウントの共有設定を疑ってください。
docker run --rm -v "$HOME/work/app":/src ghcr.io/google/osv-scanner:v2.7.0 scan source -r /src
scan sourceによる依存パッケージのスキャン
ディレクトリ・ロックファイル・SBOMの指定方法
# ディレクトリを再帰的に検査(.gitignore の対象は除外される)
osv-scanner scan source -r ./
# ロックファイルを直接指定(複数可)
osv-scanner scan source -L package-lock.json -L requirements.txt
# 規定外のファイル名は「形式:パス」で指定
osv-scanner scan source -L 'requirements.txt:./deps/extra-requirements.txt'
# SBOM も -L で渡す(ファイル名で形式を判定)
osv-scanner scan source -L ./sbom.spdx.json
SBOMは*.spdx.jsonやbom.json、*.cdx.jsonなど、各形式の命名規則に沿ったファイル名なら自動で判別されます。v2.7.0のヘルプでは専用の--sbomフラグは非推奨(DEPRECATED)と表示されるため、-Lに統一するのが安全です。SBOMの作り方と使い道はSBOMとは?ソフトウェア部品表の目的・フォーマットと作成・運用の判断を解説で整理しています。
requirements.txtの推移的依存と検出件数の実測
requests 2.19.0とjinja2 2.10だけを書いたrequirements.txtと、lodash 4.17.20・minimist 1.2.5を含むpackage-lock.jsonを用意し、v2.7.0で検査した結果です。
$ osv-scanner scan source .
Scanned /tmp/osv12926/proj/requirements.txt file and found 2 packages
Scanned /tmp/osv12926/proj/package-lock.json file and found 2 packages
Total 6 packages affected by 29 known vulnerabilities (1 Critical, 11 High, 17 Medium, 0 Low, 0 Unknown) from 2 ecosystems.
29 vulnerabilities can be fixed.
$ osv-scanner scan source --no-resolve .
Total 4 packages affected by 15 known vulnerabilities (1 Critical, 4 High, 10 Medium, 0 Low, 0 Unknown) from 2 ecosystems.
読み取ったのは4パッケージなのに、結果は6パッケージになりました。requestsが依存するidna 2.7.0とurllib3 1.23.0の脆弱性が加わったためです。--no-resolveを付けると、この推移的依存の解決が止まり15件に減ります。公式ドキュメントでは推移的依存の解決をMavenのpom.xml向けとして説明していますが、v2.7.0ではrequirements.txtでも解決が働きました。ただし、解決された推移的依存の版が実環境のインストール済みの版と一致するとは限りません。実環境の評価には、推移的依存まで版を固定したロックファイルや、対象環境で生成したrequirements.txtを使ってください。
出力形式と終了コードの使い分け
--formatで選べる形式は、table(既定)、vertical、json、markdown、html、sarif、gh-annotations、cyclonedx-1-4〜1-7、spdx-2-3です。v2.7.0からJSON出力に各パッケージのpurl(Package URL)が入るようになり、他ツールとの突き合わせがしやすくなりました。--serveを付けるとHTMLレポートをローカルのポート8000で開けます。
| 終了コード | 意味 | 実測した条件 |
|---|---|---|
| 0 | 指摘なし(対象なしを許可した場合も含む) | –allow-no-lockfiles で空ディレクトリ |
| 1 | 脆弱性または違反あり | 上の実行例・ライセンス違反 |
| 127 | 一般エラー | 存在しないロックファイルを -L で指定 |
| 128 | パッケージが1件も見つからない | 空ディレクトリ |
終了コードの定義は公式の出力ドキュメントにあります。CIでは「1は脆弱性で失敗」「128は検査対象の取りこぼし」と分けて扱うのが実務的です。128を無視すると、ロックファイルを置き忘れたまま毎回「問題なし」で通過してしまいます。ロックファイルの無いリポジトリを意図的に通したい場合だけ--allow-no-lockfilesを付けます。複数ツールの終了コードを並べた設計はDockerの脆弱性診断をCI/CDに組み込む手順とツール選定で比較しています。
osv-scanner.tomlによる誤検知と受容リスクの除外
影響を受けない脆弱性や、対応を先送りすると決めた脆弱性は、スキャン対象と同じディレクトリに置いたosv-scanner.tomlで除外します。設定ファイルの仕様上、この設定は子ディレクトリには引き継がれません。全体に同じ設定を当てたい場合は--config=パスで明示します。
[[IgnoredVulns]]
id = "GHSA-xvch-5gv4-984h"
ignoreUntil = 2026-12-31
reason = "minimist はビルド時のみ使用。次回の依存更新で解消予定"
[[PackageOverrides]]
name = "lodash"
ecosystem = "npm"
ignore = true
effectiveUntil = 2026-12-31
reason = "社内フォーク版に置き換え済み"
ignoreUntilを過去の日付にして実行すると、その脆弱性は再び検出され、「has unused ignores」として期限切れの除外が一覧表示されました。除外は期限付きで書き、期限が来たら必ず再判定される運用にできます。期限を書かない除外は、誰も見直さないまま残り続けるため避けるべきです。
scan imageによるコンテナイメージのスキャン
イメージ名とtarアーカイブの指定方法
# ローカルに無ければ docker で pull してから検査
osv-scanner scan image python:3.12-slim-bookworm
# docker save で書き出した tar を検査(Docker 不要)
docker save my-app:latest > my-app.tar
osv-scanner scan image --archive ./my-app.tar
# HTML レポートをブラウザで確認
osv-scanner scan image --serve my-app:latest
イメージ名を渡すとdocker saveで一時ファイルに書き出して解析するため、公式ドキュメントのとおりコンテナ内のコードは実行されません。結果はパッケージごとに「どのレイヤーで入ったか」と「ベースイメージ由来か」が表示されます。python:3.12-slim-bookwormでは、glibcやgnupg2が「# 0 Layer」「debian」と表示され、ベースイメージの更新で直すべきものか、自分のDockerfileで直すべきものかを切り分けられます。Dockerfileの組み立て方はDockerfileとは|書き方・主要命令・ベストプラクティスを解説を参照してください。
同じイメージでTrivyと検出件数がずれる理由
2026年10月6日作成のpython:3.12-slim-bookworm(イメージID sha256:34386ef0cb08…・linux/amd64)を、2026年10月14日にOSV-Scanner v2.7.0とTrivy v0.74.0で検査しました。コマンドはosv-scanner scan image python:3.12-slim-bookwormとtrivy image --scanners vuln --format json python:3.12-slim-bookwormで、Trivyの列はOSパッケージ分だけを数えています(Pythonパッケージは別に6件)。件数は各DBの更新で変わるため、この日の条件での結果として読んでください。
| 項目 | OSV-Scanner v2.7.0 | Trivy v0.74.0 |
|---|---|---|
| 検出件数(集計範囲・単位は異なる) | 24パッケージ・95件(Python含む) | 278行(CVE IDは121種) |
| 集計の単位 | ソースパッケージ単位 | バイナリパッケージ×CVE |
| 重要度の低い脆弱性 | 46件を非表示 | 表示する |
| 修正版の有無 | 修正可能12件と表示 | 未修正・修正見送りも状態つきで表示 |
| alpine:3.18(サポート終了) | No issues found・警告なし | サポート終了の警告を表示 |
差の原因は3つあります。OSV-Scannerはglibcのように複数のバイナリパッケージ(libc-bin・libc6)に分かれるものを1つのソースパッケージとして数えます。重要度が低い(unimportant)と判定された46件は既定で隠し、--all-vulnsを付けたときだけ表に出します。Trivyはバイナリパッケージごとに1行ずつ出し、修正見送り(fix_deferred)や修正予定なし(will_not_fix)の状態も表示します。
件数の多さでツールを選ぶべきではありません。また、サポートが終了したOSのイメージを検査する用途では、OSV-Scannerの「No issues found」を安全の根拠にしないでください。Alpineのリリース表では、3.18のサポート期限は2025年5月9日で、現在の区分は「on request」(依頼があれば対応)です。今回の検出ゼロだけでは、その原因や既知・未知の脆弱性の有無は断定できません。Trivyの設定と出力の詳細はTrivy(トリヴィ)の使い方|インストールからイメージ・IaCスキャン、CI設定まで【v0.74対応】で解説しています。
オフラインスキャンとライセンス検査の設定
オフラインDBのサイズと推移的依存の扱い
# 初回:エコシステム別のDBを取得してからローカルで照合
osv-scanner scan source --offline-vulnerabilities --download-offline-databases -r ./
# 2回目以降:取得済みDBで完全オフライン
osv-scanner scan source --offline -r ./
オフラインモードでは、プロジェクトに含まれるエコシステムの分だけDBを取得します。npmとPyPIの2つで初回の取得は49秒、容量はnpmが約218MB、PyPIが約36MBでした。保存先は環境変数OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORYで変えられます(v2.5.1で再対応)。
--offlineでは推移的依存の解決も止まります。先ほどの例では、オンラインで29件だった結果が15件になりました。閉域網で使う場合は、pip-compileで生成した、推移的依存まで版を固定したrequirements.txtや、uvが生成したuv.lockをスキャナに渡さないと、間接依存の脆弱性を見落とします。--offline-vulnerabilitiesだけを付けた場合は、脆弱性DBの照合だけをローカルで行い、依存の解決には通信を使います。
ライセンス許可リストの書式と落とし穴
# 使われているライセンスの集計だけ表示
osv-scanner scan source --licenses -r ./
# 許可リスト外を違反として表示(= が必須)
osv-scanner scan source --licenses="MIT,Apache-2.0,BSD-3-Clause" -r ./
ライセンス検査の許可リストはSPDX識別子で書きます。=を省いて--licenses Apache-2.0と書くと、v2.7.0では「Apache-2.0」がスキャン対象のパスとして解釈され、「failed to resolve path」で止まりました。許可リストをApache-2.0だけにした検査では、MITのlodashとminimistが違反として表示され、終了コードは1でした。
fixコマンド(ガイド付き修正)の戦略と実行条件
ガイド付き修正は、脆弱性を解消できるバージョンを計算してファイルを書き換える機能です。公式では実験的機能(experimental)の扱いで、対応はnpmとMavenに限られます。実行前に変更をコミットするかバックアップを取り、実行後は差分とテスト結果を確認してください。また、fixはosv-scanner.tomlの除外設定を参照しません。minimistの脆弱性をosv-scanner.tomlで除外してもfixは6件すべてを修正対象にし、--ignore-vulns GHSA-xvch-5gv4-984hを付けたときだけ5件に減りました。
| 戦略 | 対象ファイル | 書き換え方 |
|---|---|---|
| in-place | package-lock.json | 既存の範囲指定の中でロックの版だけ上げる |
| relax | package.json | 範囲指定を緩めて新しい版を許す |
| override | pom.xml | dependencyManagement で版を上書き |
$ osv-scanner fix -M package.json -L package-lock.json --strategy=in-place
Can fix 6/6 matching vulnerabilities by changing 2 dependencies
UPGRADED-PACKAGE: lodash,4.17.20,4.18.1
UPGRADED-PACKAGE: minimist,1.2.5,1.2.8
$ osv-scanner fix -M package.json --strategy=relax
UPGRADED-PACKAGE: lodash,4.17.20,^4.18.1
in-placeは、package.jsonの^4.17.20の範囲内でロックの版を上げるため、範囲指定そのものは変わりません。relaxは、package.jsonで4.17.20と固定していた指定を^4.18.1に書き換えました。注意点として、手で作った最小構成のpackage-lock.jsonでは「No dependency patches are possible」となり、何も直りませんでした。npm install --package-lock-onlyで生成したロックファイルに対して実行してください。
実行前の条件も決めておくべきです。fixは実行時に「信頼できないプロジェクトで実行すると、パッケージマネージャーがスクリプトを実行したり、プロジェクトが指定する外部レジストリへ接続したりする場合がある」と警告を出します。v2.6.0ではファイル操作をos.Rootの中に閉じ込めるパストラバーサル対策が入りましたが、スクリプト実行の危険は残ります。受け取ったばかりの外部リポジトリやフォークのPRには使わず、自社で管理するブランチでだけ実行してください。
GitHub Actionsとpre-commitによる継続スキャン
プルリクエストで新しい脆弱性だけを止める設定
公式のGitHub Actionは、再利用可能ワークフロー(reusable workflow)として呼び出します。PR用のワークフローはマージ先とPRのブランチを両方スキャンし、PRで新しく入った脆弱性だけを報告します。
name: OSV-Scanner PR Scan
on:
pull_request:
branches: [main]
merge_group:
branches: [main]
permissions:
actions: read
security-events: write
contents: read
jobs:
scan-pr:
uses: "google/osv-scanner-action/.github/workflows/[email protected]"
fail-on-vulnの既定値はtrueのため、新しい脆弱性があればチェックは失敗します。ただしマージを止めるには、ブランチ保護で必須のステータスチェックに指定する必要があります。security-events: writeは、結果をSARIFとしてCode scanningへ送るために必要です。Code scanningの画面と料金はGitHub Code Scanningとは?CodeQLの設定手順・対応言語・料金【2026年9月】を参照してください。
定期スキャンとSHA固定
on:
schedule:
- cron: "30 12 * * 1"
push:
branches: [main]
jobs:
scan-scheduled:
permissions:
actions: read
security-events: write
contents: read
uses: "google/osv-scanner-action/.github/workflows/[email protected]"
依存を変えていなくても、新しい脆弱性はあとから登録されます。PRスキャンだけでは既存の依存に後から見つかった脆弱性を拾えないため、週1回の定期スキャンを併用してください(上の例は毎週月曜12時30分UTC)。タグの付け替えによるすり替えを防ぐなら、@v2.7.0の代わりにタグが指すコミットb1d267aea5211fa648d9d442fa64b90dbdcbdd36で固定します。考え方はSHA Pinningとは?GitHub ActionsをコミットSHAで固定する方法【Enforce設定・pinact v5】で解説しています。
pre-commitフックの実行方式と参照イメージ
リポジトリ直下の.pre-commit-hooks.yamlには、Goでビルドして動かすosv-scannerと、コンテナで動かすosv-scanner-dockerの2つのフックが定義されています。前者はscan source --recursiveをカレントディレクトリに実行します。後者が参照するイメージのタグはv2.3.3のまま更新されていないため、使う場合はフック定義のentryでイメージの版を確認してください。argsはスキャナへの引数で、イメージの版を指定する項目ではありません。
OSV-ScannerとTrivyの使い分け
| 比較軸 | OSV-Scanner v2.7.0 | Trivy v0.74.0 |
|---|---|---|
| 依存パッケージの脆弱性 | ○(推移的依存を解決) | ○ |
| OSパッケージ | ○(重要度低は既定で非表示) | ○(検出分を状態つきで表示) |
| 修正バージョンの書き換え | ○(npm・Maven) | × |
| Goの呼び出し解析 | ○(既定で有効) | × |
| IaC設定ミス・シークレット | × | ○ |
| サポート終了OSの警告 | × | ○ |
Goの呼び出し解析はgovulncheckとは|Goの脆弱性を到達可能性で検出する仕組みと使い方と同じライブラリを使い、脆弱な関数を実際には呼んでいない検出(uncalled)を既定で表から外します。--all-vulnsを付けると表示されます。Rustの呼び出し解析は実験的で、依存のbuild.rsを実行するため、信頼できないコードには使えません。
判断の基準は次のとおりです。アプリ開発のリポジトリで依存の脆弱性を減らしていく目的ならOSV-Scannerを主に使い、PRごとの差分スキャンとfixを回します。本番イメージの出荷判定や、Terraform・Kubernetesマニフェストの設定ミスまで1本で見たいならTrivyを主にします。両方を入れる場合は、件数を突き合わせて差を問題視するのではなく、OSV-Scannerはリポジトリ、Trivyはイメージと役割を分けるのが運用しやすい形です。ソフトウェア構成分析(SCA)全体の中での位置づけはSCAとは?ソフトウェア構成分析の仕組みとSAST・SBOMとの違い・導入判断を解説で整理しています。
受託開発の納品物では、発注側が検収条件に「OSV-Scannerの終了コード0、または除外理由と期限を書いたosv-scanner.toml」を入れておくと、納品時点の依存の状態を第三者が再現できる形で確認できます。スキャン結果の読み解きや、ツールで拾えない設計上の弱点まで確認したい場合は、脆弱性診断・セキュリティ診断の対象として扱うのが確実です。
v2.4.0から変わった点(v2.5.0〜v2.7.0)
| 版 | 公開日 | 主な変更 |
|---|---|---|
| v2.5.0 | 2026-08-07 | 検出処理をOSV-SCALIBRへ全面移行・VS Code拡張(.vsix)対応 |
| v2.5.1 | 2026-08-17 | オフライン照合の不具合修正・DB保存先の環境変数を再対応 |
| v2.6.0 | 2026-09-14 | Actionのarm64イメージ・JSロックファイルのGit依存の照合 |
| v2.7.0 | 2026-10-12 | JSONにpurl・CycloneDXに深刻度・–x- 短縮フラグ |
運用に影響が大きいのはv2.7.0のフラグ名の変更です。--experimental-pluginsなどの--experimental-系フラグは非推奨になり、--x-pluginsのような短い名前が追加されました。旧名も動きますが、ヘルプには「DEPRECATED」と表示されるため、CIの設定は今のうちに書き換えておくと安全です。v2.5.0では、RHEL系(AlmaLinux・Rocky Linux)のRPMでエポック付きの版を修正済みでも未修正と誤検出する不具合が直っています。v2.4.0以前で誤検出を除外設定に入れていた場合は、更新後に除外を外して再判定してください。各版の詳細はGitHub Releasesで確認できます。
よくある質問
OSV-Scannerは無料で商用利用できますか?
できます。Apache 2.0ライセンスのオープンソースで、利用料、アカウント登録、スキャン回数の制限はありません。照合先のOSV.dev APIも無料で使えます。社内で改変して配布する場合は、Apache 2.0の条件(ライセンス文と変更の告知)に従います。
OSV.devとは何ですか?
Googleが運営する、オープンソースの脆弱性情報を共通のOSV形式で集めたデータベースです。GitHub Advisory DatabaseやPyPI、Go、RustSecなどの情報源から取り込み、どのパッケージのどの版が影響を受けるかを機械で判定できる形で公開しています。OSV-Scannerはこのデータベースの公式フロントエンドにあたります。
WindowsでOSV-Scannerを使えますか?
使えます。winget install Google.OSVScannerまたはscoop install osv-scannerで導入でき、GitHub Releasesにはamd64とarm64の.exeも置かれています。Windows上でコンテナイメージを名前で検査するには、Docker(docker コマンド)が必要です。
Dockerイメージだけで実行できますか?
できます。ghcr.io/google/osv-scanner:v2.7.0にソースディレクトリをマウントしてscan sourceを実行します。終了コード128になった場合は、コンテナ内からマウント先のファイルが見えているかを先に確認してください。macOSのDocker Desktopで/tmp配下をマウントした検証では0件・終了コード128になり、ホーム配下をマウントし直すと検出できました。
OSV-ScannerとTrivyはどちらを選ぶべきですか?
リポジトリの依存の脆弱性を減らしたいならOSV-Scanner、コンテナイメージの出荷判定やIaC・シークレットの検査まで1本で行いたいならTrivyです。同じイメージでも件数の数え方が違うため、両方を使う場合は対象を分けて運用します。