CRA Desk
Every generate step

Generating an SBOM for JavaScript / TypeScript (npm)

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:

npm ci
npm sbom --sbom-format cyclonedx --omit dev > sbom.json
Produces:
sbom.json
Format:
CycloneDX JSON
Generator documentation

What the generator actually reads

The inventory is the resolved dependency graph on disk, so it reflects what the lockfile actually produced on this runner - including the platform-specific optional dependencies installed here and not the ones for other platforms. An SBOM generated on a Linux runner therefore describes a Linux install: the right answer for a container image, the wrong one for a package meant to run anywhere.

Versions and package URLs

Components come out as pkg:npm purls with scoped names percent-encoded (pkg:npm/%40scope/name@1.2.3), which is exactly how OSV keys its npm advisories - so npm is one of the ecosystems where matching is near-exact rather than heuristic. Aliased and file: dependencies are the exception: they carry no registry identity, so they are counted as gaps rather than reported clean.

The whole CI job

GitHub Actions

- uses: actions/checkout@v4
- run: npm ci
- run: npm sbom --sbom-format cyclonedx --omit dev > sbom.json
- uses: mmalinowski/cradesk-action@v1
  with:
    sbom-path: sbom.json

GitLab CI/CD

generate-sbom:
  script:
    - npm ci
    - npm sbom --sbom-format cyclonedx --omit dev > 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

npm sbom needs an installed tree, so npm ci has to run first. Omitting --omit dev puts your build tooling in the inventory and every advisory against it in your report.

Repositories with more than one thing in them

In a workspace, npm ci at the root installs every package and npm sbom at the root covers all of them in one file. Running it inside a single workspace package gives that package's tree alone - a legitimate choice, one SBOM per shipped artefact, as long as the scan is pointed at each of them.

What CRA Desk does with the result

Components from this build carry pkg:npm package URLs, which the watch matches against OSV's npm 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