osv-scanner-mcp
v0.2.0
Published
MCP server that wraps Google's OSV-Scanner to scan Java projects for known vulnerabilities
Maintainers
Readme
OSV-Scanner-MCP
Google製 OSV-Scanner をラップするMCPサーバーです。Claude等のMCPクライアントから「このJavaプロジェクトの脆弱性をチェックして」と自然言語で依頼するだけで、依存ライブラリの既知の脆弱性(CVE / GHSA)を深刻度順のレポートで取得できます。
ステータス: npmで公開中(
npx -y osv-scanner-mcp)。Maven(pom.xml)と Gradle(gradle.lockfile / lockfile方式)に対応しています。MCPクライアントは Claude Code / Claude Desktop / Codex CLI / Antigravity / VS Code(GitHub Copilot)での利用手順を用意しています。
特徴
- ワンショットスキャン:
scan_java_projectツールにプロジェクトパスを渡すだけで、検出→スキャン→整形済みレポートまで一気に返します - JAR/WAR実体スキャン:
scan_java_artifactツールで、lockfileが無い・shaded/fat JARしか手元にないプロジェクトでもアーカイブ内メタデータから既知の脆弱性を検出します(ベストエフォート同定であることを明示するcoverage情報付き) - 深刻度順のレポート: パッケージごとに脆弱性をCVSSスコア順に整理し、5段階の深刻度ラベル(critical / high / medium / low / unknown)とサマリ集計付きで返します
- 修正版の提示: 各脆弱性の
fixed_versionsをMavenバージョン優先順位規則で正しくソートして含めます(2.17.1-RELEASEのようなsemver非対応の表記にも対応) - セキュリティ第一の設計: シェル非経由の実行・引数ホワイトリスト・パス正規化と境界チェック・タイムアウト/出力サイズ上限を実装段階から組み込んでいます
動作要件
- Node.js >= 20.19
- OSV-Scanner バイナリ — 手動インストールは不要です。見つからない場合、公式GitHub Releasesからピン留めバージョンを自動ダウンロードし、パッケージに埋め込まれたSHA256チェックサムで検証してから使用します(
~/.cache/osv-scanner-mcp/にキャッシュ)- 手動インストール済みのバイナリ(PATH上または
OSV_SCANNER_PATH指定)があればそちらを優先します - 自動ダウンロードを無効化する場合は
OSV_MCP_AUTO_DOWNLOAD=0 - PATH上のバイナリを使わず常に検証済み自動ダウンロードを使う場合は
OSV_MCP_PREFER_DOWNLOAD=1(運用環境向け)
- 手動インストール済みのバイナリ(PATH上または
- スキャン時(OSV-Scanner経由)および
explain_vulnerability実行時にapi.osv.devへのネットワークアクセスが発生します(照会先はOSVデータベースのみ)
セットアップ
Claude Code への登録
claude mcp add osv-scanner -- npx -y osv-scanner-mcpClaude Desktop への登録
claude_desktop_config.json に追加:
{
"mcpServers": {
"osv-scanner": {
"command": "npx",
"args": ["-y", "osv-scanner-mcp"]
}
}
}Codex CLI への登録
codex mcp add osv-scanner -- npx -y osv-scanner-mcpまたは ~/.codex/config.toml に追加:
[mcp_servers.osv-scanner]
command = "npx"
args = ["-y", "osv-scanner-mcp"]
startup_timeout_sec = 60 # 初回のnpxパッケージ取得に備えて延長
tool_timeout_sec = 300 # 既定60秒。バイナリ自動ダウンロード+スキャン(既定120秒)を見込んで延長注意: CodexのMCPツール実行タイムアウトは既定60秒です。本サーバーはスキャンのタイムアウトが既定120秒のため、初回のOSV-Scanner自動ダウンロードや大きめのプロジェクトのスキャンでは既定値のままだとCodex側が先にタイムアウトします。上記のように
tool_timeout_secの延長を推奨します。
Antigravity への登録
エージェントパネルの MCP Servers → Manage MCP Servers → View raw config で開く mcp_config.json に追加(Claude Desktopと同じ形式):
{
"mcpServers": {
"osv-scanner": {
"command": "npx",
"args": ["-y", "osv-scanner-mcp"]
}
}
}VS Code(GitHub Copilot)への登録
code --add-mcp '{"name":"osv-scanner","command":"npx","args":["-y","osv-scanner-mcp"]}'またはワークスペースの .vscode/mcp.json に追加(コマンドパレットの MCP: Add Server からも設定可能):
{
"servers": {
"osv-scanner": {
"type": "stdio",
"command": "npx",
"args": ["-y", "osv-scanner-mcp"]
}
}
}ソースから使う場合
git clone https://github.com/tedorigawa001/OSV-Scanner-MCP.git
cd OSV-Scanner-MCP
npm install
npm run build
# 登録時は `npx -y osv-scanner-mcp` の代わりに `node /path/to/OSV-Scanner-MCP/dist/index.js` を指定環境変数
| 変数 | 説明 |
|---|---|
| OSV_SCANNER_PATH | 使用するosv-scannerバイナリの明示指定。省略時はPATH→自動ダウンロードの順で解決。指定が無効な場合はフォールバックせずエラーになります(意図しないバイナリの実行防止) |
| OSV_MCP_ALLOWED_ROOT | 指定時、このディレクトリ配下以外のスキャンを拒否します(パストラバーサル対策の境界)。設定を推奨 |
| OSV_MCP_REQUIRE_ALLOWED_ROOT | 1 または true 指定時、OSV_MCP_ALLOWED_ROOT が未設定ならサーバーの起動自体を拒否します(運用環境向けのfail-closedモード) |
| OSV_MCP_MAX_CONCURRENT_SCANS | 同時実行できるスキャン数の上限(デフォルト 2、最大 16)。超過したリクエストは待たずに即時エラーになります |
| OSV_MCP_AUTO_DOWNLOAD | 0 または false でバイナリの自動ダウンロードを無効化(デフォルト有効) |
| OSV_MCP_PREFER_DOWNLOAD | 1 または true 指定時、PATH上のosv-scannerを使わず、チェックサム検証済みの自動ダウンロードバイナリを常に使用します(PATH汚染による偽バイナリ実行の防止。OSV_SCANNER_PATH の明示指定は引き続き最優先) |
推奨:
OSV_MCP_ALLOWED_ROOTは未設定でも動作しますが、その場合は任意の絶対パスをスキャンできてしまいます。悪意ある指示(プロンプトインジェクション)経由で意図しないディレクトリをスキャンさせられる経路を塞ぐため、プロジェクト置き場のルート(例:~/projects)を設定しておくことを推奨します。各クライアントの設定で"env": {"OSV_MCP_ALLOWED_ROOT": "/Users/you/projects"}のように渡せます(Codex CLIのTOMLでは[mcp_servers.osv-scanner.env]セクション)。
本番運用の推奨構成: 共有サーバーやCI等の運用環境では、次の3つをセットで設定してください。
OSV_MCP_ALLOWED_ROOT=/スキャン対象のルート— スキャン範囲の境界を固定OSV_MCP_REQUIRE_ALLOWED_ROOT=1— 境界未設定なら起動を拒否(fail-closed)OSV_SCANNER_PATH=/管理者所有の絶対パスまたはOSV_MCP_PREFER_DOWNLOAD=1— PATH解決に依存せず、実行するバイナリを固定
提供ツール
scan_java_project
Java(Maven)プロジェクトをスキャンし、既知の脆弱性レポートを返します。
入力
| パラメータ | 型 | 説明 |
|---|---|---|
| project_path | string | スキャン対象のプロジェクトディレクトリ、または pom.xml / gradle.lockfile の絶対パス |
Gradleプロジェクトについて: 本ツールはlockfile方式のみ対応です(ビルド実行方式は build.gradle の任意コード実行を伴うため、セキュリティ上の理由から採用していません)。
gradle.lockfileが無い場合は./gradlew dependencies --write-locksで生成してください(依存ロック未設定の場合はbuild.gradleにdependencyLocking { lockAllConfigurations() }の追加が必要です)。
出力(成功時)
{
"project_dir": "/path/to/project",
"manifests": ["pom.xml"],
"source_files": ["/path/to/project/pom.xml"],
"vulnerable_package_count": 4,
"vulnerability_count": 14,
"severity_breakdown": { "critical": 3, "high": 3, "medium": 7, "low": 0, "unknown": 1 },
"packages": [
{
"name": "org.apache.logging.log4j:log4j-core",
"version": "2.14.1",
"ecosystem": "Maven",
"vulnerabilities": [
{
"id": "GHSA-jfh8-c2jp-5v3q",
"cve": "CVE-2021-44228",
"aliases": ["CVE-2021-44228"],
"severity_score": 10,
"severity": "critical",
"summary": "Remote code injection in Log4j",
"fixed_versions": ["2.3.1", "2.12.2", "2.15.0"]
}
]
}
]
}packagesは最も深刻な脆弱性を持つ順、各vulnerabilitiesは深刻度順(unknownは末尾)fixed_versionsはMaven優先順位で昇順。複数のリリース系統(例: 2.12系バックポートと2.15系)が混在することがあります。空配列は「修正版が存在しない」ことを意味しますseverity_scoreが取得できない脆弱性はnull/"unknown"として扱います
scan_java_artifact
JAR/WARファイルの実体をスキャンします。既存のマニフェスト方式とは別ツールです。
{ "artifact_path": "/absolute/path/to/application.war" }artifact_path はJAR/WARファイル、または探索するディレクトリの絶対パスです。
ディレクトリ指定では target や build も探索します。.git、node_modules、.idea、.vscode と探索中のシンボリックリンクは除外します。
探索上限は深さ8・100ファイル・10,000エントリです。上限に達して探索を完了できない場合は、結果を黙って省略せず artifact_search_limit_exceeded を返します。対象を絞って再実行してください。
OSV_MCP_ALLOWED_ROOT による制限も適用されます。
OSV-Scanner 2.4.0の java/archive プラグインを使用し、ネストJARもスキャナー側で解析します。Javaコードやビルドは実行しません。
識別にはアーカイブ内メタデータを用いるため、除去済みメタデータやshaded/minimized JAR内の依存を見落とす場合があります。
出力の読み方:
- 先頭の
coverageにjars_found、jars_identified、unidentified_jarsを返します。件数はWARも含む、ファイルシステム上で列挙した外側のアーカイブ単位です。ネストJARの総数ではありません。 artifacts[].statusはidentified_with_vulnerabilities/identified_without_known_vulnerabilities/unidentifiedの3値です。「同定済み」は少なくとも1件のMaven座標を取得できた意味であり、全依存の同定ではありません。coverage.completenessは常にincomplete。identified_vulnerability_count: 0は安全性の保証ではありません。packagesは同定できた脆弱なパッケージの詳細です。複数アーカイブに含まれる同一パッケージ・脆弱性は全体集計では重複排除します。- JAR/WARが無い場合は
no_scannable_artifacts、全件同定不能の場合は警告を含む成功レポートです。
suggest_fix は引き続きマニフェスト方式専用です。experimentalプラグインを使うため、OSV-Scannerのピン留めバージョン更新時には、フラグとJAR/WARの出力形式も再検証してください。
信頼できないアーカイブの展開はOSV-Scannerのネイティブ処理に依存します。タイムアウト・出力上限はありますが、OSレベルのメモリ制限やサンドボックスを提供するものではありません。
suggest_fix
スキャンを実行し、脆弱なパッケージごとに推奨アップグレードバージョンを提案します。単純な最大バージョンではなく、現在のバージョンに最も近いリリース系統の修正版を3段階フォールバックで選定します:
| Tier | 意味 |
|---|---|
| same_minor | 現在と同じ major.minor 系統内の修正版(最小の変更で済む) |
| major_internal | 同一メジャー内の修正版(マイナーバージョンアップが必要) |
| cross_major | メジャーアップグレードが必要(破壊的変更の可能性あり) |
入力: scan_java_project と同じ(project_path)
出力(成功時)
{
"project_dir": "/path/to/project",
"manifests": ["pom.xml"],
"vulnerable_package_count": 4,
"unfixed_vulnerability_count": 1,
"suggestions": [
{
"package": "org.apache.logging.log4j:log4j-core",
"current_version": "2.14.1",
"ecosystem": "Maven",
"recommended_upgrade": "2.25.4",
"upgrade_tier": "major_internal",
"verification": "verified",
"upgrade_note": "取得済みの影響範囲に基づき修正対象CVEの範囲外と確認した候補です",
"per_cve_detail": [
{ "id": "GHSA-jfh8-c2jp-5v3q", "cve": "CVE-2021-44228", "severity": "critical", "fixed_in": "2.15.0", "tier": "major_internal" }
]
}
]
}recommended_upgradeは既知の修正版を候補に、修正対象の全CVEの影響範囲外と確認できたものを3段階Tier順・バージョン昇順で選びます。CVEごとの修正版の最大値を単純に採用せず、別系統で再び影響を受ける候補も除外します。全公開版の中での最小性や未検出の脆弱性がないことは保証しません。- MavenのOSV
ECOSYSTEM範囲(introduced/fixed/last_affected/ 上限なし)を照合します。versionsに明示された影響も確認します。範囲欠落・不正・未対応形式・limitによる不完全な情報では安全と推定せず、候補を検証できなければrecommended_upgrade: null、verification: "no_verified_candidate"を返します。 - 推奨時は
verification: "verified"、CVEごとのrecommended_statusはaffected/not_affected/unknownです。推奨保留時はnot_evaluatedになります。per_cve_detail.fixed_inは各CVE単独の候補であり、最終推奨先の判定はrecommended_statusを参照してください。 - 現在より新しい修正版候補がないCVEは
tier: "unfixed"として推奨の修正対象から除外します(情報欠落を含む場合があります)。除外したCVEも推奨先で判定し、その状態を表示します。全CVEがunfixedの場合もrecommended_upgradeはnullです。
explain_vulnerability
指定したGHSA-ID / CVE-IDの脆弱性の詳細をOSVデータベースAPI(api.osv.dev)から直接取得して返します(スキャンは実行しません)。スキャン結果の id をそのまま渡せます。クライアントLLMの知識カットオフ以降に公開された脆弱性の説明に特に有効です。
入力
| パラメータ | 型 | 説明 |
|---|---|---|
| vulnerability_id | string | 脆弱性のID(例: GHSA-jfh8-c2jp-5v3q、CVE-2021-44228) |
出力(成功時): id / aliases / summary / details(説明markdown、4,000字上限)/ severity(CVSSベクトル)/ published / modified / affected(影響パッケージとバージョン範囲)/ references(アドバイザリ・修正コミット等のURL、http/httpsのみ・20件上限)
注意: OSVの正規IDはGHSA等のため、CVE-IDでは見つからない場合があります(その場合はエラーメッセージでGHSA-IDでの照会を案内します)。
出力(エラー時) — 全ツール共通
isError: true とともに、機械判読可能な kind を含むJSONを返します:
{
"error": {
"kind": "no_manifest_found",
"message": "対応マニフェスト(pom.xml / gradle.lockfile)が見つかりません(深さ3まで探索): /path/to/project"
}
}| kind | 意味 |
|---|---|
| binary_not_found | OSV-Scannerが見つからない(インストール案内をmessageに含む) |
| project_not_found | 指定パスが存在しない・ディレクトリ/pom.xmlでない |
| no_manifest_found | 対応マニフェスト(pom.xml / gradle.lockfile)が見つからない |
| binary_download_failed | バイナリのダウンロード失敗(未対応プラットフォーム含む) |
| binary_checksum_mismatch | ダウンロードしたバイナリのチェックサム不一致(改ざん/破損の可能性) |
| gradle_lockfile_missing | Gradleプロジェクトだがgradle.lockfileが無い(生成手順をmessageで案内) |
| path_outside_allowed_root | OSV_MCP_ALLOWED_ROOT の外を指している |
| no_packages_found | スキャン対象パッケージなし(依存関係が未定義のpom.xml等) |
| scan_failed | OSV-Scannerが異常終了(stderr抜粋をdetailに含む) |
| scan_timeout | タイムアウト(デフォルト120秒) |
| too_many_concurrent_scans | 同時実行スキャン数が上限(デフォルト2)に達している。完了を待って再試行 |
| output_too_large | 出力がサイズ上限(デフォルト32MB)を超過 |
| invalid_output | 出力がJSONとして解釈できない |
| invalid_vulnerability_id | 脆弱性IDの形式が不正 |
| vulnerability_not_found | 指定IDの脆弱性がOSVデータベースに存在しない |
| api_request_failed | OSV APIへのリクエスト失敗(ネットワーク・タイムアウト・非2xx) |
| internal_error | 想定外のエラー(内部情報は返しません) |
セキュリティ設計
脆弱性診断ツール自体が攻撃経路にならないよう、以下を実装しています。
- サプライチェーン対策: バイナリの自動ダウンロードは公式GitHub Releasesに限定し、バージョンをピン留め。パッケージに埋め込まれたSHA256チェックサムで検証します(配布元のSHA256SUMSファイルは信用しないため、リリース側が改ざんされても検出可能)。検証合格まで実行権限を与えず、キャッシュ済みバイナリも使用のたびに再検証します。
OSV_MCP_PREFER_DOWNLOAD=1でPATH上の未検証バイナリを使わない運用も選べます - コマンドインジェクション対策: シェルを経由しない
spawn+ 引数配列で実行。OSV-Scannerへの引数は固定リストのみで、可変部は検証済み絶対パス1つだけ - パストラバーサル対策: 入力パスは
realpathでシンボリックリンク解決後に境界チェック。pom.xml探索ではシンボリックリンクを辿りません - DoS対策: タイムアウト・stdout上限・stderr抜粋上限を設定。スキャン結果は防御的にパースし、形式不正でも例外を投げません。同時実行スキャン数も上限(デフォルト2)を設け、並列リクエストによるプロセスの無制限起動を防ぎます
- fail-closedな運用モード:
OSV_MCP_REQUIRE_ALLOWED_ROOT=1で、スキャン許可ルート未設定時にサーバーの起動自体を拒否できます - 情報漏えい対策: 想定外の例外はスタックトレース等を含めず
internal_errorに丸めます。外部由来のテキスト(脆弱性summary等)は長さ上限付きの「データ」として構造化して返します - プロンプトインジェクション対策: OSVデータベース由来のテキスト(summary / details / ID等)とOSV-Scannerのstderrは、LLMクライアントへ返す前にサニタイズします。制御文字(ANSIエスケープ含む)・ゼロ幅文字・双方向制御文字(RLO等)・Unicodeタグ文字(不可視のテキスト密輸)・行区切り(U+2028/2029)を除去し、NFC正規化を適用。外部データの読み取りアクセサを単一のサニタイズ境界にすることで適用漏れを防いでいます
開発
npm test # テスト実行(vitest)
npx vitest run --coverage # カバレッジ計測
npm run typecheck # 型チェック
npm run build # dist/ へビルド設計メモ・残課題は docs/DESIGN_TODO.md を参照してください。
ロードマップ
- [x]
suggest_fixツール: 現在のバージョンに最も近い系統の修正版を提案(3段階Tierフォールバック) - [x]
explain_vulnerabilityツール: 脆弱性の詳細説明(OSV API経由) - [x] npmパッケージ化(
npx osv-scanner-mcp) - [x] OSV-Scannerバイナリの自動ダウンロード(チェックサム検証付き)
- [x] Gradle対応(lockfile方式)
- [x]
scan_java_artifactツール: JAR/WAR実体スキャン(lockfileが無い・shaded/fat JARのみのプロジェクト向け)
