CRA Desk
Every generate step

Generating an SBOM for Any stack (cdxgen)

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:

npx @cyclonedx/cdxgen -o sbom.json .
Produces:
sbom.json
Format:
CycloneDX JSON
Generator documentation

What the generator actually reads

cdxgen walks the repository and fires one detector per language it recognises, so what lands in the file depends on which manifests and lockfiles are present the moment it runs. A bare checkout gives it declared dependencies; a checkout after the build gives it resolved ones. Restrict it with -t when the repository also contains something you do not ship.

Versions and package URLs

Because several detectors can fire in a single run, one file can carry pkg:npm, pkg:maven and pkg:pypi components side by side - that is the point of this recipe and also its risk. A detector that found only a manifest emits ranges rather than pinned versions and a component with no resolved version cannot be matched against an advisory.

The whole CI job

GitHub Actions

- uses: actions/checkout@v4
- run: npx @cyclonedx/cdxgen -o sbom.json .
- uses: mmalinowski/cradesk-action@v1
  with:
    sbom-path: sbom.json

GitLab CI/CD

generate-sbom:
  script:
    - npx @cyclonedx/cdxgen -o 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

cdxgen auto-detects most stacks, which is also its weakness: a repository holding several will report whatever it recognises, so check the component count matches what you expect.

Repositories with more than one thing in them

A monorepo is where this recipe earns its place: one run covers every language in it and one file carries them all. The cost is that the file then describes the repository rather than a product - if you ship one service out of six, the readiness report has to say which components belong to it.

What CRA Desk does with the result

cdxgen reports whatever stacks it recognises, so coverage follows the package URLs it emits: components landing in one of the watched ecosystems are matched against OSV and the rest are counted as gaps rather than reported as clean.

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