Generating an SBOM for Elixir / Erlang (Mix)
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.
Add to mix.exs:
defp deps do
[{:sbom, "~> 0.6", only: :dev, runtime: false}]
endRun in CI:
mix deps.get mix sbom.cyclonedx -f -o sbom.json
- Produces:
- sbom.json
- Format:
- CycloneDX JSON
What the generator actually reads
mix.lock is the input, so the file describes the versions Mix resolved for the current project, Erlang dependencies included - but only once mix deps.get has run, because an empty lock produces an empty inventory rather than an error.
Versions and package URLs
Dependencies come out as pkg:hex purls matching OSV's Hex advisories and both Elixir and Erlang packages from Hex share that namespace - which is what makes a mixed BEAM project readable in one file. A dependency pulled from a git ref rather than from Hex has no purl of that kind and is counted as a gap.
The whole CI job
GitHub Actions
- uses: actions/checkout@v4
- run: mix deps.get
- run: mix sbom.cyclonedx -f -o sbom.json
- uses: mmalinowski/cradesk-action@v1
with:
sbom-path: sbom.jsonGitLab CI/CD
generate-sbom:
script:
- mix deps.get
- mix sbom.cyclonedx -f -o sbom.json
include:
- component: gitlab.com/cradesk/scan/readiness@1
inputs:
sbom_path: sbom.jsonOnly the first step changes with your stack - the CRA Desk step after it is identical everywhere.
What goes wrong here
The default output is XML, so -f json is not optional here. The dependency is dev-only, which keeps it out of your release.
Repositories with more than one thing in them
An umbrella project is several applications behind one lock at the root. Generating from the umbrella root gives you the union; generating inside one child application gives that application's own dependencies. Pick whichever matches what you ship and keep the choice stable so two reports stay comparable.
What CRA Desk does with the result
Components from this build carry pkg:hex package URLs, which the watch matches against OSV's Hex 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.