When you add a NuGet package to a project, you often think about the runtime library only. In reality, packages can also import MSBuild props/targets and Roslyn analyzers. Those assets run during restore, design-time build, or build. I've already provided some examples of how this can cause issues in NuGet Packages: security risks and best practices.
This means a package can execute code on your machine or in CI without you notice. If you only need a subset of a package assets, you can restrict what is imported by using IncludeAssets and ExcludeAssets on PackageReference.
#Why this matters
NuGet packages may include:
build, buildMultitargeting, and buildTransitive files (MSBuild props and targets)analyzers (including source generators)- runtime and compile libraries
If you do not need build logic or analyzers from a package, blocking them reduces your attack surface and makes builds more predictable. If you read the recent news about supply-chain attacks, you know that any code running on your machine or CI is a potential risk. By default, all assets from a package are imported, so it's important to be aware of this and take action when needed.
#Include only what you need
The safest pattern is often to allow only the assets you need.
XML
<ItemGroup>
<PackageReference Include="Some.Package" Version="1.2.3">
<IncludeAssets>compile;runtime</IncludeAssets>
</PackageReference>
</ItemGroup>
With this configuration, NuGet does not import analyzers or build targets from the package.
#Exclude specific asset types
If you prefer to keep default behavior and remove only some asset groups, use ExcludeAssets.
XML
<ItemGroup>
<PackageReference Include="Some.Package" Version="1.2.3">
<ExcludeAssets>analyzers;build;buildMultitargeting;buildTransitive</ExcludeAssets>
</PackageReference>
</ItemGroup>
This is useful when you still want other assets such as contentFiles.
#Common scenarios
##Include only runtime/library assets
XML
<PackageReference Include="Some.Package" Version="1.2.3">
<IncludeAssets>runtime;compile</IncludeAssets>
</PackageReference>
##Keep the runtime library, disable analyzers
XML
<PackageReference Include="Some.Package" Version="1.2.3">
<ExcludeAssets>analyzers</ExcludeAssets>
</PackageReference>
##Keep analyzers, disable build targets
XML
<PackageReference Include="Some.Package" Version="1.2.3">
<ExcludeAssets>build;buildMultitargeting;buildTransitive</ExcludeAssets>
</PackageReference>
##Use a package for build tooling only
XML
<PackageReference Include="Some.Package" Version="1.2.3" PrivateAssets="all">
<IncludeAssets>build;buildMultitargeting;buildTransitive</IncludeAssets>
</PackageReference>
PrivateAssets="all" prevents the dependency from flowing transitively to projects that reference your project.
#Notes and caveats
- Some packages require analyzers or build targets to work correctly. Test after changing asset filters.
- Prefer explicit and documented choices in
.csproj files for critical dependencies. - Combine this with package lock files and package source mapping for stronger supply-chain protection.
#Additional resources
Do you have a question or a suggestion about this post? Contact me!