osv-scanner の実務Q&A — pnpm対応・CVEの除外・オフライン実行・つまずくエラー
導入後に出る質問だけ。pnpm-lock.yaml対応、特定CVEの除外(osv-scanner.toml)、オフライン3フラグの違いとエラーの原因、終了コード(0/1/127/128)のCIでの扱い、v1からv2でのフラグ名の変更を、公式仕様にもとづいて整理します。
- 公開日
- 更新日
- 最終確認
対象:osv-scanner を導入したあとで、実際に回して詰まった人。導入手順ではなく、その次に出てくる質問だけを集めています。仕様は公式ドキュメントに準拠しています。
ロックファイルの指定
# 単一のロックファイルを指定
osv-scanner scan source -L pnpm-lock.yaml
# ディレクトリを再帰的に走査
osv-scanner scan source -r ./my-project/ファイル名から形式が推測できない場合は、形式:パス の形で「どのパーサで読むか」を明示できます。
# 変わった名前のファイルを requirements.txt として解釈させる
osv-scanner scan source -L 'requirements.txt:/path/to/extra-requirements.txt'
# パスにコロンが含まれる場合は、先頭を「:」だけにして推測に任せる
osv-scanner scan source -L ':/path/to/my:projects/package-lock.json'ロックファイルを指定する意味
マニフェスト(package.json など)は「欲しいバージョンの範囲」しか書いておらず、実際に入る版が確定しません。ロックファイルには解決済みの正確なバージョンが並ぶので、スキャン結果が「実際に入っているもの」と一致します。範囲だけを見て「たぶん大丈夫」と判定するより、はるかに信用できます。
自分の環境で確認する方法
結果がおかしいと感じたら、フラグを足す前に次の5つを順に確かめます。どれも自分のプロジェクトを点検するだけの操作です。
バージョンを確かめる
osv-scanner --version を実行します。2025年に出たv2でコマンドの形とフラグ名が変わっているため、v1向けの古い記事の書き方はそのままでは動きません。公式ドキュメントも現在はv2向けです。何を読んだかを確かめる
--format json --all-packages で出せます(--all-packages はJSON出力のときだけ使えます)。終了コードを確かめる
echo $?、PowerShell なら $LASTEXITCODE で確認します。意味は下の表のとおりです。設定ファイルが効いているかを確かめる
osv-scanner.toml は、置いたディレクトリのファイルにだけ効き、子ディレクトリには引き継がれません。モノレポで -r を使うなら、ロックファイルのある各ディレクトリに置くか、--config で1つを全体に適用します。オフラインDBの場所と日付を確かめる
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY があればそこ、無ければユーザーのキャッシュディレクトリ、次に一時ディレクトリに置かれます。中は osv-scanner/<エコシステム>/all.zip の形です。ファイルの更新日時が古ければ、判定も古いデータで行われています。終了コードの意味とCIでの扱い
| 終了コード | 公式の意味 | CIでの扱い |
|---|---|---|
0 | パッケージは見つかったが、該当する脆弱性なし | 成功 |
1 | パッケージが見つかり、脆弱性あり | 失敗(検出。中身を見て直すか、期限付きで除外) |
127 | 一般的なエラー | 失敗(スキャン自体の故障として調べる) |
128 | パッケージが1つも見つからない(読めるファイルが無かった) | 失敗(パスやロックファイルの有無を直す) |
公式は、1〜126を脆弱性の結果に関するもの、129〜255を結果と関係ないエラー用に予約しています。CIでは「0だけ成功、1は検出、それ以外はスキャンの故障」と3通りに分けて扱うと、原因の切り分けが速くなります。
特定のCVEを除外する
スキャンするディレクトリに osv-scanner.toml を置きます。
[[IgnoredVulns]]
id = "GHSA-xxxx-xxxx-xxxx"
ignoreUntil = 2026-12-31
reason = "該当機能を使っておらず、到達経路がないため。12月の棚卸しで再評価する"id(必須):脆弱性の識別子ignoreUntil(任意):この日付を過ぎると再び報告されますreason(任意):理由。将来の自分と他人のために必ず書く
--config=/path/to/config.toml を渡すと、走査対象の各ディレクトリに置いた設定より優先して全体に適用されます。なお、別名(エイリアス)として扱われている脆弱性もまとめて無視されます。
除外は恒久的に消さず、期限を付けて見送る
ignoreUntil を書かない除外は、永久に消えたまま誰も気づかない項目になります。当サイトの運用ルールは「除外には必ず期限と理由を書く」です。期限が来れば再び報告に出てくるので、判断を先送りしたことを忘れられません。CI を黙らせるためだけの無期限除外は、半年後に必ず事故のもとになります。
オフライン実行:3つのフラグの関係
ここが最も誤解されます。役割が違う3つがあります。
| フラグ | 何をするか |
|---|---|
--offline | 完全オフライン。事前に取得したローカルDBだけで判定し、DBの更新もせず、プロジェクトや依存の情報をどこにも送りません |
--offline-vulnerabilities | 脆弱性判定だけをローカルDBで行う。推移的依存の解決など他の処理はネットワークを使い得ます |
--download-offline-databases | ローカルDBの取得・更新を許可する。offline 系フラグと併用したときだけ有効 |
--offline
完全オフライン。DBの更新もせず、外へ何も送らない
--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 系フラグと併用が必要- ローカルDBが無い状態で
--offline→ 「DBが無い」というエラーになる。初回は取得が必要 no package sources found→ そのパスに認識できるロックファイル/マニフェストが無い。-Lで明示するか-rで再帰する
迷わないための指針
- 完全な閉域で回す →
--offline(+初回だけ--download-offline-databases) - CIで外に出したくないのは脆弱性判定だけ →
--offline-vulnerabilities - DBの置き場所を固定したい → 環境変数
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 <イメージ名> |
--json | --format=json |
--skip-git | --include-git-root(意味が逆になった) |
なお、osv-scanner <ディレクトリ> は osv-scanner scan source <ディレクトリ> の省略形として動きます。迷ったら scan source を明記しておくと、読む人にも意図が伝わります。
よくある間違いと直し方
よくある間違い
- CIで「終了コードが1以外なら成功」と判定している
- 失敗を嫌って
|| trueを付けている - リポジトリのルートに
osv-scanner.tomlを1つ置けば、サブディレクトリにも効くと思っている package.jsonだけをコミットして、ロックファイルを置いていない--offlineでもpom.xmlの推移的依存まで見ていると思っているignoreUntilを書かずに除外している
直し方
- 成功は0だけ。128(何も読めなかった)が緑になるのを防ぐ
- 外すなら除外設定で個別に、期限付きで外す
- 各ディレクトリに置くか、
--configで全体に適用する - ロックファイルをコミットする(JavaScriptは
package-lock.json・pnpm-lock.yaml・yarn.lock・bun.lockが対象) - 推移的依存の解決はオフラインでは無効。ネットワークが使えるなら
--offline-vulnerabilitiesを選ぶ - 期限と理由を必ず書き、期限が来たら判断し直す
pom.xml(Maven)は、ロックファイルのように解決済みの全バージョンを持っていません。そのため 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 に当てはめると、確かめるべきことは2つです。
- 推移的依存まで含めて読めているか。ロックファイルがあるエコシステムならロックファイルを、Maven なら推移的依存の解決が有効な状態(オフライン・
--no-resolveではない)で走らせる - 読んだパッケージの一覧に、間接的に入っているライブラリが本当に含まれているか。
--format json --all-packagesで一覧を出して確かめる
既知の脆弱性が公表された日に「該当するか」を数分で答えられるかどうかは、普段から一覧を正しく取れているかで決まります。
他ツールとの使い分け
| ツール | 参照するデータ | 得意なこと |
|---|---|---|
| osv-scanner | OSV.dev | 複数エコシステム横断。ロックファイル単位、オフライン実行可 |
npm audit / pnpm audit | npm アドバイザリDB | npm系で手軽。追加インストール不要 |
| Dependabot | GitHub Advisory | 更新PRを自動で作る(検出だけでなく修正提案) |
| Trivy | 複数(OSV含む) | コンテナイメージやOSパッケージまで含めた広い走査 |
どれか1つに絞るのが誤りです。データベースが違えば拾うものも違います。当サイトは pnpm audit を日次の自動チェックに、osv-scanner をロックファイル横断の確認に使い分けています。
当サイトの視点:固定したバージョンが古くなってもスキャナは知らせない
実運用で分かった注意点があります。推移的依存を overrides で特定バージョンに固定すると、以後そのパッケージはパッチ更新を受け取りません。そしてスキャナが警告するのは「既知の脆弱性がある」ときだけなので、固定したバージョンが古くなっただけの状態は、誰も教えてくれません。当サイトはこれで3回続けて問題を起こし、最終的に「各ピンが同じメジャー内の最新パッチかどうか」を毎日照合する自前チェックを監査スクリプトに足しました。スキャナは今ある既知の脆弱性を見る道具で、将来脆弱性が残りやすい状態は別の仕組みで見る必要がある。これが当サイトの結論です。
出典
- OSV-Scanner 公式ドキュメント — Usage / Offline Mode / Configuration
- Supported Artifacts and Manifests(対応ロックファイル一覧)
- OSV-Scanner 公式ドキュメント — Output(出力形式と終了コード) / Migration Guide(v1からv2への変更)
- CISA — Apache Log4j Vulnerability Guidance
次に読む
- 導入:osv-scannerの導入と使い方(インストールからCI組込みまで)
- 優先度:CVEの直す順番はどう決めるか(CVSS・EPSS・KEVの使い分け)
- 事故:Log4Shell(推移的依存で「使っているか」が分からなかった事例)
- 実務:脆弱性対応の実務 / npmサプライチェーンワームへの防御
よくある質問
Qosv-scanner は pnpm-lock.yaml に対応していますか?
対応しています。npm/yarn/pnpm のロックファイルはいずれも対象で、`osv-scanner scan source -L pnpm-lock.yaml` のように直接指定できます。ディレクトリを再帰的に走査する場合は `-r` を使います。
Q特定のCVEだけ除外したいときは?
スキャン対象のディレクトリに `osv-scanner.toml` を置き、`[[IgnoredVulns]]` に `id` を書きます。`ignoreUntil`(期限)と `reason`(理由)も書けるので、恒久的に消すのではなく『いつまで・なぜ見送るか』を残せます。`--config=/path/to/config.toml` を渡すと、対象ディレクトリ内の設定より優先して全体に適用されます。
Q『databases can only be downloaded when running in offline mode』が出ます
`--download-offline-databases` は、offline 系のフラグと一緒に指定したときだけ有効になるためです。単体では使えません。ローカルDBを取得しながら実行したい場合は offline フラグと併用してください。
Qnpm audit や Dependabot があれば osv-scanner は不要ですか?
見ているデータベースと守備範囲が違います。npm audit は npm のアドバイザリDBで npm エコシステム限定、Dependabot は主に GitHub 上での更新提案、osv-scanner は OSV.dev を参照して複数エコシステム(npm/PyPI/Go/Maven など)を横断し、ロックファイル単位でオフライン実行もできます。片方が拾って片方が拾わない、は普通に起きるので、どれか1つに絞るのではなく役割で使い分けます。
QCIでは osv-scanner の終了コードをどう扱えばいいですか?
公式の定義では、0=パッケージは見つかったが該当する脆弱性なし、1=脆弱性あり、127=一般的なエラー、128=パッケージが1つも見つからない、です。成功として扱うのは0だけにしてください。『1以外なら成功』と書くと、ロックファイルを読めなかった128がそのまま緑になり、何も検査していないのに通ってしまいます。
Q古い記事の --experimental-offline や --docker が動きません
v2で名前が変わったか、別のコマンドに移ったためです。公式の移行ガイドでは、--experimental-offline は --offline、--experimental-download-offline-databases は --download-offline-databases に、コンテナの走査は --docker から `osv-scanner scan image` に、JSON出力は --json から `--format=json` に変わっています。`osv-scanner --version` でまず自分のバージョンを確認してください。