Skip to content

Generate and publish a CycloneDX SBOM of the website bundle - #4136

Open
ppkarwasz wants to merge 2 commits into
CVEProject:devfrom
ppkarwasz:feat/sbom
Open

ppkarwasz wants to merge 2 commits into
CVEProject:devfrom
ppkarwasz:feat/sbom

Conversation

@ppkarwasz

Copy link
Copy Markdown

Closes #4135

This PR adds the generation of a CycloneDX SBOM to the Vite build. The SBOM lists the code actually shipped to the visitor's browser and is published together with the website.

Changes

  • SBOM generation: rollup-plugin-sbom collects the npm packages included in the bundle and writes a CycloneDX 1.7 SBOM to /cyclonedx/bom.json and /.well-known/sbom. Build-time dependencies are intentionally left out, since they are already covered by the GitHub Dependency Graph through package-lock.json.

  • Sass packages: Bulma is compiled into the CSS through Sass @forward, which the bundler never sees. It is added explicitly in the plugin's afterCollect hook.

  • Third-party scripts: the scripts loaded at runtime from their vendors' servers are added as components, since they run with the same privileges as the website:

    • osano.js (Osano cookie consent), a dependency of the website;
    • gtag.js (Google tag), a dependency of vue-gtag, which injects it;
    • cse.js (Google Programmable Search), a dependency of the website.

    These scripts are generated by their vendors for this website and have no version, so they are identified by their exact download URL (a pkg:generic purl with a download_url qualifier). The Google URLs are built from the same VITE_GOOGLE_ANALYTICS_PROPERTY_ID and VITE_GOOGLE_SITE_SEARCH_ENGINE_ID variables used by the website, and a script is omitted when its ID is not configured, since it is not loaded either.

  • Footer link: a "Website SBOM" link is added to the "Policies & Cookies" section of the footer.

  • .env-EXAMPLE: adds the missing VITE_LIST_SEARCH_PATH and VITE_GOOGLE_SITE_SEARCH_ENGINE_ID variables and documents each variable.

Notes

  • Deployment: the SBOM is written to dist/ together with the rest of the website, so the deployment process does not need to change.
  • File names: /cyclonedx/bom.json and /.well-known/sbom are the defaults of rollup-plugin-sbom, not a deliberate choice. I have no strong preference: both can be changed through the plugin options (outDir, outFilename and includeWellKnown) if you prefer other locations.
  • Content type: /.well-known/sbom has no file extension. If the deployment infers content types from extensions, this file should be uploaded as application/vnd.cyclonedx+json (or application/json). The footer link points to /cyclonedx/bom.json, which is not affected.
  • Build warning: the build prints Error during auto-registration of tool "rollup". This is harmless: the plugin tries to record Rollup in the SBOM metadata, but Vite 8 uses Rolldown instead. The build succeeds and the SBOM is complete.

Dependencies

  • rollup-plugin-sbom 4.0.1 (dev dependency, no install scripts).
  • @cyclonedx/cyclonedx-library 10.3.0 (dev dependency, same version as used by the plugin, to build the extra components).

Testing

  1. Run npm run build && npm run preview.
  2. Open http://localhost:4173/ and follow the "Website SBOM" link in the footer.
  3. To see the Google scripts in the SBOM, set VITE_GOOGLE_ANALYTICS_PROPERTY_ID and VITE_GOOGLE_SITE_SEARCH_ENGINE_ID in .env before building.

🤖 Generated with Claude Code

Add `rollup-plugin-sbom` to the Vite build, so every build emits a
CycloneDX SBOM of the packages actually shipped to the browser
(`/cyclonedx/bom.json` and `/.well-known/sbom`).
Build-time dependencies are intentionally left out: they are already
covered by the GitHub Dependency Graph via `package-lock.json`.

Packages pulled in through Sass (Bulma) are invisible to the bundler,
so they are added explicitly in the `afterCollect` hook.
The same hook adds the third-party scripts loaded at runtime from their
vendors' servers (Osano, Google tag, Google Programmable Search), as
unversioned components identified by their exact download URL.
The Google scripts are generated per tag/search engine ID, so their
URLs are built from the same `VITE_*` variables the website uses, and
they are omitted when the ID is not configured (the script is then not
loaded). The Google tag is recorded as a dependency of `vue-gtag`,
which injects it.

Link the SBOM from the "Policies & Cookies" section of the footer.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Add `VITE_LIST_SEARCH_PATH` and `VITE_GOOGLE_SITE_SEARCH_ENGINE_ID`,
which are used by the code (and the latter by the SBOM generation) but
were missing from the example, and describe each variable.

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish an SBOM for the JavaScript libraries shipped by cve.org

1 participant