Safeguard
Software Supply Chain•

How do you generate an SBOM for a C# and .NET project?

Restore NuGet packages, commit a lock file, then run an SBOM generator like the CycloneDX .NET tool or sbom-tool in CI and archive the output.

Safeguard Security Team
4 min read

Restore your NuGet packages first, then run an SBOM generator against the solution: the CycloneDX .NET tool for CycloneDX output, or Microsoft's sbom-tool for SPDX. Commit packages.lock.json so the result is reproducible, run the step in CI on every release build, and store the file with the artifact.

What goes into a .NET SBOM?

An SBOM lists the components in your software and how they relate. For .NET, the interesting part is the package graph: the NuGet packages you reference directly and the ones they pull in. The files you ship and the base image matter too if you deploy a container.

A constructed example: a solution called Billing has an API project and a worker project. The API references a PDF library. That library references a font package and an XML helper, and the XML helper references a compression package. You wrote one PackageReference. Your shipped build contains four packages. The SBOM has to show all four, with exact versions.

How do you get a stable dependency list?

NuGet resolves versions at restore time, and a range can resolve differently next week. Microsoft's documentation describes an opt-in lock file: set the MSBuild property RestorePackagesWithLockFile to true, and restore writes packages.lock.json at the project root, listing all package dependencies. Commit it.

Then make CI strict. With locked mode enabled, through dotnet restore --locked-mode or the RestoreLockedMode property, restore uses exactly the packages in the lock file and fails if the project's dependencies changed after the lock file was created. Your SBOM then describes the build you tested, not whatever resolved at 3 a.m.

Which tools generate the file?

Two open source options come from primary projects:

  • CycloneDX for .NET. Its repository describes it as a tool that creates CycloneDX software bills of materials from .NET projects. It installs as a global tool with dotnet tool install --global CycloneDX and runs as dotnet-CycloneDX <path> -o <output directory>. Run it after restore so it can read the resolved packages.
  • Microsoft sbom-tool. Its repository describes SPDX 2.2 and SPDX 3.0 compatible SBOMs for a variety of artifacts. It uses Component Detection to find components and the ClearlyDefined API to fill in license information. You run sbom-tool generate with a drop path (the folder of files you ship), a build components path (usually your source folder, scanned for project files such as *.csproj), and package name, version, supplier and namespace values.

The two answer slightly different questions. CycloneDX for .NET starts from the project and its packages. The sbom-tool also hashes the files in your drop folder, which ties the SBOM to the exact bytes you shipped. Pick the format your customers or auditors ask for, and keep the choice consistent across products.

Where does it run?

In CI, after build and test, on the release pipeline. Archive the SBOM next to the binaries or image with the same version in its name. If you publish a NuGet package yourself, ship the SBOM with the build output.

How do you check the output?

Do three things before you trust it.

  1. Compare the package count with dotnet list package --include-transitive on the same solution.
  2. Open the file and find a transitive dependency you know about, such as the compression package above.
  3. Check that every component has a name, an exact version and a package URL or equivalent identifier.

NuGet's auditing documentation also describes dotnet list package --vulnerable and the NuGetAudit feature that checks restore against known vulnerabilities. Those answer a different question from an SBOM, but they pair well: the SBOM is the inventory, and the audit is one use of it.

What to do

  1. Turn on RestorePackagesWithLockFile and commit the lock files.
  2. Use locked restore in CI.
  3. Add an SBOM generation step after build, writing to a named output folder.
  4. Archive the SBOM with each release artifact.
  5. Validate package counts against a transitive listing once per project.
  6. Feed the SBOM into your vulnerability and license checks, and keep old SBOMs for released versions.

Safeguard can generate an SBOM from a repository and scan NuGet dependencies, and its CLI supports SBOM generation and scanning in CI. The free scan at gold.safeguard.sh/scan covers up to 100 dependencies in the browser.

Quick check

Why run the SBOM tool after restore?

Restore resolves the actual package versions. Generating before it can miss transitive packages or record ranges instead of exact versions.

Does the SBOM replace NuGet audit?

No. The SBOM lists what you ship. Auditing compares those packages with known vulnerabilities. Use both.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.