diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 3800bdda520..46c231c0625 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -73,6 +73,15 @@ jobs: xz -9e "${GRASS}.tar" md5sum "${GRASS}.tar.xz" > "${GRASS}.tar.xz.md5" sha256sum "${GRASS}.tar.xz" > "${GRASS}.tar.xz.sha256" + - name: Generate CycloneDX SBOM + uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + with: + path: . + format: cyclonedx-json + output-file: ${{ env.OUT_DIR }}/grass.cdx.json + artifact-name: grass.cdx.json + upload-artifact: true + upload-release-assets: false - name: Publish draft distribution to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 @@ -93,3 +102,4 @@ jobs: ${{ env.OUT_DIR }}/${{ env.GRASS }}.tar.xz.sha256 ${{ env.OUT_DIR }}/ChangeLog.gz ${{ env.OUT_DIR }}/core_modules_with_last_commit.patch + ${{ env.OUT_DIR }}/grass.cdx.json diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 00000000000..b4eb501df04 --- /dev/null +++ b/.github/workflows/sbom.yml @@ -0,0 +1,39 @@ +--- +name: Generate SBOM + +on: + workflow_dispatch: + inputs: + source-path: + description: Path to scan for SBOM generation + required: false + default: '.' + output-file: + description: Output SBOM file name + required: false + default: 'grass.cyclonedx.json' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + generate: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Generate CycloneDX SBOM + uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + with: + path: . + format: cyclonedx-json + artifact-name: grass.cdx.json + upload-artifact: true + upload-release-assets: false diff --git a/Makefile b/Makefile index 59b32e92ae1..2a264a0d8bc 100644 --- a/Makefile +++ b/Makefile @@ -123,8 +123,19 @@ code-coverage-clean: -find . -type f \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete -rm -f .coverage +sbom: + @if command -v syft > /dev/null 2>&1; then \ + echo "Generating CycloneDX SBOM using Syft..."; \ + syft dir:. -o cyclonedx-json=grass.cdx.json; \ + echo "SBOM written to grass.cdx.json"; \ + else \ + echo "Syft not found. Install it from https://github.com/anchore/syft" >&2; \ + exit 1; \ + fi + distclean: clean -rm -f config.cache config.log config.status config.status.$(ARCH) 2>/dev/null + -rm -f grass.cdx.json -rm -f ChangeLog ChangeLog.bak $(ERRORLOG) grass.pc -rm -f include/grass/config.h include/grass/version.h -rm -f include/Make/Platform.make include/Make/Doxyfile_arch_html include/Make/Doxyfile_arch_latex 2>/dev/null @@ -136,5 +147,5 @@ include $(MODULE_TOPDIR)/include/Make/Sphinx.make DOXNAME=grass -.PHONY: default libs +.PHONY: default libs sbom .PHONY: cleandistdirs cleanscriptstrings clean libsclean distclean diff --git a/SECURITY.md b/SECURITY.md index d960978e29b..66cc2590361 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -55,4 +55,23 @@ in allowing us time to address any reported vulnerabilities before disclosing them publicly. We ask that you refrain from disclosing any details of the vulnerability until we have had adequate time to provide a fix. +## Software Bill of Materials (SBOM) + +GRASS publishes a CycloneDX SBOM with every official release. + +- The SBOM is generated automatically in CI via [Syft](https://github.com/anchore/syft) + and attached as `grass.cdx.json` to each GitHub release draft. + It covers dependencies declared in the source tree (Python package + requirements); components linked at build time (GDAL, PROJ, GEOS, + etc.) are resolved only in built artifacts and appear in the Docker + image SBOMs where they are installed from OS package repositories. +- For Docker images, an SBOM is embedded at build time (`sbom: true` in + `docker/build-push-action`) and attestations are published via + `actions/attest-build-provenance`. +- To generate an SBOM locally, install Syft and run `make sbom` from the + repository root. +- SBOM generation can also be triggered manually via the + [Generate SBOM](https://github.com/OSGeo/grass/actions/workflows/sbom.yml) + workflow on GitHub Actions. + Thank you for helping to keep GRASS secure!