Summary
Package pruning only considers directly declared FrameworkReferences when computing the prune set, for restore repeatability. However, this means projects that get a FrameworkReference transitively (via a ProjectReference) have a narrower prune set than expected — leading to spurious vulnerability warnings (NU1903) for packages that are actually provided by the framework at runtime.
Repro
Minimal repro: https://github.com/nkolev92/temp/tree/14920
- Library: targets net8.0, declares
<FrameworkReference Include="Microsoft.AspNetCore.App" />, references Microsoft.EntityFrameworkCore 8.0.0
- Consumer: targets net8.0, has a ProjectReference to Library, does NOT explicitly declare the FrameworkReference
With NuGetAuditMode=all and RestoreEnablePackagePruning=true:
- Consumer gets NU1903 for
Microsoft.Extensions.Caching.Memory 8.0.0 (transitive from EF Core) because it's not in the Consumer's prune set
- Adding
<FrameworkReference Include="Microsoft.AspNetCore.App" /> directly to the Consumer suppresses the warning (package is now prunable)
Expected Behavior
The prune set should account for transitively-inherited FrameworkReferences, or there should be guidance/warning when a project underprunes due to this gap.
Context
Real-world instance: json-api-dotnet/JsonApiDotNetCore#2017
Options
Option 1: Restore uses prune data from transitive FrameworkReferences
Expand the prune set at restore time to include FrameworkReferences from project references.
Pros:
- Solves the problem completely and transparently
Cons:
- Can't do it — during the graph walk we don't know which frameworks or even which projects will be used (conditional TFMs, conditional ProjectReferences, etc.)
- Additional note: There's way to successfully resolve graphs like this in some scenarios, but it may prove challenging to find a general solution. Would affect restore graph resolution performance for sure.
Option 2A: Restore-time warning when underpruning is detected. Raise a warning suggesting the customer to add an explicit framework reference.
After restore completes, check if there are transitive FrameworkReferences that differ from direct ones, and raise a warning.
Pros:
- Actionable guidance right where the problem manifests
- Fires at restore time, so the user sees it even if restore fails the build
Cons:
- Too aggressive — effectively defeats the transitivity of framework refs by telling users to redundantly declare them directly. Even scoping to only project-reference-transitive framework refs is questionable.
- Puts the onus on the user. We're asking them to do something they probably wouldn't want to do, since build/publish already deduped. It doesn't change their output.
Shared concern for all Option 2 variations: There's a versioning problem when projects target different TFMs (e.g. a net10.0 consumer referencing a net8.0 library). The framework-provided version numbers in .NET 8 are lower than what .NET 10 carries. Is it safe to assume that the transitive framework ref's prune data applies to the consumer? The heuristic would need to match on both package ID and version — and cross-TFM scenarios make this unreliable.
Option 2B: Raise a warning suggesting the customer to add an explicit framework reference when a vulnerable package is affected
Only raise the underpruning warning when a package that would have been pruned is flagged as vulnerable.
Pros:
- Low perf impact (only triggers on the vulnerability code path, which is already doing extra work)
- High signal — only fires when there's actual user pain
Cons:
- Users still see the NU1903 alongside the new warning (two warnings for one problem)
- Versioning risk mentioned above.
Option 2C: Suppress NU1903 on packages that would be pruned by transitive framework refs
Don't report vulnerability warnings for packages that would have been pruned if transitive FrameworkReferences were considered.
Pros:
- Directly eliminates the false-positive NU1903
- Clean UX — no spurious warnings
Cons:
- 3rd party scanners still see the package in the graph and will flag it independently
- Silently hiding a warning may confuse users who don't understand why
- Versioning risk mentioned above
Option 2D: Combine 2B and 2C
Suppress the NU1903 AND raise a specific informational warning about the underpruning situation.
Pros:
- Best UX of the restore-time options — removes noise, adds guidance
- Low perf (only on vulnerability path)
Cons:
- Puts the onus on the user. We're asking them to do something they probably wouldn't want to do, since build/publish already deduped. It doesn't change their output.
- Versioning risk mentioned above.
Option 3: Build-time warning
At build time (where the full project graph is resolved), detect underpruning and warn.
Pros:
- Full information available at build time — no ambiguity about which frameworks are in play
- Low perf cost
Cons:
- If restore was going to fail the build (e.g. NU1903 promoted to error via WarningsAsErrors), the user never reaches build to see the guidance
- Puts the onus on the user. We're asking them to do something they probably wouldn't want to do, since build/publish already deduped. It doesn't change their output.
Summary
Package pruning only considers directly declared FrameworkReferences when computing the prune set, for restore repeatability. However, this means projects that get a FrameworkReference transitively (via a ProjectReference) have a narrower prune set than expected — leading to spurious vulnerability warnings (NU1903) for packages that are actually provided by the framework at runtime.
Repro
Minimal repro: https://github.com/nkolev92/temp/tree/14920
<FrameworkReference Include="Microsoft.AspNetCore.App" />, referencesMicrosoft.EntityFrameworkCore 8.0.0With
NuGetAuditMode=allandRestoreEnablePackagePruning=true:Microsoft.Extensions.Caching.Memory 8.0.0(transitive from EF Core) because it's not in the Consumer's prune set<FrameworkReference Include="Microsoft.AspNetCore.App" />directly to the Consumer suppresses the warning (package is now prunable)Expected Behavior
The prune set should account for transitively-inherited FrameworkReferences, or there should be guidance/warning when a project underprunes due to this gap.
Context
Real-world instance: json-api-dotnet/JsonApiDotNetCore#2017
Options
Option 1: Restore uses prune data from transitive FrameworkReferences
Expand the prune set at restore time to include FrameworkReferences from project references.
Pros:
Cons:
Option 2A: Restore-time warning when underpruning is detected. Raise a warning suggesting the customer to add an explicit framework reference.
After restore completes, check if there are transitive FrameworkReferences that differ from direct ones, and raise a warning.
Pros:
Cons:
Shared concern for all Option 2 variations: There's a versioning problem when projects target different TFMs (e.g. a net10.0 consumer referencing a net8.0 library). The framework-provided version numbers in .NET 8 are lower than what .NET 10 carries. Is it safe to assume that the transitive framework ref's prune data applies to the consumer? The heuristic would need to match on both package ID and version — and cross-TFM scenarios make this unreliable.
Option 2B: Raise a warning suggesting the customer to add an explicit framework reference when a vulnerable package is affected
Only raise the underpruning warning when a package that would have been pruned is flagged as vulnerable.
Pros:
Cons:
Option 2C: Suppress NU1903 on packages that would be pruned by transitive framework refs
Don't report vulnerability warnings for packages that would have been pruned if transitive FrameworkReferences were considered.
Pros:
Cons:
Option 2D: Combine 2B and 2C
Suppress the NU1903 AND raise a specific informational warning about the underpruning situation.
Pros:
Cons:
Option 3: Build-time warning
At build time (where the full project graph is resolved), detect underpruning and warn.
Pros:
Cons: