Skip to content

Improving the experience when Package pruning underprunes when FrameworkReference is transitive #14920

@nkolev92

Description

@nkolev92

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions