From ed5116a335ad6746685e91bce3e6fef6ed1fa1f1 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Tue, 14 Jul 2026 15:44:55 +0200 Subject: [PATCH 1/2] CI: add CycloneDX SBOM generation for releases Generate a CycloneDX Software Bill of Materials (SBOM) automatically in CI for every tagged release, attached as `grass.cyclonedx.json` alongside the tarballs in the release draft. A standalone `sbom.yml` workflow allows ad-hoc generation via `workflow_dispatch`. Local generation is available with `make sbom` (requires [Syft](https://github.com/anchore/syft)). Addresses #7215 --- .github/workflows/create_release_draft.yml | 10 ++++++ .github/workflows/sbom.yml | 39 ++++++++++++++++++++++ Makefile | 12 ++++++- SECURITY.md | 15 +++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/sbom.yml diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 3800bdda520..84fa14ecefd 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 (for tags only) + if: startsWith(github.ref, 'refs/tags/') + uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 + with: + path: . + format: cyclonedx-json + output-file: ${{ env.OUT_DIR }}/grass.cyclonedx.json + upload-artifact: false + 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.cyclonedx.json diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml new file mode 100644 index 00000000000..9e64713fd56 --- /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: ${{ inputs.source-path }} + format: cyclonedx-json + output-file: ${{ inputs.output-file }} + upload-artifact: true + artifact-name: grass-sbom diff --git a/Makefile b/Makefile index 59b32e92ae1..4e434054b85 100644 --- a/Makefile +++ b/Makefile @@ -123,6 +123,16 @@ 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.cyclonedx.json; \ + echo "SBOM written to grass.cyclonedx.json"; \ + else \ + echo "Syft not found. Install it from https://github.com/anchore/syft"; \ + exit 1; \ + fi + distclean: clean -rm -f config.cache config.log config.status config.status.$(ARCH) 2>/dev/null -rm -f ChangeLog ChangeLog.bak $(ERRORLOG) grass.pc @@ -136,5 +146,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..a96c1bf09c0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -55,4 +55,19 @@ 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.cyclonedx.json` to each GitHub release draft. +- 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! From 8ccba36f8e54d480b8788a60ce17e5bd46fa70c8 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Thu, 16 Jul 2026 12:43:47 +0200 Subject: [PATCH 2/2] file naming improvements (apply suggestions from code review) Co-authored-by: Vaclav Petras --- .github/workflows/create_release_draft.yml | 10 +++++----- .github/workflows/sbom.yml | 6 +++--- Makefile | 7 ++++--- SECURITY.md | 6 +++++- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 84fa14ecefd..46c231c0625 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -73,14 +73,14 @@ 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 (for tags only) - if: startsWith(github.ref, 'refs/tags/') + - name: Generate CycloneDX SBOM uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 with: path: . format: cyclonedx-json - output-file: ${{ env.OUT_DIR }}/grass.cyclonedx.json - upload-artifact: false + 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/') @@ -102,4 +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.cyclonedx.json + ${{ env.OUT_DIR }}/grass.cdx.json diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 9e64713fd56..b4eb501df04 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -32,8 +32,8 @@ jobs: - name: Generate CycloneDX SBOM uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 with: - path: ${{ inputs.source-path }} + path: . format: cyclonedx-json - output-file: ${{ inputs.output-file }} + artifact-name: grass.cdx.json upload-artifact: true - artifact-name: grass-sbom + upload-release-assets: false diff --git a/Makefile b/Makefile index 4e434054b85..2a264a0d8bc 100644 --- a/Makefile +++ b/Makefile @@ -126,15 +126,16 @@ code-coverage-clean: sbom: @if command -v syft > /dev/null 2>&1; then \ echo "Generating CycloneDX SBOM using Syft..."; \ - syft dir:. -o cyclonedx-json=grass.cyclonedx.json; \ - echo "SBOM written to grass.cyclonedx.json"; \ + 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"; \ + 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 diff --git a/SECURITY.md b/SECURITY.md index a96c1bf09c0..66cc2590361 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -60,7 +60,11 @@ vulnerability until we have had adequate time to provide a fix. 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.cyclonedx.json` to each GitHub release draft. + 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`.