.NET 10で導入されるNuGet Package Pruningは、脆弱性チェックを弱める機能ではありません。結論から言うと、.NET Runtime Librariesがすでに提供しているパッケージをNuGetの依存関係グラフから整理し、開発者が実際に対応すべき脆弱性レポートを見つけやすくするための変更です。
2026年5月19日に確認されたMicrosoft公式情報では、.NET 10でNuGetAuditMode=allとPackage pruningが既定の組み合わせになり、従来の既定値を使うプロジェクトと比べて推移的な脆弱性レポートが70%少なくなると説明されています。管理者や開発者がまず確認すべきなのは、.NET 10を対象にしたプロジェクト、packages.lock.json、CI/CDの警告ポリシー、そして不要になった直接PackageReferenceです。(Microsoft for Developers)
.NET 10のNuGet Package Pruningで何が変わるのか
NuGet Package Pruningは、restore時に「プラットフォーム側がすでに提供しているパッケージ」を依存関係グラフから取り除く仕組みです。たとえば、System.Text.JsonやSystem.Text.Encodings.Webのように、かつてはNuGetパッケージとして参照されていたライブラリでも、対象フレームワークの.NET Runtimeが同等以上のバージョンを提供している場合、NuGet上の古い推移的依存関係として残す必要がないケースがあります。(Microsoft for Developers)
この変更の狙いは、単に依存パッケージ数を減らすことではありません。古い推移的パッケージがグラフに残っていると、実際のアプリケーションでは.NET Runtime側の修正済みライブラリを使っているにもかかわらず、脆弱性スキャナーが「古いパッケージを使っている」と判断することがあります。Package pruningは、このようなノイズを減らし、脆弱性レポートをより実務で使いやすいものにします。
| 項目 | .NET 10での変更 | 実務上の影響 |
|---|---|---|
| Package pruning | .NET 10以降を対象にするプロジェクトで既定有効 | プラットフォーム提供済みの推移的パッケージが依存関係グラフから外れる |
| NuGet Audit | NuGetAuditModeの既定がall | 直接依存だけでなく推移的依存関係も監査対象になる |
直接PackageReference | prune対象の場合、PrivateAssets='all'とIncludeAssets='none'が暗黙適用 | 使われない直接参照が見つかりやすくなる |
| NU1510 | 削除可能な直接PackageReferenceに警告 | 不要な参照を削除する判断材料になる |
| ロックファイル | prune後にpackages.lock.jsonの内容が減る場合がある | CIのlocked modeやレビュー時に差分の意味を確認する必要がある |
Package pruningは、.NET SDK 9.0.200ではオプトイン機能として提供され、.NET 10では既定の体験に組み込まれています。また、.NET 10 SDKでは直接パッケージにも影響し、prune対象の直接PackageReferenceにはPrivateAssets='all'とIncludeAssets='none'が暗黙的に適用されます。(Microsoft Learn)
なぜ脆弱性レポートが見やすくなるのか
従来の.NETプロジェクトでは、最大限の互換性を確保するためにnetstandard2.0を対象にしたライブラリが多く使われてきました。これらのライブラリは、System.MemoryやSystem.Text.JsonなどをNuGetパッケージとして依存関係に含むことがあります。しかし、より新しい.NETを対象にしたアプリケーションでは、同じ機能が.NET Runtime側から提供されていることがあります。(Microsoft for Developers)
この状態でCVEが公開されると、脆弱性レポートには「推移的依存関係に古いパッケージがある」と表示されることがあります。ところが、実際のビルドや実行時には.NET Runtime側のライブラリが使われるため、開発チームは「これは本当に対応すべき警告なのか」を毎回確認しなければなりません。
.NET 10のPackage pruningでは、prune範囲に入る推移的パッケージはダウンロードされず、restore出力にも表示されず、NuGet Auditの推移的依存関係としても扱われません。これにより、残った警告は「実際にプロジェクトが依存しているパッケージ」に近づきます。(Microsoft for Developers)
ただし、ここで注意したいのは「警告が減る=セキュリティ対応が不要になる」ではないことです。.NET 10ではむしろNuGetAuditMode=allにより推移的依存関係の監査範囲が広がります。Package pruningは、その広がった監査結果からプラットフォーム提供済みのノイズを取り除く役割です。
影響を受けるプロジェクト
もっとも影響を受けるのは、net10.0以降をターゲットにしたSDKスタイルの.NETプロジェクトです。マルチターゲット構成で、いずれかのターゲットフレームワークにnet10.0以降が含まれる場合も注意が必要です。Microsoftの説明では、.NET 10以降のターゲットが含まれるプロジェクトではPackage pruningがプロジェクト内の全ターゲットフレームワークに適用されます。(Microsoft for Developers)
| プロジェクトの状態 | 確認すべきポイント |
|---|---|
net10.0単独 | Package pruningと推移的依存関係監査が既定で有効になる |
net10.0;netstandard2.0などのマルチターゲット | 古いターゲット向けに必要なパッケージまで誤って削除しない |
| .NET 9以前 | Package pruningは既定ではなく、必要に応じてRestoreEnablePackagePruningを有効化して検証する |
packages.lock.jsonを使うアプリ | prune後のロックファイル差分をレビューし、CIのlocked modeと整合させる |
| ライブラリをNuGetパッケージ化して配布 | .nuspecの依存関係が意図どおり残るか確認する |
特にライブラリ開発者は、アプリケーション開発者より慎重に確認する必要があります。自分のライブラリをdotnet packして配布する場合、あるターゲットでは.NET Runtimeが提供するため依存関係が不要でも、別のターゲットではNuGetパッケージとして依存関係を残す必要があるかもしれません。
管理者・開発者が確認すべき設定
NuGetAuditModeは「all」が既定になる
.NET 10以降を対象にしたプロジェクトでは、明示的に設定していない場合、NuGetAuditModeの既定値がallになります。これにより、直接参照しているパッケージだけでなく、その先にある推移的依存関係の脆弱性もrestore時の警告対象になります。警告をエラー扱いにしているCIでは、移行直後にrestoreが失敗する可能性があります。(Microsoft Learn)
リポジトリ全体で方針を明確にするなら、Directory.Build.propsに設定を集約すると管理しやすくなります。
<Project>
<PropertyGroup>
<NuGetAuditMode>all</NuGetAuditMode>
<RestoreEnablePackagePruning>true</RestoreEnablePackagePruning>
</PropertyGroup>
</Project>
.NET 10では上記の値が既定に近い状態ですが、明示しておくことで「このリポジトリでは推移的依存関係まで監査する」という方針をチーム内で共有できます。
一方、移行初期にCIが頻繁に失敗して開発が止まる場合は、一時的に警告の扱いを調整する選択肢もあります。ただし、NuGetAuditMode=directに戻す運用を長期化させると、推移的依存関係の脆弱性を見逃しやすくなります。
NU1901〜NU1904の扱いを決める
NuGet Auditでは、脆弱性の深刻度に応じてNU1901からNU1904までの警告が使われます。Microsoft Learnでは、低・中程度の脆弱性は警告のままにし、高・重大な脆弱性をエラー扱いにする例も示されています。(Microsoft Learn)
たとえば、高以上をビルド停止の対象にするなら、次のように設定できます。
<PropertyGroup>
<WarningsAsErrors>$(WarningsAsErrors);NU1903;NU1904</WarningsAsErrors>
</PropertyGroup>
すでにTreatWarningsAsErrorsを使ってすべての警告をエラーにしている場合、移行初期はNuGet Audit関連の警告をエラー化から外し、棚卸し後に段階的に厳格化する方法もあります。
<PropertyGroup>
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904;$(WarningsNotAsErrors)</WarningsNotAsErrors>
</PropertyGroup>
おすすめは、最初から全警告を無視するのではなく、次のように段階を分けることです。
| フェーズ | 運用方針 |
|---|---|
| 移行前の調査 | 警告を収集し、直接依存・推移的依存・prune対象を分類する |
| 移行直後 | NU1903とNU1904を優先対応し、低リスク項目は期限付きで管理する |
| 安定運用 | 高・重大な脆弱性はCIでブロックし、抑制はレビュー必須にする |
Audit Sourcesも確認する
企業ネットワークでは、nuget.orgへの直接アクセスを制限している場合があります。その場合、脆弱性情報を取得できず、監査結果が不完全になる可能性があります。NuGet AuditではauditSourcesを使い、パッケージ取得元とは別に脆弱性情報の取得元を指定できます。Microsoftは、パッケージコンテンツを含まない脆弱性データ専用のエンドポイントも説明しています。(Microsoft Learn)
<configuration>
<auditSources>
<clear />
<add key="nuget-org-vulnerability-data" value="https://data.nuget.org/v3/index.json" />
</auditSources>
</configuration>
社内NuGetフィードやプロキシを使っている場合は、「パッケージは社内フィードから取得するが、脆弱性情報は取得できているか」を必ず確認してください。セキュリティ監査が有効でも、脆弱性データに到達できなければ意味がありません。
移行時の実践手順
現在の依存関係を棚卸しする
まずは、移行前の状態で依存関係と脆弱性レポートを保存します。.NET 10 SDKではdotnet package listが使えます。.NET 9以前では従来どおりdotnet list package形式を使います。Microsoft Learnでは、.NET 10で「noun first」のdotnet package list形式が導入されたと説明されています。(Microsoft Learn)
dotnet package list --include-transitive --vulnerable --format json > dependency-report-before.json
推移的依存関係だけを確認したい場合は、次のコマンドも役立ちます。
dotnet package list --include-transitive
.NET 9以前のSDKを使っている環境では、次の形式に読み替えます。
dotnet list package --include-transitive --vulnerable
restore後の差分を見る
次に、.NET 10 SDKでrestoreを実行し、依存関係グラフの変化を確認します。
dotnet restore
dotnet package list --include-transitive --vulnerable --format json > dependency-report-after.json
ここで見るべきなのは、単に「警告数が減ったか」ではありません。次の3点を確認してください。
| 確認項目 | 判断基準 |
|---|---|
| 消えた警告 | prune対象のプラットフォーム提供パッケージだったか |
| 残った警告 | 実際に直接・推移的に使っているパッケージか |
| 新たに出た警告 | NuGetAuditMode=allにより、これまで見えていなかった推移的依存関係か |
消えた警告を「すべて問題なし」として処理するのではなく、なぜ消えたのかを確認することが重要です。特にセキュリティレビューや監査証跡が必要な組織では、「.NET Runtime提供パッケージとしてpruneされたため」と説明できる状態にしておくと、後から判断を追跡しやすくなります。
NU1510が出たら直接参照を見直す
NU1510は、Package pruningの結果として「この直接PackageReferenceは不要な可能性がある」と知らせる警告です。Microsoft Learnでは、対象の.NET SDKが同等以上のアセンブリを提供している場合、そのパッケージ参照を削除できる可能性があると説明されています。(Microsoft Learn)
たとえば、net10.0のみを対象にしたアプリでSystem.Text.Jsonを直接参照しており、NU1510が出た場合は、次の流れで確認します。
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="10.0.0" />
</ItemGroup>
確認手順は次のとおりです。
| 手順 | 実施内容 |
|---|---|
| 1 | PackageReferenceを一時的に削除する |
| 2 | dotnet restoreとdotnet buildを実行する |
| 3 | 単体テスト、統合テスト、publish結果を確認する |
| 4 | 問題がなければ参照削除をコミットする |
| 5 | マルチターゲットの場合は古いターゲットで必要ないか再確認する |
注意点は、net10.0;net48のようなマルチターゲット構成です。net10.0では不要でも、net48ではパッケージ参照が必要な場合があります。この場合は無条件に削除せず、Condition付きのPackageReferenceとして残す判断が必要です。
packages.lock.jsonの差分をレビューする
packages.lock.jsonを使っているプロジェクトでは、Package pruningによってロックファイルから一部の推移的パッケージが消えることがあります。Microsoft Learnでも、pruningを有効化した既存プロジェクトでは、ロックファイル再生成時に以前より少ないパッケージが含まれる可能性があると説明されています。(Microsoft Learn)
これは必ずしも異常ではありません。ただし、CI/CDで--locked-modeを使っている場合、ロックファイルを更新しないままSDKやターゲットフレームワークだけを変えるとrestoreが失敗することがあります。
dotnet restore --locked-mode
移行ブランチでは、次の順序で作業すると安全です。
| 順序 | 作業 |
|---|---|
| 1 | SDKバージョンとTargetFrameworkの変更を行う |
| 2 | 通常のdotnet restoreでロックファイルを再生成する |
| 3 | 消えたパッケージがprune対象か確認する |
| 4 | dotnet restore --locked-modeでCI相当の動作を確認する |
| 5 | ロックファイル差分をコードレビューに含める |
脆弱性が残った場合の対応方針
Package pruning後に残る脆弱性警告は、優先度を付けて対応します。直接参照しているパッケージに脆弱性がある場合は、修正済みバージョンへの更新が基本です。推移的依存関係の場合は、Microsoft Learnが推奨するように、まず直接参照に近い上位パッケージを更新し、それでも解消しない場合に問題のパッケージを直接更新する流れが現実的です。(Microsoft Learn)
| 警告の種類 | 優先対応 |
|---|---|
| 直接依存の脆弱性 | 該当パッケージを修正済みバージョンへ更新する |
| 推移的依存の脆弱性 | まず上位の直接依存パッケージを更新する |
| 修正済みバージョンがない | アドバイザリの緩和策、代替パッケージ、Issue報告を検討する |
| 自社環境では影響しないと判断 | 証跡を残したうえでNuGetAuditSuppressを検討する |
NuGetAuditSuppressは便利ですが、最後の手段として扱うべきです。抑制すると、該当アドバイザリが監査レポートから見えなくなります。使う場合は、影響しない理由、レビュー者、見直し期限をチケットやリポジトリ内のドキュメントに残してください。(Microsoft Learn)
<ItemGroup>
<NuGetAuditSuppress Include="https://github.com/advisories/XXXX" />
</ItemGroup>
展開時に失敗しやすいポイント
警告が減ったことを「対応完了」と誤解する
Package pruningで減るのは、主にプラットフォーム提供済みパッケージに由来するノイズです。NuGet Auditそのものは.NET 10で推移的依存関係まで既定で監査するため、残った警告はむしろ重要度が上がると考えるべきです。
セキュリティ担当者には、「警告を隠した」のではなく「依存関係グラフを実際の利用状態に近づけた」と説明すると伝わりやすくなります。
CIのTreatWarningsAsErrorsで移行が止まる
TreatWarningsAsErrorsを全体に設定しているリポジトリでは、.NET 10移行後にNU1901〜NU1904が原因でrestoreやbuildが止まることがあります。これはセキュリティ上は望ましい面もありますが、移行作業の初日にすべてを解決するのは現実的でない場合があります。
おすすめは、移行ブランチで脆弱性レポートを棚卸しし、重大度と到達可能性で対応期限を分けることです。高・重大なものはCIでブロックし、低・中程度は期限付きで警告にするなど、チームの運用に合わせて段階的に厳格化します。
マルチターゲットの古いフレームワークを見落とす
net10.0だけなら不要なパッケージでも、netstandard2.0や.NET Framework向けには必要なことがあります。NU1510が出たからといって、すぐに全ターゲットから削除すると、古いターゲットのビルドやパッケージ利用者に影響する可能性があります。
判断に迷う場合は、対象フレームワークごとにPackageReferenceへConditionを付けます。
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" Version="9.0.4" />
</ItemGroup>
SBOMやライセンスチェックの差分を見落とす
依存関係グラフからパッケージが消えると、SBOM生成ツールやライセンスチェックツールの結果も変わる場合があります。これはPackage pruningの目的に沿った変化ですが、監査部門や運用部門が「依存パッケージが消えた」とだけ見ると混乱します。
移行時は、脆弱性レポートだけでなく、SBOM、ライセンス一覧、コンテナイメージのスキャン結果も比較しておくと安全です。
.NET 9以前で試す場合の考え方
.NET 9 SDK 9.0.200以降では、Package pruningをオプトインで試せます。ただし、.NET 10と同じ既定動作ではありません。既存プロジェクトで効果を見たい場合は、検証ブランチでRestoreEnablePackagePruningを有効にし、依存関係グラフとロックファイルの差分を確認します。(Microsoft Learn)
<Project>
<PropertyGroup>
<RestoreEnablePackagePruning>true</RestoreEnablePackagePruning>
<NuGetAuditMode>all</NuGetAuditMode>
</PropertyGroup>
</Project>
この設定を本番リポジトリに入れる前に、次の点を確認してください。
| 確認項目 | 理由 |
|---|---|
| CIとローカルのSDKバージョン | SDK差異でrestore結果が変わる可能性がある |
packages.lock.json | prune後に差分が出る可能性がある |
dotnet pack結果 | ライブラリ配布時の依存関係が変わる可能性がある |
| セキュリティレポート | 警告数だけでなく、残った警告の内容を確認する |
まず実施すべきチェックリスト
.NET 10への移行、またはNuGet Package Pruningの検証を始めるなら、次の順番で確認すると無駄がありません。
| チェック | 実施内容 |
|---|---|
| SDK確認 | global.json、CIエージェント、開発端末の.NET SDKを確認する |
| 対象TFM確認 | net10.0以降を含むプロジェクトを洗い出す |
| 監査設定確認 | NuGetAuditMode、NuGetAuditLevel、auditSourcesを確認する |
| 依存関係比較 | dotnet package list --include-transitive --vulnerableで前後差分を見る |
| NU1510対応 | 不要な直接PackageReferenceを削除または条件付きにする |
| ロックファイル更新 | packages.lock.jsonの差分をレビューし、CIで--locked-modeを確認する |
| CIポリシー調整 | NU1903、NU1904を優先してビルドブロック対象にする |
| 抑制ルール整備 | NuGetAuditSuppressは理由と期限を残して使う |
NuGet Package Pruningは、.NETの依存関係管理を「数が多いものを全部見る」状態から、「実際に使っているものを優先して見る」状態へ近づける変更です。まずは移行ブランチで依存関係レポートを出し、消えた警告、残った警告、新しく見えた警告を分けて確認してください。そのうえで、不要なPackageReferenceの削除、ロックファイルの更新、CIの警告ポリシー整備を進めるのが、もっとも安全で実務的な対応です。

コメント