@cyclonedx/cyclonedx-esbuild
Version:
Creates CycloneDX Software Bill of Materials (SBoM) from esbuild projects
208 lines (159 loc) • 9.79 kB
Markdown
# CycloneDX SBOM generator for _esbuild_
[![shield_npm-version]][link_npm]
[![shield_gh-workflow-test]][link_gh-workflow-test]
[![shield_coverage]][link_codacy]
[![shield_ossf-best-practices]][link_ossf-best-practices]
[![shield_license]][license_file]
[![shield_website]][link_website]
[![shield_slack]][link_slack]
[![shield_groups]][link_discussion]
[![shield_twitter-follow]][link_twitter]
----
Create _[CycloneDX]_ Software Bill of Materials (SBOM) from _[esbuild]_ projects.
This package provides both an **[esbuild plugin](#esbuild-plugin)** and a **[CLI tool](#cli-tool)** to generate CycloneDX SBOMs from your builds.
This tooling uses the linkages generated by _esbuild_ to create <!-- a dependency graph -- not yet -- see https://github.com/CycloneDX/cyclonedx-esbuild/issues/11 --> an inventory which only contain the dependencies that are actually used (after [tree-shaking](https://esbuild.github.io/api/#tree-shaking)).
## Features
* 🔌 **esbuild plugin** for automatic SBOM generation during builds
* 🖥️ **CLI tool** for generating SBOMs from esbuild metafiles
* 🎯 Supports multiple **CycloneDX spec versions** (1.2, 1.3, 1.4, 1.5, 1.6, 1.7)
* 🔍 Extracts components and dependencies from bundled projects
* 📝 **License evidence gathering** (experimental)
* ✅ **Validates** generated SBOMs against CycloneDX schema
* 🔄 **Reproducible output** option for consistent SBOM generation
* 📊 Works with **TypeScript**, **Angular**, and other modern frameworks
## Requirements
* _Node.js_ `>= 20.18`
* _esbuild_ (required when using the plugin; not required for CLI-only usage)
## Installing
Use your preferred package manager and install as a dev-dependency:
```shell
npm install --save-dev @cyclonedx/cyclonedx-esbuild
pnpm add --save-dev @cyclonedx/cyclonedx-esbuild
yarn add --dev @cyclonedx/cyclonedx-esbuild
```
## Usage
### Esbuild Plugin
The _esbuild_ plugin automatically generates an SBOM during your build process.
#### Esbuild Plugin Options & Configuration
<!-- the following table is based on `src/plugin.ts`::`CycloneDxEsbuildPluginOptions` -->
| Name | Type | Default | Description |
|:-----|:----:|:-------:|:------------|
| **`specVersion`** | `{string}`<br/> one of: `"1.2"`, `"1.3"`, `"1.4"`, `"1.5"`, `"1.6"`, `"1.7"` | `"1.6"` | Which version of [CycloneDX-spec] to use.<br/> Supported values depend on the installed dependency [CycloneDX-javascript-library]. |
| **outputFile** | `{string}` | `"bom.json"` | Path to the output file. |
| **gatherLicenseTexts** | `boolean` | `false` | Search for license files in components and include them as license evidence.<br/> This feature is experimental. |
| **outputReproducible** | `{boolean}` | `false` | Whether to go the extra mile and make the output reproducible.<br/> This requires more resources, and might result in loss of time- and random-based-values. |
| **mcType** | `{string}` | `"application"` | Set the MainComponent's type.<br/> See [list of valid values](https://cyclonedx.org/docs/1.7/json/#metadata_component_type). |
| **validate** | `{boolean \| undefined}` | `undefined` | Validate resulting BOM before outputting.<br/> Validation is skipped, if requirements not met. |
#### Esbuild Plugin Example
```javascript
const esbuild = require('esbuild');
const { cyclonedxEsbuildPlugin } = require('@cyclonedx/cyclonedx-esbuild');
/** @type {import('@cyclonedx/cyclonedx-esbuild').CycloneDxEsbuildPluginOptions} */
const cyclonedxEsbuildPluginOptions = {
specVersion: '1.7',
outputFile: 'bom.json'
}
esbuild.build({
// ...
plugins: [
cyclonedxEsbuildPlugin(cyclonedxEsbuildPluginOptions),
]
});
```
See extended [examples].
### CLI Tool
The Command Line Interface for generating SBOMs from [esbuild metafiles](https://esbuild.github.io/api/#metafile).
#### CLI Call
Calling the CLI depends on the used install method.
Here are examples for the various package managers and setups:
```shell
npm exec -- cyclonedx-esbuild --help
pnpm exec cyclonedx-esbuild --help
yarn exec cyclonedx-esbuild --help
```
#### CLI Help Page
```text
Usage: cyclonedx-esbuild [options] <metafile>
Create CycloneDX Software Bill of Materials (SBOM) from esbuild metafile.
Arguments:
metafile Path to esbuild metafile
Options:
--ewd, --esbuild-working-dir Working dir used in the esbuild process.
--gather-license-texts Search for license files in components and include them as license evidence.
This feature is experimental.
(default: false)
--sv, --spec-version <version> Which version of CycloneDX spec to use.
(choices: "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", default: "1.6")
--output-reproducible Whether to go the extra mile and make the output reproducible.
This requires more resources, and might result in loss of time- and random-based-values.
(env: BOM_REPRODUCIBLE)
-o, --output-file <file> Path to the output file.
Set to "-" to write to STDOUT.
(default: write to STDOUT)
--validate Validate resulting BOM before outputting.
Validation is skipped, if requirements not met. See the README.
--no-validate Disable validation of resulting BOM.
--mc-type <type> Type of the main component.
(choices: "application", "firmware", "library", default: "application")
-v, --verbose Increase the verbosity of messages.
Use multiple times to increase the verbosity even more.
-V, --version output the version number
-h, --help display help for command
```
### Use with Angular
For _Angular_ projects using _esbuild_ (Angular 17+), you can generate SBOMs from the build stats.
```jsonc
// package.json
{
"scripts": {
"build:app": "ng build --stats-json",
"build:sbom": "cyclonedx-esbuild --gather-license-texts --output-reproducible -o dist/bom.json dist/stats.json",
"build": "npm run build:app && npm run build:sbom"
}
}
```
See an example here: [integration with Angular20](https://github.com/CycloneDX/cyclonedx-esbuild/tree/1.0-dev/tests/_testbeds/angular20-yarn).
## Internals
This tooling utilizes the [CycloneDX library][CycloneDX-javascript-library] to generate the actual data structures.
Besides the class `CycloneDxEsbuildPlugin` and the interface `CycloneDxEsbuildPluginOptions`,
this _esbuild_ plugin and this tool does **not** expose any additional _public_ API or classes - all code is intended to be internal and might change without any notice during version upgrades.
However, the CLI is stable - you may call it programmatically like:
```javascript
const { execFileSync } = require('child_process')
const { constants: { MAX_LENGTH: BUFFER_MAX_LENGTH } } = require('buffer')
const sbom = JSON.parse(execFileSync(process.execPath, [
'../path/to/this/package/bin/cyclonedx-exbuild-cli.js',
'--spec-version', '1.7',
'--output-file', '-'
// additional CLI args
], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'buffer', maxBuffer: BUFFER_MAX_LENGTH }))
```
## Development & Contributing
Feel free to open issues, bug reports or pull requests.
See the [CONTRIBUTING][contributing_file] file for details.
## License
Permission to modify and redistribute is granted under the terms of the Apache 2.0 license.
See the [LICENSE][license_file] file for the full license.
[license_file]: https://github.com/CycloneDX/cyclonedx-esbuild/blob/1.0-dev/LICENSE
[contributing_file]: https://github.com/CycloneDX/cyclonedx-esbuild/blob/1.0-dev/CONTRIBUTING.md
[examples]: https://github.com/CycloneDX/cyclonedx-esbuild/tree/1.0-dev/examples
[CycloneDX]: https://cyclonedx.org/
[esbuild]: https://esbuild.github.io
[CycloneDX-javascript-library]: https://github.com/CycloneDX/cyclonedx-javascript-library/
[shield_gh-workflow-test]: https://img.shields.io/github/actions/workflow/status/CycloneDX/cyclonedx-esbuild/nodejs.yml?branch=1.0-dev&logo=GitHub&logoColor=white "tests"
[shield_npm-version]: https://img.shields.io/npm/v/%40cyclonedx%2fcyclonedx-esbuild/latest?label=npm&logo=npm&logoColor=white "npm"
[shield_ossf-best-practices]: https://img.shields.io/cii/percentage/11463?label=OpenSSF%20best%20practices "OpenSSF best practices"
[shield_coverage]: https://img.shields.io/codacy/coverage/4900a38bdc544b2283695447e9513ab5?logo=Codacy&logoColor=white "test coverage"
[shield_license]: https://img.shields.io/github/license/CycloneDX/cyclonedx-esbuild?logo=open%20source%20initiative&logoColor=white "license"
[shield_website]: https://img.shields.io/badge/https://-cyclonedx.org-blue.svg "homepage"
[shield_slack]: https://img.shields.io/badge/slack-join-blue?logo=Slack&logoColor=white "slack join"
[shield_groups]: https://img.shields.io/badge/discussion-groups.io-blue.svg "groups discussion"
[shield_twitter-follow]: https://img.shields.io/badge/Twitter-follow-blue?logo=Twitter&logoColor=white "twitter follow"
[link_website]: https://cyclonedx.org/
[link_gh-workflow-test]: https://github.com/CycloneDX/cyclonedx-esbuild/actions/workflows/nodejs.yml?query=branch%3A1.0-dev
[link_codacy]: https://app.codacy.com/gh/CycloneDX/cyclonedx-esbuild/dashboard
[link_ossf-best-practices]: https://www.bestpractices.dev/projects/11463
[link_npm]: https://www.npmjs.com/package/@cyclonedx/cyclonedx-esbuild
[link_slack]: https://cyclonedx.org/slack/invite
[link_discussion]: https://groups.io/g/CycloneDX
[link_twitter]: https://twitter.com/CycloneDX_Spec