CRA Desk
Every generate step

Generating an SBOM for C# / .NET (NuGet)

The step below writes a CycloneDX JSON software bill of materials (SBOM) in your own pipeline. The CRA Desk scan reads that file and turns it into a Cyber Resilience Act (CRA) readiness report - it never generates one for you.

Run in CI:

dotnet tool install --global CycloneDX
dotnet restore
dotnet CycloneDX . -o . --json --filename sbom.json
Produces:
sbom.json
Format:
CycloneDX JSON
Generator documentation

What the generator actually reads

What restore writes is a per-project assets file and that is the tool's actual input - so the SBOM describes the graph NuGet resolved, not the <PackageReference> lines you would read in the csproj. A restore that succeeded against a private feed and one that fell back to nuget.org produce different files from identical sources.

Versions and package URLs

Components come out as pkg:nuget purls, the identifier behind OSV's NuGet advisories. Multi-targeting is the thing to check: a project built for several target frameworks can resolve different package versions per framework and one flat SBOM will not tell you which framework a given version came from.

The whole CI job

GitHub Actions

- uses: actions/checkout@v4
- run: dotnet tool install --global CycloneDX
- run: dotnet restore
- run: dotnet CycloneDX . -o . --json --filename sbom.json
- uses: mmalinowski/cradesk-action@v1
  with:
    sbom-path: sbom.json

GitLab CI/CD

generate-sbom:
  script:
    - dotnet tool install --global CycloneDX
    - dotnet restore
    - dotnet CycloneDX . -o . --json --filename sbom.json

include:
  - component: gitlab.com/cradesk/scan/readiness@1
    inputs:
      sbom_path: sbom.json

Only the first step changes with your stack - the CRA Desk step after it is identical everywhere.

What goes wrong here

The tool resolves what restore put on disk, so dotnet restore has to run first; pointing it at a solution rather than a single project is what gets every shipped assembly in.

Repositories with more than one thing in them

Central Package Management moves versions out of the project files and into Directory.Packages.props. The SBOM still records the version that was resolved, which is the right answer - but it does mean the file and the csproj will not agree and the file is the one to trust.

What CRA Desk does with the result

Components from this build carry pkg:nuget package URLs, which the watch matches against OSV's NuGet advisories when the SBOM arrives and again as new advisories land. Matching runs on every plan; e-mail alerts and the full match list are what a paid plan adds.

A generator that runs is not the same as coverage. Components with no purl, no version, or from an ecosystem outside the ones we watch are counted as gaps in every report rather than reported as clean.

Other stacks