Generating an SBOM for PHP (Composer)
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:
composer require --dev cyclonedx/cyclonedx-php-composer composer CycloneDX:make-sbom --output-format=JSON --output-file=sbom.json
- Produces:
- sbom.json
- Format:
- CycloneDX JSON
What the generator actually reads
The plugin reads composer.lock, so the inventory is the resolved set rather than the constraints in composer.json and the lock has to be committed and current. The plugin installs as a dev dependency, which means it lands in that lock and in its own output - a good reason to exclude the development packages deliberately rather than by habit.
Versions and package URLs
Packages come out as pkg:composer/vendor/name purls, matching OSV's Packagist advisories. Platform requirements - the PHP version itself and the ext-* entries - are part of what composer resolves but are not Packagist packages, so they never turn into advisory matches even where they appear in the file.
The whole CI job
GitHub Actions
- uses: actions/checkout@v4
- run: composer require --dev cyclonedx/cyclonedx-php-composer
- run: composer CycloneDX:make-sbom --output-format=JSON --output-file=sbom.json
- uses: mmalinowski/cradesk-action@v1
with:
sbom-path: sbom.jsonGitLab CI/CD
generate-sbom:
script:
- composer require --dev cyclonedx/cyclonedx-php-composer
- composer CycloneDX:make-sbom --output-format=JSON --output-file=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
Without --output-format=JSON the plugin writes XML, which the scan detects and refuses rather than mis-parsing. Add --omit=dev to leave development packages out.
Repositories with more than one thing in them
One composer.lock is one inventory. A repository shipping several PHP applications has a lock each and each needs its own run and its own scan: there is no aggregate mode that would merge them correctly.
What CRA Desk does with the result
Components from this build carry pkg:composer package URLs, which the watch matches against OSV's Packagist 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.