Supply-Chain Security
|
This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The C# Reference bibliography lists the reference material consulted while preparing these pages. |
A .NET build executes far more code than the project’s own: every package can add compile-time analyzers and source
generators, MSBuild .props/.targets that run arbitrary tasks, and runtime assemblies that ship to users. The supply
chain is the path that code takes from a package author to a build agent. This page lists the threats along that path and
the control the tool chain offers for each, with the real errors and warnings as they appear in .NET SDK 10.0.402 /
NuGet 7.9.0. Each control has a detail page; this page shows how they fit together. Supply-chain advice for the
publishing side (signing, Trusted Publishing, key hygiene) is summarised at the end.
Threats and controls
| Threat | Control | Detail page |
|---|---|---|
Dependency confusion: an internal package name is registered on a public feed with a higher version, and restore picks the public one |
Package source mapping (every ID maps to the feeds allowed to serve it); |
|
Typosquatting: a look-alike ID ( |
Prefix reservation (a verified owner’s IDs show a check mark on nuget.org); review new dependencies |
|
Interception or tampering in transit |
HTTPS-only sources ( |
|
A different package than reviewed (new transitive version, re-published content on a private feed) |
|
|
Known-vulnerable dependencies, direct or transitive |
NuGetAudit during restore; |
|
A freshly published malicious version picked up before anyone notices |
Minimum publish age ( |
|
Malicious build logic in a package or a repository folder |
Build only reviewed sources; |
|
Stolen publishing credentials |
Trusted Publishing (no long-lived key); scoped, short-lived API keys |
HTTPS-only sources
Since NuGet 6.12 (Visual Studio 17.12, .NET 9.0.100), an http:// source is an error for restore and push,
not the earlier warning NU1803. A project whose nuget.config lists the local BaGetter server over HTTP:
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="ledgerline-bagetter" value="http://localhost:5555/v3/index.json" />
</packageSources>
</configuration>
Determining projects to restore...
~/audit/Audit.csproj : error NU1302: You are running the 'restore' operation with an 'HTTP' source: http://localhost:5555/v3/index.json. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information.
Failed to restore ~/audit/Audit.csproj (in 117 ms).
The opt-out is per source, allowInsecureConnections="true" — acceptable for a server on localhost, as in
Ledgerline, and not for anything that crosses a network. For one-off commands, dotnet nuget add source and
dotnet nuget push accept --allow-insecure-connections. The documentation’s version labels for these are
inconsistent: the HTTPS page gives ".NET 9.0.1xx" for add source and ".NET 10.0.1xx" for push, while the NuGet
release notes introduced the option in NuGet 6.11 / SDK 8.0.400; and its rollout table pairs .NET 6.0.100 with NuGet 6.3,
although SDK 6.0.100 shipped NuGet 6.0.
Vulnerable dependencies: NuGetAudit
Restore checks every resolved package against the vulnerability data the sources publish (nuget.org mirrors the GitHub
Advisory Database). Since .NET 10, projects targeting net10.0 or later audit all packages, transitive ones
included (NuGetAuditMode=all); older TFMs audit direct references only by default. Restoring a project that references
NuGet.Protocol 5.11.2 — the example the NuGet documentation uses — reports the package and four of its dependencies:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NuGet.Protocol" Version="5.11.2" />
</ItemGroup>
</Project>
Determining projects to restore...
~/audit/Audit.csproj : warning NU1903: Package 'Newtonsoft.Json' 9.0.1 has a known high severity vulnerability, https://github.com/advisories/GHSA-5crp-9r3c-p9vr
~/audit/Audit.csproj : warning NU1903: Package 'NuGet.Common' 5.11.2 has a known high severity vulnerability, https://github.com/advisories/GHSA-6qmf-mmc7-6c2p
~/audit/Audit.csproj : warning NU1904: Package 'NuGet.Packaging' 5.11.2 has a known critical severity vulnerability, https://github.com/advisories/GHSA-68w7-72jg-6qpp
~/audit/Audit.csproj : warning NU1901: Package 'NuGet.Packaging' 5.11.2 has a known low severity vulnerability, https://github.com/advisories/GHSA-g4vj-cjjj-v7hg
~/audit/Audit.csproj : warning NU1903: Package 'NuGet.Protocol' 5.11.2 has a known high severity vulnerability, https://github.com/advisories/GHSA-6qmf-mmc7-6c2p
~/audit/Audit.csproj : warning NU1903: Package 'NuGet.Protocol' 5.11.2 has a known high severity vulnerability, https://github.com/advisories/GHSA-g3q9-xf95-8hp5
~/audit/Audit.csproj : warning NU1901: Package 'NuGet.Protocol' 5.11.2 has a known low severity vulnerability, https://github.com/advisories/GHSA-g4vj-cjjj-v7hg
Restored ~/audit/Audit.csproj (in 2.84 sec).
The documentation shows GHSA-g3q9-xf95-8hp5 as a moderate (NU1902) finding; on 2026-10-23 the advisory was rated
high, so the code is NU1903 — the codes follow the advisory’s current severity (NU1901 low, NU1902 moderate,
NU1903 high, NU1904 critical). They are warnings: with TreatWarningsAsErrors they fail the build, which is the
intent; hiding them with NoWarn defeats it (suppress a specific advisory with NuGetAuditSuppress instead). The audit
needs the sources to be reachable — an unreachable source gives NU1900, as the container example on
CI Builds and Caching
shows.
Trust: signatures and trusted signers
Every package on nuget.org carries a repository signature by nuget.org; many also carry an author signature.
dotnet nuget verify checks them, and the trustedSigners section of nuget.config, together with
signatureValidationMode="require", makes restore refuse packages not signed by a listed author or repository
(optionally restricted to a list of nuget.org owners). The commands and the full trust-policy setup are on
Signed Packages and Trust Policies.
Source: NuGet: Client trust policies.
Minimum publish age (.NET 11 preview)
A newly published malicious version is typically detected and removed within hours or days. A cooldown keeps restore
from selecting versions younger than a threshold. The .NET 11 preview SDK adds --min-publish-age-hours to
dotnet nuget add source and dotnet nuget update source, writing a minPublishAgeHours attribute to the source in
nuget.config; dotnet package add and dotnet package update respect it, and restore of a floating version fails with
NU1020 when no version is old enough. Not available in SDK 10.0.402 and not run here:
# .NET 11 preview SDK only
dotnet nuget add source https://api.nuget.org/v3/index.json --name public --min-publish-age-hours 24 --configfile nuget.config
dotnet nuget update source public --min-publish-age-hours 48 --configfile nuget.config
minPublishAgeHours and NU1020 have no reference pages yet; the what’s-new page is the only documentation.
MSBuild’s own guidance
Building a project means running its code. The MSBuild security guidance comes down to four rules:
-
Restrict write access to the MSBuild installation — files next to
MSBuild.exeand in the extension folders change every build. -
Build only sources you know and have reviewed — including opening them in Visual Studio, which evaluates the project.
-
Build in a dedicated location — MSBuild imports
Directory.Build.props/.targets/.rsp,.userfiles andbefore/aftersolution targets from every parent folder up to the drive root; on a shared agent, an emptyDirectory.Build.propsat the repository root stops the upward search. -
Know what packages contribute to the build — package
build/assets and analyzers run during restore and compilation;ExcludeAssets(or an explicitIncludeAssets) keeps the build logic of packages that do not need it out.
For publishers
-
Trusted Publishing (nuget.org, announced 2025-09-22): a CI workflow exchanges its OIDC identity for a short-lived API key, so no long-lived key is stored anywhere.
-
API keys: since 2026-08-17 nuget.org issues keys valid for at most 30 days, and keys created earlier expire on 2026-11-01. The API key pages on MS Learn still describe 365-day keys; the NuGet blog announced the change.
-
Prefix reservation stops others from publishing IDs under your prefix.
-
Deprecate, do not delete a vulnerable version: mark it deprecated with an alternative, so audits and IDEs flag it.
These are covered on Trusted Publishing, nuget.org Accounts and API Keys and Managing Published Packages.
Common mistakes
-
Several sources without source mapping — the classic dependency-confusion setup (CPM even warns,
NU1507). -
<clear />forgotten in the repository’snuget.config— machine- or user-level feeds take part in restore. -
allowInsecureConnectionson a network feed — disables the protection HTTPS-only was introduced for. -
Audit warnings hidden with
NoWarn— useNuGetAuditSuppressfor a reviewed advisory instead. -
--locked-modewithout committed lock files — there is nothing to lock against. -
Credentials committed in
nuget.config— use environment variables or a credential provider. -
Building untrusted pull requests with secrets in the environment — the PR’s build logic can read them.
Release notes
-
NuGet 6.3: HTTP sources warn (
NU1803); NuGet 6.12: they are errors (NU1302). -
NuGet 6.8 / .NET 8: NuGetAudit during restore.
-
.NET 10:
NuGetAuditModedefaults toallfornet10.0and later. -
2025-09-22: Trusted Publishing on nuget.org; 2026-08-17: 30-day maximum for API keys.
-
.NET 11 (preview): minimum publish age (
--min-publish-age-hours,NU1020).