Xshell Pro
📖 Tutorial

Clean Up Dependencies and Silence False Alerts: A Guide to NuGet Package Pruning in .NET 10

Last updated: 2026-05-20 13:46:30 Intermediate
Complete guide
Follow along with this comprehensive guide

Overview

If you’ve ever run a NuGet vulnerability audit on a .NET project, you’ve probably seen warnings about transitive packages—dependencies that were never directly added. Often, packages like System.Text.Json or System.IO.Pipelines are flagged with CVEs even though your application is actually using a newer, safe version provided by the .NET runtime libraries. This is a classic false positive.

Clean Up Dependencies and Silence False Alerts: A Guide to NuGet Package Pruning in .NET 10
Source: devblogs.microsoft.com

In .NET 10, a feature called package pruning eliminates this noise. During restore, NuGet automatically removes any package from the dependency graph that is already supplied (at an equal or higher version) by the runtime libraries. Combined with the new default audit mode (NuGetAuditMode = all), this reduces reported transitive vulnerabilities by an average of 70% according to Microsoft telemetry.

This guide explains how pruning works, how to take full advantage of it, and what to watch out for.

Prerequisites

Before diving into package pruning, make sure you have:

  • .NET 10 SDK or later (the feature is built into the SDK and is on by default)
  • A .NET project targeting net10.0 or a later version
  • Familiarity with basic NuGet commands (dotnet restore, dotnet list package –vulnerable)

If you’re still on .NET 8 or .NET 9, you can still benefit from the concept—but pruning is only fully automatic from .NET 10 onward.

Step-by-Step Instructions

Step 1: Create or Open a .NET 10 Project

Create a new console app or open an existing one. For this example, let’s create a simple project:

dotnet new console -n PruningDemo
cd PruningDemo

Make sure your project file (.csproj) targets net10.0:

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

Step 2: Add a NuGet Package That Brings Transitive Dependencies

Choose a package that depends on older versions of common runtime libraries. For instance, a package targeting netstandard2.0 often pulls in System.Memory or System.Text.Json. Let’s add one such package:

dotnet add package Newtonsoft.Json --version 13.0.3

This package has a dependency on System.Runtime.CompilerServices.Unsafe and other libraries that are now part of the .NET runtime. When you restore, NuGet will evaluate whether those transitive packages can be pruned.

Step 3: Run Restore and Observe Pruning

Run a restore with verbose logging to see pruning in action:

dotnet restore --verbosity normal

Look for lines like:

Pruning package 'System.Text.Json' version 6.0.0 from dependency graph (already provided by runtime)

If you don’t see any, you can force a detailed view:

dotnet restore --verbosity detailed | findstr Pruning   # Windows
grep Pruning                                         # Linux/Mac

You should see several packages being removed—these are the ones that the .NET runtime already supplies.

Step 4: Run a Vulnerability Audit

Now run the built-in vulnerability scanner:

dotnet list package --vulnerable

Because pruning has removed the redundant packages, you’ll likely see far fewer alerts. In fact, many “vulnerable” transitive packages that are provided by the runtime are now gone, so only genuine, unresolved threats remain.

Step 5: Check the Final Dependency Graph

To confirm pruning worked, list all resolved packages:

Clean Up Dependencies and Silence False Alerts: A Guide to NuGet Package Pruning in .NET 10
Source: devblogs.microsoft.com
dotnet list package --include-transitive

Notice that packages like System.Text.Json or System.IO.Pipelines are absent if they were pruned—even though the original library still depends on them in its nuspec. The actual runtime-provided versions are used at compile time.

Common Mistakes

Mistake 1: Expecting Pruning to Remove All Vulnerabilities

Package pruning only removes transitive dependencies that the runtime libraries already provide at the same or higher version. If a vulnerable transitive package is not included in the runtime (e.g., a third-party library with its own CVE), pruning won’t help. You still need to update or otherwise mitigate those.

Mistake 2: Disabling Pruning Accidentally

Some developers might set NuGetAuditMode to direct or turn off auditing entirely. While that removes warnings, it also disables pruning. To keep pruning active while reducing noise, use the new defaults: NuGetAuditMode = all (the default in .NET 10). If you must audit only direct dependencies, consider adding NuGetAuditMode = direct but be aware that transitive pruning is still recommended.

Mistake 3: Assuming All Runtime Packages Are Pruned

Not every package that “belongs” to the runtime is pruned. The SDK maintains a specific mapping of framework-provided packages per target framework. If a transitive dependency version is higher than what the runtime supplies (e.g., System.Text.Json 9.0.0 when targeting net8.0, which only provides up to 8.0.x), it will not be pruned. Always check the actual runtime version you’re targeting.

Mistake 4: Ignoring the NuGet Cache After Pruning

Pruning does not delete the packages from your local cache—it only removes them from the dependency graph. If you’re concerned about disk space, run dotnet nuget locals all –-clear periodically. But remember, the pruned packages are still stored locally, they just aren’t used.

Summary

NuGet Package Pruning in .NET 10 is a game changer for eliminating false-positive vulnerability reports and simplifying your dependency graph. By automatically removing transitive packages that are already part of the .NET runtime libraries, it reduces clutter and noise, letting you focus on real security issues. The feature is enabled by default, so upgrading to .NET 10 is the first step. Then, just restore as usual—pruning happens behind the scenes. Remember to keep your runtime up to date and verify pruning by checking the restore logs. With this simple change, you can cut your vulnerability report count by 70% and improve your project’s clarity.