Google製OSV-ScannerをMCPサーバーとして使える「OSV-Scanner-MCP」の v0.16.0 をnpmで公開しました。
これまでJava・JavaScript・Python・Go・Rustの依存をまとめてスキャンできました。
今回は .NET(NuGet) を加えました。
使い方は変わりません
scan_projectにプロジェクトのディレクトリを渡すと、.NETのファイルも他の言語のlockfileと一緒に検出してスキャンします。
{
"project_path": "/absolute/path/to/solution"
}
| 対応ファイル | 内容 |
|---|---|
packages.lock.json(packages.<プロジェクト名>.lock.json) |
RestorePackagesWithLockFileで生成するlockfile。直接・推移的依存の確定版 |
obj/project.assets.json |
dotnet restoreの生成物。lockfileを有効にしていないプロジェクトでも、復元済みならある |
packages.config(packages.<プロジェクト名>.config) |
.NET Frameworkの形式 |
*.deps.json |
ビルド・配置の出力。自己完結型アプリのランタイムパックも含む |
*.csproj・*.fsproj・*.vbproj
|
上のどれも無いプロジェクトの、直接のPackageReferenceだけ |
これまでと同じく、dotnet restoreもMSBuildも実行しません。
ファイルを読むだけで、プロジェクトのファイルを評価することもありません。
osv-scannerに.NETのファイルをそのまま渡すと起きること
実装前に、osv-scanner 2.7.0に各形式のファイルを渡して挙動を確認しました
(既知の脆弱性があるNewtonsoft.Json 12.0.1を使用)。問題が2つ見つかりました。
1. 解釈できない版でも「脆弱」と報告される
.csprojのVersion="$(NewtonsoftVersion)"やVersion="[12.0.1,13.0.0)"を、osv-scannerはそのまま版の文字列としてapi.osv.devに送ります。そこでOSVのAPIに直接いろいろな文字列を送ってみました。
| 送った版 | 返った脆弱性 |
|---|---|
12.0.1 |
1件(GHSA-5crp-9r3c-p9vr) |
13.0.1(修正版) |
0件 |
$(NJ) |
1件 |
[12.0.1,13.0.0) |
1件 |
garbage |
1件 |
OSVは解釈できない版を「すべての版より前」とみなします。
そのため、実際には修正版を使っていても、プロパティや範囲で書いた参照は脆弱と報告されてしまいます。
2. 自分のプロジェクトがNuGetのパッケージとして照会される
*.deps.jsonには、依存するパッケージだけでなく、自分のプロジェクト(type: project)も並びます。osv-scannerはこれもNuGetのパッケージとして照会します。公開パッケージと同じ名前なら誤検知になり、社内のプロジェクト名はapi.osv.devに送られます。
そこでOSV-Scanner-MCPでは、.NETのファイルはどれもそのままosv-scannerに渡しません。本サーバーが解析し、版が確定しているパッケージだけを書いたpackages.lock.json形式のコピーをスキャンします(RustのCargo.lockと同じ考え方です)。
公開リポジトリの実物のファイルでも確かめました。BitMonoの*.deps.jsonでは、osv-scannerが読む16件のうち、本サーバーが照会しないのは自分のプロジェクトの4件(Echoなど)だけでした。Project-MONAIのpackages.lock.jsonでは、43件のうちProjectの1件だけでした。それ以外は完全に一致しています。
プロジェクトのファイルごとに、情報源を対応付ける
.NETでは、1つのディレクトリに複数のプロジェクトを置けます。
そのため、ディレクトリにlockfileが1つあっても、すべてのプロジェクトの情報とは限りません。OSV-Scanner-MCPは プロジェクトのファイルごとに、そのプロジェクトのものと確認できた情報源を次の順で使います。
packages.<プロジェクト名>.lock.json-
packages.lock.json(ディレクトリにプロジェクトが1つだけの場合) -
obj/project.assets.json(記録されたprojectPathがそのプロジェクトのファイルと一致する場合だけ。同じディレクトリのプロジェクトはobjを共有して上書きし合うため) - どれも無ければ、プロジェクトのファイルの直接の
PackageReferenceだけ
NuGetLockFilePathで別の保存先を指定しているプロジェクトは、既定の名前のlockfileと対応付けません。対応付けられないlockfileも、載っているパッケージは実在するのでスキャンしますが、どのプロジェクトも「確認済み」にはしません。
情報源の無いプロジェクトは、coverage.lockfile_missingに示します。
{
"lockfile_missing": [
{
"path": "Direct/Direct.csproj",
"ecosystem": "NuGet",
"status": "direct_only",
"hint": "No packages.lock.json or obj/project.assets.json was found for this project, so only its direct PackageReference items were scanned (transitive dependencies were not). Set <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> ..."
},
{
"path": "Two/A.csproj",
"ecosystem": "NuGet",
"status": "unconfirmed",
"hint": "A lockfile exists in this directory, but it cannot be confirmed to belong to this project ..."
}
]
}
この場合は推移的依存をスキャンしていないので、complete: falseになります。
.csprojの版は「下限」として扱う
NuGetのVersion="3.6.0"は「3.6.0ちょうど」ではなく、**「3.6.0以上で、最も低い版を優先」**です(完全一致は[3.6.0])。指定の版がフィードに無ければ、それより上の版が復元されます。
そこで、lockfileの無いプロジェクトの.csprojは次のように扱います。
| 指定 | 扱い |
|---|---|
[x](完全一致) |
xとして照会 |
x・[x,)・[x,y)
|
下限xとして照会し、version_is_lower_bound: trueを付ける |
(x,)など下限を含まない範囲 |
照会しない(range) |
$(...) |
照会しない(property) |
4.* |
照会しない(floating) |
| 版なし(中央パッケージ管理) | 照会しない(central_package_management) |
MSBuildを評価しないので、使われるか分からない参照も照会しません。
-
Conditionの付いた参照・ItemGroup、Choose・Targetの中の参照(conditional) -
Update・Removeで同じ名前を扱う要素がある参照。Directory.Build.props/.targetsで扱う名前も含みます(updated・removed。Updateだけを無視すると、先に書いた古い版を照会してしまうため) - 同じ名前の重複、
VersionOverride
照会しなかった参照は、理由とともにcoverage.unresolved_package_referencesに並べます。
{
"unresolved_package_references": [
{ "file": "Direct/Direct.csproj", "name": "Microsoft.Data.SqlClient", "specifier": "1.1.3", "reason": "conditional" },
{ "file": "Direct/Direct.csproj", "name": "System.Net.Http", "specifier": "$(HttpVersion)", "reason": "property" },
{ "file": "Direct/Direct.csproj", "name": "Moq", "specifier": "4.*", "reason": "floating" },
{ "file": "Direct/Direct.csproj", "name": "Old.Pkg", "specifier": "1.0.0", "reason": "updated" }
]
}
自己完結型アプリのランタイムも見る
自己完結型(self-contained)で配置したアプリの*.deps.jsonには、.NETのランタイムそのもの(Microsoft.NETCore.App.Runtime.linux-x64など)が含まれます。SDKはこれをruntimepack.という接頭辞付きの名前で書くため、そのまま照会するとOSVでは1件も一致しません。
| 照会した名前(8.0.0) | 脆弱性 |
|---|---|
runtimepack.Microsoft.NETCore.App.Runtime.linux-x64 |
0件 |
Microsoft.NETCore.App.Runtime.linux-x64 |
12件 |
OSV-Scanner-MCPは接頭辞を除いて照会するので、ランタイムの脆弱性も報告されます。
応答の.NETの項目
| 項目 | 意味 |
|---|---|
local_projects |
照会しなかった自分のプロジェクトの数。自分のコードなのでcompleteに影響しません |
unscannable_nuget_packages |
復元結果のうち、未知の種類・確定版でないため照会しなかったもの |
unresolved_package_references |
.csprojから照会しなかった参照と理由 |
nuget_warnings |
下限で照会したこと、古い可能性のあるproject.assets.json(プロジェクトのファイルより1分以上古い)、確かめられない参照、nuget.org以外のパッケージソース |
bin・objは、プロジェクトのファイルがあるディレクトリのものだけ走査から外します(同じプロジェクトのビルド出力の重複のため)。名前だけで外すと、他のエコシステムのbinまで外れてしまうからです。
導入
Node.js 20.19以上が必要です。Claude Codeの場合:
claude mcp add osv-scanner -- npx -y osv-scanner-mcp
既に使っている方は、npx -y osv-scanner-mcpであれば次の起動時に最新版になります(@0.15.0のように版を固定している場合は更新してください)。Claude Desktop・Codex CLI・VS Code(GitHub Copilot)・Antigravityの設定例はREADMEのSetupにあります。
設定後は、例えば次のように依頼できます。
/absolute/path/to/MySolutionの依存の脆弱性をscan_projectで調べてください。lockfileが無くて直接依存だけになったプロジェクトと、照会できなかった参照があれば分けて教えてください。
通信先と非公開フィードについて
.NETのスキャンで通信するのは api.osv.devだけ です。送るのはパッケージの名前と版で、自分のプロジェクトの名前は送りません。
ただし、非公開のNuGetフィードのパッケージの名前は送られます。 lockfileなどには「どのフィードから取得したか」が記録されないため、公開パッケージと見分けられないからです。スキャン対象のnuget.configにnuget.org以外のソースがあれば、nuget_warningsでその旨を示します。
このリリースのその他の変更
-
1つのファイルでスキャン全体が止まる不具合を修正しました(v0.4.0〜v0.15.0)。 XMLの範囲外の文字参照(
�など)で例外が起き、pom.xml(親POMの参照)が1つあるだけで、同じプロジェクトの他の言語の結果も返らずにinternal_errorになっていました。該当のファイルだけをskipped_filesに示し、残りはスキャンするようにしました。以前の版を使っている方は更新をお勧めします - スキャン範囲の確認を強化しました。読み込んだファイルの場所を、名前で解決し直すのではなく、開いたままのファイルからOSに問い合わせて確認します(Linuxは
/proc/self/fd、macOS・WindowsはネイティブのAPI)。Nodeの権限モデル(--permission)で起動している方は、macOS・Windowsでは--allow-addons、Linuxでは--allow-fs-read=/proc/self/fdを追加してください
注意点と今後
-
検出0件は安全の保証ではありません。 必ず
coverageを確認してください -
suggest_fix(修正版の推奨)は、NuGetにはまだ対応していません(unsupported_ecosystemを返します)。NuGetの版の順序(4つ目の数、大文字小文字を区別しないプレリリースなど)を扱い、dotnet add packageによる更新方法を案内する予定です - 中央パッケージ管理(
Directory.Packages.props)の版は、lockfileが無い場合は解決しません。MSBuildの評価が必要なためで、lockfileの有効化を案内します
まとめ
OSV-Scanner-MCPが.NETのプロジェクトにも使えるようになりました。
osv-scannerにファイルをそのまま渡すと、解釈できない版の参照が脆弱と報告され、自分のプロジェクトがNuGetのパッケージとして照会されます。それを防ぎ、確かめられなかったものはcoverageで隠さずに返すことを重視しました。
不具合や改善案はGitHub Issuesでお知らせください。