MSBuild targets for automatic CycloneDX SBOM (Software Bill of Materials) generation during build and pack operations. Seamlessly integrates the CycloneDX .NET tool into your build pipeline.
CycloneDX.MSBuild is a NuGet package inspired by Microsoft.Sbom.Targets that automatically generates CycloneDX SBOMs for your .NET projects. Simply add the package reference, and SBOMs are generated automatically during dotnet build or dotnet pack.
- β Zero Configuration: Works out of the box with sensible defaults
- β Security by Design: Runs in build context, no elevated permissions required
- β Multi-Target Support: Handles projects with multiple target frameworks
- β Flexible: Highly configurable via MSBuild properties
- β Standards Compliant: Generates CycloneDX 1.2-1.6 compatible SBOMs
- β Development Dependency: Doesn't pollute your dependency tree
Add the package to your project:
dotnet add package CycloneDX.MSBuildOr add it manually to your .csproj:
<ItemGroup>
<PackageReference Include="CycloneDX.MSBuild" Version="1.0.0" PrivateAssets="all" />
</ItemGroup>dotnet buildThat's it! Your SBOM will be generated at bin/Debug/net8.0/sbom.json (or your output directory).
SBOMs are generated automatically:
- During Build: After successful compilation
- During Pack: Included in NuGet packages under
/sbom/ - During Publish: Copied to the publish directory
- Multi-Targeting: Generates once per project, not per framework
- JSON (default) - CycloneDX JSON format
- XML - CycloneDX XML format
The tool automatically uses the latest CycloneDX specification version supported by the installed tool version (typically 1.6).
All configuration is done via MSBuild properties. Set them in your .csproj, Directory.Build.props, or command line.
<PropertyGroup>
<!-- Disable for specific projects -->
<GenerateCycloneDxSbom>false</GenerateCycloneDxSbom>
</PropertyGroup><PropertyGroup>
<!-- Fail build if SBOM generation fails (default: true, continues on error) -->
<CycloneDxContinueOnError>false</CycloneDxContinueOnError>
</PropertyGroup><PropertyGroup>
<!-- json (default) or xml -->
<CycloneDxOutputFormat>xml</CycloneDxOutputFormat>
</PropertyGroup><PropertyGroup>
<!-- Custom output directory -->
<CycloneDxOutputDirectory>$(MSBuildProjectDirectory)/sbom/</CycloneDxOutputDirectory>
<!-- Custom filename (without extension) -->
<CycloneDxOutputFilename>software-bom</CycloneDxOutputFilename>
</PropertyGroup><PropertyGroup>
<!-- Exclude development dependencies -->
<CycloneDxExcludeDev>true</CycloneDxExcludeDev>
<!-- Exclude test projects -->
<CycloneDxExcludeTestProjects>true</CycloneDxExcludeTestProjects>
</PropertyGroup><PropertyGroup>
<!-- Disable auto-generated SBOM serial number -->
<CycloneDxDisableSerialNumber>true</CycloneDxDisableSerialNumber>
</PropertyGroup>New in v1.1.0: CycloneDX.MSBuild now automatically extracts assembly and package metadata from your project properties and includes it in the generated SBOM. This eliminates the need for manual metadata templates in most cases.
Automatically Extracted Metadata:
- Version: From
Version,VersionPrefix,AssemblyVersion, or GitVersion properties (GitVersion_FullSemVer,GitVersion_SemVer) - Component Name: From
AssemblyNameor project name - Authors/Supplier: From
AuthorsorCompanyproperties - Description: From
DescriptionorPackageDescriptionproperties - Copyright: From
Copyrightproperty - License: From
PackageLicenseExpressionproperty - Package URL (purl): Automatically generated as
pkg:nuget/{name}@{version}
Example Project Configuration:
<PropertyGroup>
<Version>1.2.3</Version>
<Authors>Your Name</Authors>
<Company>Your Company Inc.</Company>
<Description>A brief description of your application</Description>
<Copyright>Copyright (c) 2024 Your Company Inc.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>Generated SBOM Component Metadata:
{
"component": {
"type": "application",
"bom-ref": "pkg:nuget/YourProject@1.2.3",
"name": "YourProject",
"version": "1.2.3",
"description": "A brief description of your application",
"supplier": {
"name": "Your Name"
},
"copyright": "Copyright (c) 2024 Your Company Inc.",
"licenses": [
{
"license": {
"id": "MIT"
}
}
],
"purl": "pkg:nuget/YourProject@1.2.3"
}
}Disable Automatic Metadata Generation:
If you prefer to use a manual metadata template or disable metadata generation entirely:
<PropertyGroup>
<!-- Disable automatic metadata extraction -->
<CycloneDxGenerateMetadata>false</CycloneDxGenerateMetadata>
</PropertyGroup>GitVersion Integration:
If you're using GitVersion for semantic versioning, CycloneDX.MSBuild automatically detects and uses the GitVersion-generated version:
<!-- No configuration needed - GitVersion properties are automatically detected -->
<!-- Checks GitVersion_FullSemVer and GitVersion_SemVer properties -->Note: With automatic metadata generation enabled by default (see above), manual metadata templates are typically only needed for advanced scenarios or custom metadata structures.
<PropertyGroup>
<!-- Import metadata from template file (overrides automatic generation) -->
<CycloneDxImportMetadataPath>$(MSBuildProjectDirectory)/sbom-metadata.xml</CycloneDxImportMetadataPath>
</PropertyGroup>When you specify a custom metadata template path, automatic metadata generation is skipped, and your template is used instead. This is useful for:
- Adding custom component information beyond standard properties
- Including additional metadata fields not extracted automatically
- Providing complex metadata structures
Example sbom-metadata.xml:
Note: CycloneDX.MSBuild supports metadata templates using CycloneDX schema versions 1.2 through 1.6. You may use any supported version, but 1.6 is recommended for new projects.
<?xml version="1.0" encoding="utf-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6">
<metadata>
<component type="application" bom-ref="pkg:nuget/YourProject@1.0.0">
<name>YourProject</name>
<version>1.0.0</version>
<description>
<![CDATA[Your project description]]>
</description>
<licenses>
<license>
<name>Apache License 2.0</name>
<id>Apache-2.0</id>
</license>
</licenses>
<purl>pkg:nuget/YourProject@1.0.0</purl>
</component>
</metadata>
</bom><PropertyGroup>
<!-- Enable GitHub license resolution -->
<CycloneDxEnableGitHubLicenses>true</CycloneDxEnableGitHubLicenses>
<!-- GitHub credentials (optional, for higher rate limits) -->
<CycloneDxGitHubUsername>your-username</CycloneDxGitHubUsername>
<CycloneDxGitHubToken>ghp_yourtoken</CycloneDxGitHubToken>
</PropertyGroup><PropertyGroup>
<!-- Pin specific CycloneDX tool version -->
<CycloneDxToolVersion>5.5.0</CycloneDxToolVersion>
</PropertyGroup>When you run dotnet pack, the generated SBOM is automatically included in the NuGet package under the /sbom/ directory.
dotnet packConsumers of your NuGet package can inspect the SBOM:
# Extract and view
unzip -q MyPackage.1.0.0.nupkg -d extracted
cat extracted/sbom/sbom.jsonWhen you run dotnet publish, the generated SBOM is automatically copied to the publish directory alongside your application.
dotnet publish -c ReleaseThe SBOM will be available in your publish directory:
# View the SBOM in publish directory
cat bin/Release/net8.0/publish/sbom.jsonThis makes it easy to include the SBOM when deploying your application, ensuring supply chain transparency in production environments.
CycloneDX.MSBuild uses MSBuild's extensibility hooks:
.propsfile - Defines configurable properties with defaults.targetsfile - Implements build targets for SBOM generationbuildMultiTargeting/- Special handling for multi-target projects
Build β ValidateCycloneDxConfiguration β EnsureCycloneDxToolInstalled β GenerateCycloneDxSbom
- Sandboxed Execution: Runs in build context without elevated privileges
- Input Validation: All properties are validated before use
- Fail-Safe Defaults: Continues build on errors by default
- No Code Execution: Only executes vetted CycloneDX tool
- Dependency Pinning: Explicit tool version control
- Separation of Concerns: Configuration (
.props) separated from logic (.targets) - Single Responsibility: Each target has one clear purpose
- DRY: Reusable property groups
- Consistent Naming:
CycloneDx*prefix for all properties - Comprehensive Documentation: XML comments for all properties
The repository includes integration tests for various scenarios:
tests/
βββ SimpleProject/ # Basic single-target project
βββ MultiTargetProject/ # Multi-targeting (.NET 6, 8, Standard 2.0)
βββ DisabledProject/ # Project with SBOM generation disabled
This project uses automated semantic versioning with:
- MinVer: Automatic version calculation from Git tags
- semantic-release: Automated releases, changelogs, and NuGet publishing
All commits must follow Conventional Commits format.
π For detailed information, see VERSIONING.md
This project includes a comprehensive CI/CD pipeline with automated testing and publishing:
Runs on all pull requests and feature branches:
- β Multi-platform testing (Ubuntu, Windows, macOS)
- β Multi-version .NET testing (.NET 6.0 and 8.0)
- β Code quality checks (formatting, package validation)
- β Dependency security scanning (vulnerable and deprecated packages)
- β Build artifact generation
Runs on pushes to main, master, beta, or alpha branches:
- Build & Test - Full test suite execution
- Package Validation - NuGet package quality checks
- Semantic Release - Automatic version determination from commits
- NuGet Publishing - Automatic publishing to NuGet.org
- GitHub Release - Automated release notes and changelog
To enable automated NuGet publishing:
- Get a NuGet API key from NuGet.org
- Add it as a GitHub secret named
NUGET_API_KEY - The release workflow will automatically publish on version bumps
π For detailed CI/CD documentation, see .github/workflows/README.md
CycloneDX.MSBuild/
βββ src/
β βββ CycloneDX.MSBuild/
β βββ build/
β β βββ CycloneDX.MSBuild.props # Configuration properties
β β βββ CycloneDX.MSBuild.targets # Build integration
β βββ buildMultiTargeting/ # Multi-target support
β βββ CycloneDX.MSBuild.csproj # Package definition
βββ tests/
β βββ Integration.Tests/
βββ README.md
# Build the package
dotnet build src/CycloneDX.MSBuild/CycloneDX.MSBuild.csproj
# Pack the package
dotnet pack src/CycloneDX.MSBuild/CycloneDX.MSBuild.csproj
# Test with local projects
dotnet build tests/Integration.Tests/SimpleProject/SimpleProject.csproj| Feature | Microsoft.Sbom.Targets | CycloneDX.MSBuild |
|---|---|---|
| SBOM Format | SPDX 2.2 | CycloneDX 1.2-1.6 |
| Tool | Microsoft.Sbom.Tool (embedded) | CycloneDX .NET tool (local tool, installed on demand) |
| Build Support | β | β |
| Pack Support | β | β |
| Publish Support | β | β |
| Multi-Targeting | β | β |
| Development Dependency | β | β |
Problem: SBOM file is not created after build.
Solutions:
-
Check if generation is enabled:
dotnet build -p:GenerateCycloneDxSbom=true -v:detailed
Look for "GenerateCycloneDxSbom" messages in the output.
-
Verify the package is installed:
dotnet list package | grep CycloneDX.MSBuild -
Check for multi-target projects: For projects with multiple target frameworks, the SBOM is only generated once at the outer build level. Check the project root or custom output directory.
-
Look in the correct location: Default is
bin/[Configuration]/[TargetFramework]/sbom.json. Check yourCycloneDxOutputDirectorysetting if customized. -
Check build verbosity: Run with detailed verbosity to see what's happening:
dotnet build -v:detailed 2>&1 | grep -i cyclonedx
Problem: CycloneDX tool fails to install or is not found.
Solutions:
-
Manually install the tool:
dotnet tool install --local CycloneDX --version 5.5.0
-
Clear tool cache and retry:
dotnet tool uninstall --local CycloneDX dotnet build
-
Check network connectivity: Tool installation requires internet access to download from NuGet.org.
-
Verify .NET SDK version: Ensure you have .NET SDK 6.0 or later installed:
dotnet --version
-
Check for proxy/firewall issues: If behind a corporate proxy, configure NuGet:
dotnet nuget update source nuget.org --http-proxy http://proxy.company.com:8080
Problem: GitHub API rate limiting or authentication errors.
Solutions:
-
Rate limiting (60 requests/hour without auth):
- Use GitHub token for higher limits (5000 requests/hour)
- Set
CycloneDxGitHubUsernameandCycloneDxGitHubTokenproperties
-
Generate a GitHub token:
- Go to GitHub Settings β Developer settings β Personal access tokens
- Create token with
public_reposcope (read-only) - Store securely (use environment variables, not source control)
-
Use environment variables in CI/CD:
dotnet build -p:CycloneDxGitHubToken=$GITHUB_TOKEN -
Disable if not needed:
<CycloneDxEnableGitHubLicenses>false</CycloneDxEnableGitHubLicenses>
Problem: Build time significantly increased after adding CycloneDX.MSBuild.
Solutions:
-
Disable for Debug builds (only generate for Release):
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <GenerateCycloneDxSbom>false</GenerateCycloneDxSbom> </PropertyGroup>
-
Use custom output directory to avoid file locking issues:
<CycloneDxOutputDirectory>$(MSBuildProjectDirectory)/sbom/</CycloneDxOutputDirectory>
-
Exclude test projects from SBOM generation:
<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('.Tests'))"> <GenerateCycloneDxSbom>false</GenerateCycloneDxSbom> </PropertyGroup>
-
Pin tool version to avoid repeated downloads:
<CycloneDxToolVersion>5.5.0</CycloneDxToolVersion>
Problem: Multiple SBOMs generated or wrong output location for multi-target projects.
Solution: This is expected behavior. CycloneDX.MSBuild automatically detects multi-target projects and generates a single SBOM at the outer build level. If you're seeing multiple SBOMs, check:
-
Verify buildMultiTargeting files are imported: The package should automatically handle this.
-
Check output location: SBOM should be in the project directory or custom output directory, not in individual target framework folders.
-
Clean and rebuild:
dotnet clean dotnet build
Problem: SBOM has incorrect dependencies, versions, or metadata.
Solutions:
-
Ensure dependencies are restored:
dotnet restore dotnet build
-
Check for package version conflicts: Look at the build output for version resolution messages.
-
Use metadata template to override project information:
<CycloneDxImportMetadataPath>sbom-metadata.xml</CycloneDxImportMetadataPath>
-
Verify tool version: Ensure you're using a recent version of the CycloneDX tool:
<CycloneDxToolVersion>5.5.0</CycloneDxToolVersion>
-
Clean package cache and rebuild:
dotnet nuget locals all --clear dotnet restore dotnet build
Problem: SBOM generation works locally but fails in CI/CD.
Solutions:
-
Ensure .NET SDK is available: CI environment must have .NET SDK 6.0+ installed.
-
Check for file permissions: CI systems may have restricted file access. Use:
dotnet build -p:CycloneDxContinueOnError=true
-
Network access required: Tool installation needs internet access to NuGet.org.
-
Use explicit restore:
dotnet restore dotnet tool restore dotnet build
-
Cache the tool in CI to improve performance:
# GitHub Actions example - uses: actions/cache@v3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
Problem: Generated SBOM has parsing errors or invalid format.
Solutions:
-
Validate the SBOM: Use CycloneDX validation tools:
# Install validator npm install -g @cyclonedx/cyclonedx-cli # Validate SBOM cyclonedx validate --input-file sbom.json
-
Check tool version: Older versions may have bugs. Update to latest:
<CycloneDxToolVersion>5.5.0</CycloneDxToolVersion>
-
Try different format: Switch between JSON and XML to isolate issues:
<CycloneDxOutputFormat>xml</CycloneDxOutputFormat>
-
Check for special characters: Ensure project/package names don't have invalid XML/JSON characters.
dotnet build -v:detailed > build.log 2>&1
grep -i cyclonedx build.logdotnet tool list --localShould show:
Package Id Version Commands
--------------------------------------------------------
CycloneDX 5.5.0 dotnet-CycloneDX
dotnet build -v:diagnostic 2>&1 | grep "CycloneDX.MSBuild.targets"Should show the targets file being imported.
Create a simple test project:
dotnet new console -n TestSbom
cd TestSbom
dotnet add package CycloneDX.MSBuild
dotnet build
ls bin/Debug/net8.0/sbom.jsonQ: Does SBOM generation require internet access? A: Yes, the first build requires internet to download the CycloneDX tool from NuGet.org. Subsequent builds use the cached tool. GitHub license resolution (if enabled) also requires internet access.
Q: Can I use this with .NET Framework projects? A: The package targets netstandard2.0, so it works with .NET Framework 4.6.1+ and all versions of .NET Core/.NET 5+. However, you need .NET SDK 6.0+ installed to run the CycloneDX tool.
Q: Does this work with mono or alternative .NET implementations? A: The package should work with any MSBuild-compatible build system that supports .NET tools.
Q: How do I exclude the SBOM from version control?
A: Add to .gitignore:
# CycloneDX SBOM files
**/bin/**/sbom.json
**/bin/**/sbom.xml
sbom/Q: Can I validate the SBOM automatically during build? A: Not built-in yet, but you can add a custom MSBuild target that runs validation after SBOM generation. This is a planned feature.
Q: Does the SBOM include transitive dependencies? A: Yes, the CycloneDX tool includes all transitive dependencies by default.
Q: How do I report security vulnerabilities? A: See SECURITY.md for our security policy and reporting process.
Q: What's the performance impact? A: SBOM generation typically adds 2-5 seconds to build time, depending on project size and number of dependencies. Consider disabling for Debug builds if needed.
If you're still experiencing issues:
- Check existing issues: GitHub Issues
- Review CycloneDX tool docs: cyclonedx-dotnet
- Ask in discussions: GitHub Discussions
- File a bug report: Include build logs, project configuration, and environment details
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
- Follow existing code style and architecture
- Maintain security by design principles
- Add tests for new features
- Update documentation
This project is licensed under the MIT License - see the LICENSE file for details.
- CycloneDX .NET Tool - The underlying SBOM generation tool
- CycloneDX Specification - CycloneDX standard
- Microsoft.Sbom.Targets - Microsoft's SPDX SBOM tool
- OWASP Dependency-Track - SBOM analysis platform
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- CycloneDX Community: https://cyclonedx.org/
Made with β€οΈ for Software Supply Chain Security