From 3b708856f9b31b4515802a0ed678c78c881dbf54 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 11:29:10 +0200 Subject: [PATCH 01/10] feat(makefile): Add `sbom` target to generate Software Bill of Materials in CycloneDX 1.5 format --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index 5f0dbb55..f2e322d7 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ help: @echo ' verify - Run a bunch of checks, to see if there are any obvious deficiencies in the code ' @echo ' verifyformat - Check formatting only ' @echo ' show-outdated - Show outdated packages (depth 1)' + @echo ' sbom - Generate Software Bill of Materials (SBOM) in CycloneDX 1.5 format' @echo ' start-mock-ca - Start the mock CA server, so that it can listens to requests ' @echo ' test-mock-ca - Run the test against the mock CA server ' @echo ' test-mock-ca-verbose - Run all tests against the mock CA server ' @@ -108,6 +109,12 @@ verifyformat: show-outdated: uv tree --outdated --depth 1 +sbom: + uv sync --all-extras + uv lock --locked + uv export --format cyclonedx1.5 --locked -o sbom.cdx.json + @echo "SBOM generated: sbom.cdx.json" + dryrun: robot --dryrun --pythonpath=./ --variable environment:$(env) tests tests_pq_and_hybrid tests_mock_ca From 711aaa9903d0a2267c11d5e70e03297b38140b1d Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 11:29:49 +0200 Subject: [PATCH 02/10] feat(ci): Add GitHub workflow to generate CycloneDX 1.5 SBOM on push to main --- .github/workflows/generate_sbom.yml | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/generate_sbom.yml diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml new file mode 100644 index 00000000..66738ac0 --- /dev/null +++ b/.github/workflows/generate_sbom.yml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Copyright 2025 Siemens AG +# +# SPDX-License-Identifier: Apache-2.0 + +name: Generate SBOM + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + # Generate a docker image URL where all the characters are lower-case, for situations where an external contributor + # has a username that begins with a capital letter. This is a limitation of GHCR, which only allows lower-case + # characters. Subsequent jobs use this URL. + prepare_env: + runs-on: ubuntu-24.04 + outputs: + image_url: ${{ steps.setenv.outputs.image_url }} + steps: + - name: Set lowercase image name + id: setenv + run: | + OWNER_LC=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + echo "image_url=ghcr.io/${OWNER_LC}/cmp-test-dev:latest" >> $GITHUB_OUTPUT + + generate_sbom: + needs: prepare_env + name: Generate CycloneDX SBOM + runs-on: ubuntu-24.04 + container: + image: ${{ needs.prepare_env.outputs.image_url }} + steps: + - name: Checkout code + uses: actions/checkout@v6.0.3 + + - name: Ensure lockfile is up to date + run: uv lock --locked + + - name: Generate SBOM in CycloneDX 1.5 format + run: uv export --format cyclonedx1.5 --locked -o sbom.cdx.json + + - name: Upload SBOM artifact + uses: actions/upload-artifact@v7.0.1 + with: + name: sbom-cdx + path: sbom.cdx.json + retention-days: 30 From 89ae96b94b13b199c9072fc0f78a635b9039fa56 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 11:30:33 +0200 Subject: [PATCH 03/10] feat(ci): Trigger SBOM generation workflow on pull request to main --- .github/workflows/generate_sbom.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index 66738ac0..debbe639 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -8,6 +8,7 @@ on: push: branches: - main + pull_request: workflow_dispatch: jobs: From 060c4648bd178dcd86270877cbf99d6a06312317 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 11:32:35 +0200 Subject: [PATCH 04/10] fix(ci): Remove outdated `--locked` flag from SBOM workflow --- .github/workflows/generate_sbom.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index debbe639..11639d43 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -37,7 +37,7 @@ jobs: uses: actions/checkout@v6.0.3 - name: Ensure lockfile is up to date - run: uv lock --locked + run: uv lock - name: Generate SBOM in CycloneDX 1.5 format run: uv export --format cyclonedx1.5 --locked -o sbom.cdx.json From 31e8b6cb97d8f3ea7e05a9b3376ece1fd052c53f Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 11:55:12 +0200 Subject: [PATCH 05/10] fix(ci): Update SBOM workflow to use correct checkout version and remove lockfile step --- .github/workflows/generate_sbom.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index 11639d43..c007f26e 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -34,10 +34,7 @@ jobs: image: ${{ needs.prepare_env.outputs.image_url }} steps: - name: Checkout code - uses: actions/checkout@v6.0.3 - - - name: Ensure lockfile is up to date - run: uv lock + uses: actions/checkout@v6 - name: Generate SBOM in CycloneDX 1.5 format run: uv export --format cyclonedx1.5 --locked -o sbom.cdx.json From bdfe91c368dd855e8284b145a2996a367d5ea523 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 12:12:51 +0200 Subject: [PATCH 06/10] fix(ci): Update SBOM workflow to fix output path and adjust `uv` command options --- .github/workflows/generate_sbom.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index c007f26e..7e0ed461 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -33,15 +33,12 @@ jobs: container: image: ${{ needs.prepare_env.outputs.image_url }} steps: - - name: Checkout code - uses: actions/checkout@v6 - - name: Generate SBOM in CycloneDX 1.5 format - run: uv export --format cyclonedx1.5 --locked -o sbom.cdx.json + run: uv --project /app export --format cyclonedx1.5 --locked -o /tmp/sbom.cdx.json - name: Upload SBOM artifact uses: actions/upload-artifact@v7.0.1 with: name: sbom-cdx - path: sbom.cdx.json + path: /tmp/sbom.cdx.json retention-days: 30 From 34908dd37693fd1d88efdbf5c6866dd832e5bdbd Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Tue, 16 Jun 2026 12:24:06 +0200 Subject: [PATCH 07/10] fix(ci): Add path filters for SBOM workflow triggers --- .github/workflows/generate_sbom.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index 7e0ed461..8e4ae5f5 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -8,7 +8,13 @@ on: push: branches: - main + paths: + - pyproject.toml + - .github/workflows/generate_sbom.yml pull_request: + paths: + - pyproject.toml + - .github/workflows/generate_sbom.yml workflow_dispatch: jobs: From 3a447a7471c401cf3c5b1b3eee280d9ec728d220 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Wed, 17 Jun 2026 09:58:13 +0200 Subject: [PATCH 08/10] feat(ci): Enhance SBOM workflow to include commit back and release attachment steps --- .github/workflows/generate_sbom.yml | 92 +++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index 8e4ae5f5..2c65a22e 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -2,8 +2,14 @@ # # SPDX-License-Identifier: Apache-2.0 +# Generates a CycloneDX SBOM from the locked dependencies and publishes it so it +# can always be fetched for analysis: on push to main the SBOM is committed back +# to the repository, and on a published release it is attached as a release asset. + name: Generate SBOM +# Triggered when the dependencies (pyproject.toml) or this workflow change, on +# every published release, and manually. on: push: branches: @@ -15,8 +21,19 @@ on: paths: - pyproject.toml - .github/workflows/generate_sbom.yml + release: + types: [published] workflow_dispatch: +permissions: + contents: read + +# Serialize runs per ref so two commits to main can't race their git push. +# Releases and PRs use separate groups; a run is never cancelled mid push. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: # Generate a docker image URL where all the characters are lower-case, for situations where an external contributor # has a username that begins with a capital letter. This is a limitation of GHCR, which only allows lower-case @@ -28,9 +45,12 @@ jobs: steps: - name: Set lowercase image name id: setenv + # Pass via env, not inline ${{ }}, so the value can't be parsed as shell. + env: + REPO_OWNER: ${{ github.repository_owner }} run: | - OWNER_LC=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - echo "image_url=ghcr.io/${OWNER_LC}/cmp-test-dev:latest" >> $GITHUB_OUTPUT + OWNER_LC=$(echo "$REPO_OWNER" | tr '[:upper:]' '[:lower:]') + echo "image_url=ghcr.io/${OWNER_LC}/cmp-test-dev:latest" >> "$GITHUB_OUTPUT" generate_sbom: needs: prepare_env @@ -39,12 +59,76 @@ jobs: container: image: ${{ needs.prepare_env.outputs.image_url }} steps: + - uses: actions/checkout@v6.0.3 + with: + # Read-only job: don't leave the token in git config for later steps. + persist-credentials: false + - name: Generate SBOM in CycloneDX 1.5 format - run: uv --project /app export --format cyclonedx1.5 --locked -o /tmp/sbom.cdx.json + run: uv --project "$GITHUB_WORKSPACE" export --format cyclonedx1.5 --locked -o "$GITHUB_WORKSPACE/sbom.cdx.json" - name: Upload SBOM artifact uses: actions/upload-artifact@v7.0.1 with: name: sbom-cdx - path: /tmp/sbom.cdx.json + path: ${{ github.workspace }}/sbom.cdx.json retention-days: 30 + + # Only on direct pushes to main, never from PRs/forks (which get a read-only + # token), so the committed-back SBOM always reflects merged dependencies. + commit_sbom: + needs: generate_sbom + name: Commit SBOM to main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - uses: actions/checkout@v6.0.3 + with: + ref: main + + - name: Download SBOM artifact + uses: actions/download-artifact@v8.0.1 + with: + name: sbom-cdx + path: . + + - name: Commit and push updated SBOM + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add sbom.cdx.json + if git diff --staged --quiet; then + echo "SBOM unchanged, nothing to commit" + else + # [skip ci] keeps the bot commit from re-triggering CI. + git commit -m "chore: update SBOM [skip ci]" + # Rebase in case main advanced since checkout; only this job edits + # the SBOM, so the rebase can't conflict. + git pull --rebase origin main + git push + fi + + # Only on published releases; attaches the SBOM to that release's assets. + release_sbom: + needs: generate_sbom + name: Attach SBOM to release + if: github.event_name == 'release' + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - name: Download SBOM artifact + uses: actions/download-artifact@v8.0.1 + with: + name: sbom-cdx + path: . + + - name: Upload SBOM to GitHub release + # Pass the tag via env, not inline ${{ }}, to avoid shell injection; + # --clobber lets re-runs replace the asset instead of failing. + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: gh release upload "$TAG" sbom.cdx.json --clobber --repo "$GITHUB_REPOSITORY" From afb30c1dfb4a2b2e10f7ee404e68c3d4998858d0 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Wed, 17 Jun 2026 09:58:44 +0200 Subject: [PATCH 09/10] fix(reuse): Add `sbom.cdx.json` to excluded files list in REUSE configuration --- REUSE.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/REUSE.toml b/REUSE.toml index 5f69520b..844bfae7 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -15,6 +15,7 @@ path = [ "scripts/**", "pq_logic/fips/frodokem.py", "pyrightconfig.json", "VERSION", "client_tests/certs/**", + "sbom.cdx.json" ] precedence = "override" SPDX-FileCopyrightText = "Copyright 2024 Siemens AG" From df1f12412207573258dd59dcfae71889b129a76c Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Wed, 17 Jun 2026 12:03:35 +0200 Subject: [PATCH 10/10] fix(ci): Adjust SBOM workflow to reuse lockfile from container image --- .github/workflows/generate_sbom.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate_sbom.yml b/.github/workflows/generate_sbom.yml index 2c65a22e..69c1c81e 100644 --- a/.github/workflows/generate_sbom.yml +++ b/.github/workflows/generate_sbom.yml @@ -65,7 +65,12 @@ jobs: persist-credentials: false - name: Generate SBOM in CycloneDX 1.5 format - run: uv --project "$GITHUB_WORKSPACE" export --format cyclonedx1.5 --locked -o "$GITHUB_WORKSPACE/sbom.cdx.json" + # The container image already resolves and locks the dependencies at build + # time (data/dockerfiles/Dockerfile.dev: `WORKDIR /app` + `uv sync`, which + # writes /app/uv.lock). The checked-out workspace has no committed uv.lock, + # so point --project at /app to reuse the image's lockfile. Output still + # goes to the workspace so the upload-artifact step can find it. + run: uv --project /app export --format cyclonedx1.5 --locked -o "$GITHUB_WORKSPACE/sbom.cdx.json" - name: Upload SBOM artifact uses: actions/upload-artifact@v7.0.1