Supply-Chain Security

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

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

flowchart LR AUTH["Package author"] -->|"push: API key or Trusted Publishing"| FEED[("Feed: nuget.org, private feed")] FEED -->|"HTTPS only (NU1302)"| REST["Restore on a build agent"] REST --> LOCK["Lock file: exact versions and content hashes"] REST --> AUDIT["NuGetAudit: known vulnerabilities (NU1901-NU1904)"] REST --> SIG["Signature verification and trusted signers"] MAP["Package source mapping: which feed may serve which ID"] --> REST PREFIX["ID prefix reservation on nuget.org"] --> FEED AGE["Minimum publish age (.NET 11 preview)"] --> REST LOCK --> BUILD["Build: package props/targets, analyzers run"] BUILD --> PUB["Publish / pack"] REVIEW["MSBuild guidance: build only reviewed sources, ExcludeAssets"] --> BUILD
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); <clear /> in nuget.config

Package Sources and Source Mapping

Typosquatting: a look-alike ID (Newtonsoft.Jsom)

Prefix reservation (a verified owner’s IDs show a check mark on nuget.org); review new dependencies

nuget.org Accounts and API Keys

Interception or tampering in transit

HTTPS-only sources (NU1302); repository and author signatures; dotnet nuget verify

Signed Packages and Trust Policies

A different package than reviewed (new transitive version, re-published content on a private feed)

packages.lock.json with content hashes and --locked-mode

Lock Files and Locked Mode

Known-vulnerable dependencies, direct or transitive

NuGetAudit during restore; dotnet package list --vulnerable

Auditing Packages for Vulnerabilities

A freshly published malicious version picked up before anyone notices

Minimum publish age (minPublishAgeHours) — .NET 11 preview

nuget.config

Malicious build logic in a package or a repository folder

Build only reviewed sources; ExcludeAssets="build;analyzers" where not needed; locked-down agent folders

PackageReference

Stolen publishing credentials

Trusted Publishing (no long-lived key); scoped, short-lived API keys

Trusted Publishing

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.

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.exe and 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, .user files and before/after solution targets from every parent folder up to the drive root; on a shared agent, an empty Directory.Build.props at 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 explicit IncludeAssets) 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.

Common mistakes

  • Several sources without source mapping — the classic dependency-confusion setup (CPM even warns, NU1507).

  • <clear /> forgotten in the repository’s nuget.config — machine- or user-level feeds take part in restore.

  • allowInsecureConnections on a network feed — disables the protection HTTPS-only was introduced for.

  • Audit warnings hidden with NoWarn — use NuGetAuditSuppress for a reviewed advisory instead.

  • --locked-mode without 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: NuGetAuditMode defaults to all for net10.0 and 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).