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 CycloneDXand runs asdotnet-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 generatewith 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.
- Compare the package count with
dotnet list package --include-transitiveon the same solution. - Open the file and find a transitive dependency you know about, such as the compression package above.
- 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
- Turn on
RestorePackagesWithLockFileand commit the lock files. - Use locked restore in CI.
- Add an SBOM generation step after build, writing to a named output folder.
- Archive the SBOM with each release artifact.
- Validate package counts against a transitive listing once per project.
- 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
- Microsoft sbom-tool repository
- CycloneDX for .NET repository
- Microsoft Learn: Package references in project files (lock file)
- Microsoft Learn: Auditing packages
Last reviewed: 18 October 2026.