From affceabe4b5e2f07a3b340f43fadf27acb8108ab Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:26:26 +0100 Subject: [PATCH 01/10] Add reusable SBOM and provenance generation workflow CycloneDX SBOMs for .NET backend and npm frontend dependencies, plus SLSA-style build provenance manifest with checksums. Implements OPS-11 (#103). --- .../workflows/reusable-sbom-provenance.yml | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 .github/workflows/reusable-sbom-provenance.yml diff --git a/.github/workflows/reusable-sbom-provenance.yml b/.github/workflows/reusable-sbom-provenance.yml new file mode 100644 index 000000000..d95b06368 --- /dev/null +++ b/.github/workflows/reusable-sbom-provenance.yml @@ -0,0 +1,250 @@ +# ============================================================================= +# Reusable SBOM and Release Provenance +# +# Generates CycloneDX SBOMs for backend (.NET) and frontend (npm) dependencies, +# plus a build provenance manifest capturing build environment metadata. +# +# Implements: OPS-11 (#103) +# ============================================================================= + +name: Reusable SBOM and Provenance + +on: + workflow_call: + inputs: + dotnet-version: + required: false + type: string + default: 8.0.x + node-version: + required: false + type: string + default: 24.13.1 + artifact-name: + required: false + type: string + default: sbom-provenance-artifacts + retention-days: + required: false + type: number + default: 90 + fail-on-error: + description: Fail the workflow if SBOM generation encounters errors + required: false + type: boolean + default: false + +permissions: + contents: read + +jobs: + sbom-provenance: + name: SBOM and Provenance + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + SBOM_DIR: ${{ github.workspace }}/artifacts/sbom-provenance + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: ${{ inputs.dotnet-version }} + cache: true + cache-dependency-path: | + backend/Taskdeck.sln + backend/**/*.csproj + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: ${{ inputs.node-version }} + cache: npm + cache-dependency-path: frontend/taskdeck-web/package-lock.json + + - name: Create output directory + run: mkdir -p "$SBOM_DIR" + + - name: Restore backend dependencies + run: dotnet restore backend/Taskdeck.sln + + - name: Install CycloneDX .NET tool + run: dotnet tool install --global CycloneDX + + - name: Generate backend SBOM (CycloneDX) + id: backend_sbom + shell: bash + run: | + set +e + dotnet CycloneDX backend/Taskdeck.sln \ + --output "$SBOM_DIR" \ + --filename backend-sbom.json \ + --json \ + --include-project-references \ + --set-type application \ + --set-name "Taskdeck-Backend" \ + --set-version "${{ github.ref_name }}" 2> "$SBOM_DIR/backend-sbom.stderr" + exit_code=$? + printf '%s' "$exit_code" > "$SBOM_DIR/backend-sbom.exitcode" + if [ "$exit_code" -ne 0 ]; then + echo "::warning::Backend SBOM generation exited with code $exit_code" + else + echo "Backend SBOM generated successfully" + fi + exit "$exit_code" + continue-on-error: true + + - name: Install frontend dependencies + working-directory: frontend/taskdeck-web + run: npm ci + + - name: Generate frontend SBOM (CycloneDX) + id: frontend_sbom + shell: bash + run: | + set +e + npx --yes @cyclonedx/cyclonedx-npm \ + --output-file "$SBOM_DIR/frontend-sbom.json" \ + --spec-version 1.5 \ + --output-reproducible \ + --omit dev \ + --mc-type application \ + frontend/taskdeck-web 2> "$SBOM_DIR/frontend-sbom.stderr" + exit_code=$? + printf '%s' "$exit_code" > "$SBOM_DIR/frontend-sbom.exitcode" + if [ "$exit_code" -ne 0 ]; then + echo "::warning::Frontend SBOM generation exited with code $exit_code" + else + echo "Frontend SBOM generated successfully" + fi + exit "$exit_code" + continue-on-error: true + + - name: Generate build provenance manifest + shell: bash + run: | + cat > "$SBOM_DIR/build-provenance.json" < checksums.sha256 2>/dev/null || true + echo "Checksums:" + cat checksums.sha256 + + - name: Write SBOM summary + shell: bash + run: | + backend_exit=$(cat "$SBOM_DIR/backend-sbom.exitcode" 2>/dev/null || echo "N/A") + frontend_exit=$(cat "$SBOM_DIR/frontend-sbom.exitcode" 2>/dev/null || echo "N/A") + + backend_status="passed" + frontend_status="passed" + if [ "$backend_exit" != "0" ]; then backend_status="failed (exit $backend_exit)"; fi + if [ "$frontend_exit" != "0" ]; then frontend_status="failed (exit $frontend_exit)"; fi + + cat <> "$GITHUB_STEP_SUMMARY" + ## SBOM and Release Provenance + + | Artifact | Format | Status | + |----------|--------|--------| + | Backend SBOM | CycloneDX JSON | $backend_status | + | Frontend SBOM | CycloneDX JSON | $frontend_status | + | Build Provenance | SLSA v1 JSON | generated | + | Checksums | SHA-256 | generated | + + **Ref:** \`${{ github.ref }}\` | **SHA:** \`${{ github.sha }}\` | **Run:** \`${{ github.run_id }}\` + + Artifacts are uploaded as \`${{ inputs.artifact-name }}\` with ${{ inputs.retention-days }}-day retention. + + Policy: see \`docs/ops/SBOM_RELEASE_PROVENANCE.md\` + EOF + + - name: Enforce SBOM generation success + if: inputs.fail-on-error + shell: bash + run: | + backend_exit=$(cat "$SBOM_DIR/backend-sbom.exitcode" 2>/dev/null || echo "1") + frontend_exit=$(cat "$SBOM_DIR/frontend-sbom.exitcode" 2>/dev/null || echo "1") + if [ "$backend_exit" != "0" ] || [ "$frontend_exit" != "0" ]; then + echo "::error::SBOM generation failed. Backend exit: $backend_exit, Frontend exit: $frontend_exit" + echo "Review stderr artifacts for details." + exit 1 + fi + + - name: Upload SBOM and provenance artifacts + if: always() + uses: actions/upload-artifact@v7 + with: + name: ${{ inputs.artifact-name }} + path: ${{ env.SBOM_DIR }} + if-no-files-found: warn + retention-days: ${{ inputs.retention-days }} From a796a81be6f692274836f9aa02897f0e75bed963 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:26:58 +0100 Subject: [PATCH 02/10] Wire SBOM/provenance into ci-release.yml replacing placeholder The release build verification workflow now calls reusable-sbom-provenance.yml for real SBOM generation instead of the previous placeholder summary. --- .github/workflows/ci-release.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index b995f10ba..d1c4f4516 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -1,10 +1,10 @@ # ============================================================================= -# CI Release — release lane for SBOM/provenance placeholder and container +# CI Release — release lane for SBOM/provenance generation and container # build verification. Complements release-security.yml with a lighter-weight # build-verification pass suitable for tag/release automation. # +# SBOM/provenance: implemented in OPS-11 (#103) via reusable-sbom-provenance.yml # Follow-through: -# - OPS-11 (#103): SBOM/provenance generation and attestation policy # - SEC-09 (#106) / OPS-17 (#148): dependency vulnerability policy + automation # ============================================================================= @@ -76,15 +76,16 @@ jobs: - Backend: restore + build (Release) passed - Frontend: npm ci + vite build passed - ### SBOM / Provenance (placeholder) - - SBOM generation and provenance attestation are not yet wired. - Follow-through tracked in: - - `#103` OPS-11 SBOM/provenance workflow - - `#106` SEC-09 dependency vulnerability policy - - `#148` OPS-17 dependency update automation + SBOM and provenance artifacts are generated in the `sbom-provenance` job. EOF + sbom-provenance: + name: SBOM and Release Provenance + uses: ./.github/workflows/reusable-sbom-provenance.yml + with: + artifact-name: release-sbom-provenance + retention-days: 90 + container-images: name: Container Image Artifacts uses: ./.github/workflows/reusable-container-images.yml From 3c12556737cf6f30d2908dcf1bce20c49263bcf6 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:37:49 +0100 Subject: [PATCH 03/10] Add SBOM/provenance job to release-security.yml The release security workflow now also generates SBOM artifacts alongside the existing dependency inventory and vulnerability scans. --- .github/workflows/release-security.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-security.yml b/.github/workflows/release-security.yml index c0053d200..a2f4f6001 100644 --- a/.github/workflows/release-security.yml +++ b/.github/workflows/release-security.yml @@ -1,8 +1,9 @@ # ============================================================================= -# Release Security — deep dependency inventory and vulnerability signal lane. -# Runs on tag push, release publish, and manual dispatch. +# Release Security — deep dependency inventory, vulnerability signal, and SBOM +# generation lane. Runs on tag push, release publish, and manual dispatch. # For lighter release build verification, see ci-release.yml. # Topology context documented in ci-required.yml header. +# SBOM/provenance: OPS-11 (#103) via reusable-sbom-provenance.yml # ============================================================================= name: Release Security @@ -126,8 +127,9 @@ jobs: - container image artifact generation and checksum output via reusable container workflow Planned follow-through: - - OPS-11 (`#103`): SBOM/provenance generation and attestation policy hardening - SEC-09 (`#106`) / OPS-17 (`#148`): dependency vulnerability policy + automation tightening + + SBOM and provenance artifacts are generated in the `sbom-provenance` job. EOF - name: Upload dependency and security artifacts @@ -138,6 +140,13 @@ jobs: path: /tmp/release-security if-no-files-found: ignore + sbom-provenance: + name: SBOM and Release Provenance + uses: ./.github/workflows/reusable-sbom-provenance.yml + with: + artifact-name: release-security-sbom-provenance + retention-days: 90 + container-images: name: Container Image Artifacts uses: ./.github/workflows/reusable-container-images.yml From 64ccd813c6be30d652ed37a50cff94e9d75f5b55 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:38:06 +0100 Subject: [PATCH 04/10] Update workflow topology comment in ci-required.yml Reflect that SBOM/provenance is now implemented via reusable-sbom-provenance.yml in both release lanes. --- .github/workflows/ci-required.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-required.yml b/.github/workflows/ci-required.yml index 425fe1b82..8e9bd1514 100644 --- a/.github/workflows/ci-required.yml +++ b/.github/workflows/ci-required.yml @@ -37,11 +37,12 @@ # # ci-release.yml release lane (tag/release/manual) # ├── release build verification (backend + frontend) -# ├── SBOM/provenance placeholder (follow-through: #103, #106, #148) +# ├── reusable-sbom-provenance.yml (CycloneDX SBOM + SLSA provenance, #103) # └── reusable-container-images.yml # # release-security.yml release security deep scan (tag/release/manual) # ├── dependency inventory + vulnerability scan + enforcement +# ├── reusable-sbom-provenance.yml (CycloneDX SBOM + SLSA provenance, #103) # └── reusable-container-images.yml # # pages-frontend.yml GitHub Pages deploy (main push, frontend paths) From 301c0e7cc8c9492c7ac8f49e171baa4b0aa5102d Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:38:45 +0100 Subject: [PATCH 05/10] Add SBOM and release provenance documentation Covers format choices, workflow integration, retention policy, failure handling, and security review process for OPS-11 (#103). --- docs/ops/SBOM_RELEASE_PROVENANCE.md | 107 ++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 docs/ops/SBOM_RELEASE_PROVENANCE.md diff --git a/docs/ops/SBOM_RELEASE_PROVENANCE.md b/docs/ops/SBOM_RELEASE_PROVENANCE.md new file mode 100644 index 000000000..7d1bc4a8a --- /dev/null +++ b/docs/ops/SBOM_RELEASE_PROVENANCE.md @@ -0,0 +1,107 @@ +# SBOM Generation and Release Provenance + +Last Updated: 2026-03-30 +Owner: Taskdeck maintainers +Linked issue: `#103` (OPS-11) + +## Purpose + +This document defines the SBOM (Software Bill of Materials) generation and release provenance policy for Taskdeck. It covers: + +- What SBOM artifacts are generated and in what format +- When and how provenance metadata is captured +- Artifact retention and access policy +- Failure handling and review process + +## SBOM Format + +Taskdeck uses the [CycloneDX](https://cyclonedx.org/) format (JSON, spec version 1.5) for all SBOMs. CycloneDX is an OWASP project and an ISO standard (ISO/IEC 27036) widely supported by vulnerability scanners and dependency analysis tools. + +### Backend SBOM + +- **Tool:** `CycloneDX` .NET global tool (`dotnet CycloneDX`) +- **Input:** `backend/Taskdeck.sln` (all projects and transitive dependencies) +- **Output:** `backend-sbom.json` (CycloneDX JSON) +- **Scope:** All NuGet packages including transitive dependencies; project references included + +### Frontend SBOM + +- **Tool:** `@cyclonedx/cyclonedx-npm` (npx, latest) +- **Input:** `frontend/taskdeck-web` (production dependencies only, dev omitted) +- **Output:** `frontend-sbom.json` (CycloneDX JSON) +- **Scope:** All npm production packages including transitive dependencies + +## Build Provenance + +A SLSA (Supply-chain Levels for Software Artifacts) v1 provenance manifest is generated alongside the SBOMs. This manifest captures: + +- **Subject:** Repository name and ref/tag +- **Source digest:** Git SHA at build time +- **Builder identity:** GitHub Actions run ID and URL +- **Build inputs:** Resolved dependency reference (git URI + SHA) +- **Byproducts:** References to generated SBOM files +- **Tool versions:** .NET SDK, Node.js, CycloneDX tool versions +- **Invocation metadata:** Run ID, run number, attempt, actor, event name + +The provenance manifest is stored as `build-provenance.json`. + +All artifacts include a `checksums.sha256` file with SHA-256 digests for integrity verification. + +## Workflow Integration + +SBOM and provenance generation is implemented as a reusable GitHub Actions workflow: + +- **Reusable workflow:** `.github/workflows/reusable-sbom-provenance.yml` +- **Called by:** + - `ci-release.yml` -- on tag push, release publish, and manual dispatch + - `release-security.yml` -- on tag push, release publish, and manual dispatch + +### Trigger Matrix + +| Trigger | Workflow | SBOM Generated | +|---------|----------|----------------| +| Tag push (`v*`) | ci-release, release-security | Yes | +| Release published | ci-release, release-security | Yes | +| Manual dispatch | ci-release, release-security | Yes | +| PR merge | ci-required | No (not in critical path) | +| Nightly | nightly-quality | No (dependency signals only) | + +## Artifact Retention + +| Artifact | Retention | Location | +|----------|-----------|----------| +| Backend SBOM | 90 days | GitHub Actions artifact | +| Frontend SBOM | 90 days | GitHub Actions artifact | +| Build Provenance | 90 days | GitHub Actions artifact | +| Checksums | 90 days | GitHub Actions artifact | +| Stderr logs | 90 days | GitHub Actions artifact | + +The 90-day retention aligns with typical audit and compliance review windows. For release tags, artifacts should be downloaded and archived in long-term storage before expiry if required by compliance policy. + +## Failure Handling + +- SBOM generation steps use `continue-on-error: true` by default so that a single ecosystem failure does not block the entire release pipeline +- Exit codes and stderr are captured as artifacts for post-mortem review +- The `fail-on-error` input can be set to `true` to enforce strict SBOM generation success (recommended for production release gates once tooling is proven stable) +- The workflow summary step always reports generation status in the GitHub Actions step summary + +## Security Review Process + +When reviewing dependencies for a release: + +1. Check the SBOM artifacts from the release workflow run +2. Cross-reference with the dependency vulnerability report from `release-security.yml` +3. Any component in the SBOM with a known vulnerability should be triaged per `docs/security/SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md` +4. The provenance manifest confirms the build was produced from the expected source commit + +## Permissions + +The SBOM workflow uses `contents: read` permission only. No elevated permissions (write, packages, attestations) are required for the current implementation. If GitHub artifact attestation signing is added in the future, `id-token: write` and `attestations: write` permissions will be needed. + +## Future Enhancements + +- **Artifact attestation signing:** Use GitHub's `actions/attest-build-provenance` for cryptographic attestation once the workflow is proven stable +- **Container image SBOM:** Generate SBOMs for the Docker container images (e.g., using Syft/Grype) +- **SBOM diff on PR:** Compare SBOM between releases to surface new/removed/changed dependencies +- **Long-term archival:** Integrate with a dedicated artifact store for compliance retention beyond 90 days +- **SLSA Level 3:** Full SLSA Level 3 compliance with hermetic builds and non-forgeable provenance From a9bb4084bde060f5f3003833c02d30c2945e4cfd Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:49:41 +0100 Subject: [PATCH 06/10] Reference SBOM outputs in dependency vulnerability policy Security review process now points to SBOM/provenance artifacts generated by the reusable-sbom-provenance workflow. --- .../SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/security/SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md b/docs/security/SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md index 46ab9fa93..9da6ed312 100644 --- a/docs/security/SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md +++ b/docs/security/SECURITY_DEPENDENCY_VULNERABILITY_POLICY.md @@ -19,10 +19,11 @@ Related active security docs: - `docs/security/SECURITY_OWASP_BASELINE.md` - `docs/security/SECURITY_LOGGING_REDACTION.md` +- `docs/ops/SBOM_RELEASE_PROVENANCE.md` (SBOM generation and release provenance policy) ## Scan Sources -Taskdeck currently relies on three complementary signals: +Taskdeck currently relies on four complementary signals: - GitHub dependency review on pull requests (`.github/workflows/ci-extended.yml`) - catches manifest/lockfile diff risk before merge @@ -31,6 +32,10 @@ Taskdeck currently relies on three complementary signals: - frontend: `npm audit --json --audit-level=high` - release inventory/security lane (`.github/workflows/release-security.yml`) - captures release-time dependency inventories plus vulnerability signal artifacts +- SBOM and provenance lane (`.github/workflows/reusable-sbom-provenance.yml`) + - generates CycloneDX SBOMs for backend and frontend ecosystems + - produces SLSA-style build provenance manifest with checksums + - called by both `ci-release.yml` and `release-security.yml` on tag/release/manual triggers ## Cadence @@ -106,6 +111,13 @@ Each dependency-security run should publish: - normalized markdown summary (`summary.md`) - normalized machine-readable aggregate (`summary.json`) +Release runs additionally publish (via SBOM/provenance lane): + +- backend CycloneDX SBOM (`backend-sbom.json`) +- frontend CycloneDX SBOM (`frontend-sbom.json`) +- SLSA build provenance manifest (`build-provenance.json`) +- SHA-256 checksums for all artifacts (`checksums.sha256`) + The summary must include: - workflow context (`ci-extended`, `nightly-quality`, `release-security`) From fc4d457d11b22f9775425c7398ef05efc075160f Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 22:50:47 +0100 Subject: [PATCH 07/10] Fix provenance manifest generation to produce clean JSON Use Node.js script with environment variables instead of a bash heredoc to avoid indentation artifacts in the JSON output and prevent GitHub expression injection in shell context. --- .../workflows/reusable-sbom-provenance.yml | 114 +++++++++--------- 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/.github/workflows/reusable-sbom-provenance.yml b/.github/workflows/reusable-sbom-provenance.yml index d95b06368..26d5db9a5 100644 --- a/.github/workflows/reusable-sbom-provenance.yml +++ b/.github/workflows/reusable-sbom-provenance.yml @@ -125,72 +125,70 @@ jobs: - name: Generate build provenance manifest shell: bash + env: + PROVENANCE_REF_NAME: ${{ github.ref_name }} + PROVENANCE_REPO: ${{ github.repository }} + PROVENANCE_REF: ${{ github.ref }} + PROVENANCE_SHA: ${{ github.sha }} + PROVENANCE_WORKFLOW: ${{ github.workflow }} + PROVENANCE_RUN_ID: ${{ github.run_id }} + PROVENANCE_RUN_NUMBER: ${{ github.run_number }} + PROVENANCE_RUN_ATTEMPT: ${{ github.run_attempt }} + PROVENANCE_ACTOR: ${{ github.actor }} + PROVENANCE_EVENT: ${{ github.event_name }} + PROVENANCE_DOTNET: ${{ inputs.dotnet-version }} + PROVENANCE_NODE: ${{ inputs.node-version }} run: | - cat > "$SBOM_DIR/build-provenance.json" < Date: Mon, 30 Mar 2026 23:01:47 +0100 Subject: [PATCH 08/10] Fix frontend SBOM CLI flags to use --package-dir Replace positional directory argument and undocumented flags with the correct --package-dir flag per cyclonedx-npm docs. --- .github/workflows/reusable-sbom-provenance.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/reusable-sbom-provenance.yml b/.github/workflows/reusable-sbom-provenance.yml index 26d5db9a5..875d1bfa3 100644 --- a/.github/workflows/reusable-sbom-provenance.yml +++ b/.github/workflows/reusable-sbom-provenance.yml @@ -109,10 +109,8 @@ jobs: npx --yes @cyclonedx/cyclonedx-npm \ --output-file "$SBOM_DIR/frontend-sbom.json" \ --spec-version 1.5 \ - --output-reproducible \ --omit dev \ - --mc-type application \ - frontend/taskdeck-web 2> "$SBOM_DIR/frontend-sbom.stderr" + --package-dir frontend/taskdeck-web 2> "$SBOM_DIR/frontend-sbom.stderr" exit_code=$? printf '%s' "$exit_code" > "$SBOM_DIR/frontend-sbom.exitcode" if [ "$exit_code" -ne 0 ]; then From 3c8685228d8066ff25c7288cf426ebe45ea016ad Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 23:38:45 +0100 Subject: [PATCH 09/10] Fix shellcheck SC2035: prefix glob with ./ in checksum step Prevents filenames starting with dashes from being interpreted as options to sha256sum. --- .github/workflows/reusable-sbom-provenance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-sbom-provenance.yml b/.github/workflows/reusable-sbom-provenance.yml index 875d1bfa3..2f2375edd 100644 --- a/.github/workflows/reusable-sbom-provenance.yml +++ b/.github/workflows/reusable-sbom-provenance.yml @@ -192,7 +192,7 @@ jobs: shell: bash run: | cd "$SBOM_DIR" - sha256sum *.json > checksums.sha256 2>/dev/null || true + sha256sum ./*.json > checksums.sha256 2>/dev/null || true echo "Checksums:" cat checksums.sha256 From ccd4958c448772428c78c6949be6966cba58a315 Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Mon, 30 Mar 2026 23:42:53 +0100 Subject: [PATCH 10/10] Move GitHub expressions from run: to env: in SBOM workflow Prevents shell injection via crafted tag names. github.ref_name and other expressions are now passed through env vars instead of being interpolated directly into shell commands. --- .github/workflows/reusable-sbom-provenance.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reusable-sbom-provenance.yml b/.github/workflows/reusable-sbom-provenance.yml index 2f2375edd..caeb90fb8 100644 --- a/.github/workflows/reusable-sbom-provenance.yml +++ b/.github/workflows/reusable-sbom-provenance.yml @@ -77,6 +77,8 @@ jobs: - name: Generate backend SBOM (CycloneDX) id: backend_sbom shell: bash + env: + SBOM_VERSION: ${{ github.ref_name }} run: | set +e dotnet CycloneDX backend/Taskdeck.sln \ @@ -86,7 +88,7 @@ jobs: --include-project-references \ --set-type application \ --set-name "Taskdeck-Backend" \ - --set-version "${{ github.ref_name }}" 2> "$SBOM_DIR/backend-sbom.stderr" + --set-version "$SBOM_VERSION" 2> "$SBOM_DIR/backend-sbom.stderr" exit_code=$? printf '%s' "$exit_code" > "$SBOM_DIR/backend-sbom.exitcode" if [ "$exit_code" -ne 0 ]; then @@ -198,6 +200,12 @@ jobs: - name: Write SBOM summary shell: bash + env: + SUMMARY_REF: ${{ github.ref }} + SUMMARY_SHA: ${{ github.sha }} + SUMMARY_RUN_ID: ${{ github.run_id }} + SUMMARY_ARTIFACT_NAME: ${{ inputs.artifact-name }} + SUMMARY_RETENTION: ${{ inputs.retention-days }} run: | backend_exit=$(cat "$SBOM_DIR/backend-sbom.exitcode" 2>/dev/null || echo "N/A") frontend_exit=$(cat "$SBOM_DIR/frontend-sbom.exitcode" 2>/dev/null || echo "N/A") @@ -217,9 +225,9 @@ jobs: | Build Provenance | SLSA v1 JSON | generated | | Checksums | SHA-256 | generated | - **Ref:** \`${{ github.ref }}\` | **SHA:** \`${{ github.sha }}\` | **Run:** \`${{ github.run_id }}\` + **Ref:** \`$SUMMARY_REF\` | **SHA:** \`$SUMMARY_SHA\` | **Run:** \`$SUMMARY_RUN_ID\` - Artifacts are uploaded as \`${{ inputs.artifact-name }}\` with ${{ inputs.retention-days }}-day retention. + Artifacts are uploaded as \`$SUMMARY_ARTIFACT_NAME\` with $SUMMARY_RETENTION-day retention. Policy: see \`docs/ops/SBOM_RELEASE_PROVENANCE.md\` EOF