.NET開発においてライブラリの依存関係管理は非常に重要ですが、意図せず導入される推移的(トランジティブ)パッケージによる脆弱性警告に悩まされるケースが少なくありません。

2026年5月に公開された最新情報によると、.NET 10ではこの問題を根本から解決する「NuGetパッケージプルーニング(NuGet Package Pruning)」がデフォルトで導入されました。

この機能は、プロジェクトの依存関係グラフから不要なパッケージを自動的に排除し、開発者が本当に対処すべき脆弱性のみに集中できる環境を提供します。

本記事では、.NET 10におけるパッケージプルーニングの仕組みとその導入メリット、そして具体的にどのような変化がもたらされるのかを詳しく解説します。

背景:なぜパッケージの重複と脆弱性の誤検知が起きるのか

これまで、多くのNuGetライブラリは互換性を最大化するために netstandard2.0 をターゲットにしてきました。

これらのライブラリは、System.Text.JsonSystem.Memory といったパッケージに依存していることが一般的です。

しかし、近年の.NETプラットフォームの進化に伴い、かつて個別のパッケージとして提供されていた機能の多くが、現在の.NETランタイムライブラリ自体に組み込まれる(in-box)ようになりました。

例えば、.NET 10プロジェクトで古いライブラリを使用すると、そのライブラリが System.Text.Json 8.0.0 を要求する場合があります。

一方で、.NET 10のランタイムには既に、より新しく安全なバージョンの System.Text.Json が含まれています。

従来のNuGetの挙動では、ランタイム側に新しいバージョンが存在していても、依存関係グラフには古いパッケージが残り続けていました。

その結果、古いパッケージに対して公開された脆弱性(CVE)がスキャナーによって検出され、実際にはアプリで使用されていないにもかかわらず「脆弱性あり」と報告される「偽陽性(False Positive)」が多発していました。

現在の課題:複雑化する依存関係グラフ

偽陽性の脆弱性レポート以外にも、従来の管理方式にはいくつかの問題点がありました。

  • 復元グラフの肥大化:解決すべきパッケージが増えるほど、ダウンロード量やメモリ消費量が増加し、ビルドのパフォーマンスが低下します。
  • 古いパッケージ参照の残存:サポートが終了した古いパッケージがグラフ内に残り続けることで、ライブラリの競合や予期せぬ実行エラーの原因となります。
  • ノイズの多い監査結果:大量の推移的依存関係の警告に埋もれてしまい、本当に修正が必要な脆弱性を見逃すリスクが高まります。

パッケージプルーニング(Package Pruning)の仕組み

パッケージプルーニングは、NuGetのリストア(復元)時に、.NETランタイムライブラリが既に提供しているパッケージを依存関係グラフから削除する機能です。

.NET SDKは、各ターゲットフレームワーク(TFM)が提供するパッケージとその最高バージョンのリストを保持しています。

NuGetはリストア中にこのリストを参照し、推移的な依存関係がランタイム提供の範囲内に収まっている場合、そのパッケージをグラフから「剪定(プルーニング)」します。

推移的依存関係と直接参照の扱い

パッケージプルーニングでは、依存関係の種類によって処理方法が異なります。

推移的なパッケージ(間接的に参照されているもの)の場合、プルーニング範囲内であればグラフから完全に削除されます。

これらはダウンロードされず、リストア結果にも表示されず、NuGet Auditの監査対象からも外れます。

一方、プロジェクトファイル(.csproj)で直接 PackageReference として指定されているパッケージがプルーニング対象の場合、挙動が少し特殊です。

NuGetは自動的に PrivateAssets='all' および IncludeAssets='none' を適用し、そのパッケージが公開出力に含まれないようにします。

これは、アプリが個別のパッケージとしてではなく、.NETランタイムに含まれる実装を使用することを明示するためです。

また、全てのターゲットフレームワークにおいてそのパッケージが不要であると判断された場合、NuGetは「NU1510」という警告を発し、プロジェクトファイルからの削除を推奨します。

.NET 10における大きな変更点

.NET 10では、これまでオプトイン(任意有効化)だった機能がデフォルトの挙動へと格上げされました。

具体的には、.NET 10以降をターゲットとするプロジェクトにおいて、以下の設定が標準で有効となります。

項目.NET 10でのデフォルト設定効果
RestoreEnablePackagePruningtrue不要なランタイム提供パッケージを自動削除する
NuGetAuditModeall直接参照だけでなく、推移的依存関係も含めて脆弱性をスキャンする

この二つの機能が組み合わさることで、真に強力な依存関係管理が実現します。

プルーニングによってプラットフォーム提供の「ノイズ」が除去され、その上で NuGetAuditMode=all によって、残った推移的依存関係の中に潜む本物の脆弱性が確実に報告されます。

マイクロソフトが収集したテレメトリデータによると、これらのデフォルト設定を使用しているプロジェクトでは、推移的な脆弱性レポートの数が以前と比較して70%も減少したことが報告されています。

導入によるパフォーマンスへの影響

パッケージプルーニングの導入は、セキュリティだけでなく開発効率の向上にも大きく寄与します。

グラフから不要なパッケージが削除されることで、NuGetはより少ないパッケージを解決すれば済むようになります。

これにより、バージョンの競合解決にかかる計算コストが削減され、リストアが失敗する確率も低下します。

実際の計測結果では、プロジェクト単位でのリストア時間が最大で50%短縮されるケースも確認されています。

実例:プルーニング適用の前後比較

実際に .NET 10 プロジェクトでプルーニングがどのように機能するか、コードと出力結果で確認してみましょう。

以下の例では、脆弱性を含む古い System.Formats.Asn1 を間接的に参照するライブラリを使用しているプロジェクトを想定します。

XML
<!-- Sample.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <!-- .NET 10ではこれらはデフォルトで有効 -->
    <NuGetAuditMode>all</NuGetAuditMode>
    <RestoreEnablePackagePruning>true</RestoreEnablePackagePruning>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.AI" Version="10.0.1" />
    <PackageReference Include="NuGet.Protocol" Version="6.9.1" />
  </ItemGroup>
</Project>

プルーニングが無効な場合の出力

プルーニングが行われない場合、依存関係にある System.Formats.Asn1 6.0.0 が検出され、警告が表示されます。

実行結果
Sample.csproj : warning NU1903: Package 'System.Formats.Asn1' 6.0.0 has a known high severity vulnerability.

Transitive Package                Resolved
> System.Formats.Asn1             6.0.0 (Vulnerable!)
> System.Text.Json                10.0.0
> System.Threading.Channels       10.0.0

プルーニングが有効な場合の出力(.NET 10のデフォルト)

プルーニングが適用されると、net10.0 ランタイムがより新しい実装を提供しているため、グラフから該当パッケージが消え、警告も消失します。

実行結果
Project 'Sample' has the following package references
   [net10.0]:
   Top-level Package              Resolved
   > Microsoft.Extensions.AI      10.0.1
   > NuGet.Protocol               6.9.1

   Transitive Package             Resolved
   > Newtonsoft.Json              13.0.3
   (System.Formats.Asn1 などのランタイム提供パッケージはグラフから除外されました)

このように、開発者が意識することなく、プロジェクトの健全性が自動的に維持されるようになります。

まとめ

.NET 10から導入されたNuGetパッケージプルーニングは、現代の.NET開発における依存関係の複雑さを解消する重要な機能です。

この機能により、プラットフォーム提供のパッケージが冗長にグラフへ含まれることがなくなり、「本当に必要なパッケージだけ」が管理対象となります。

結果として、偽陽性の脆弱性レポートが劇的に減少し、ビルドパフォーマンスが向上し、開発者は本来のコーディング作業に集中できるようになります。

2026年5月の最新情報に基づき、今後 .NET 10 への移行を検討されている方は、この強力なデフォルト設定の恩恵をぜひ活用してください。

詳細な仕様については、Microsoftの公式ドキュメント(package pruning)を参照することをお勧めします。