From 99d9d6e5d7b71ca9c603779bdac9888c159f3b93 Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Wed, 4 Mar 2026 15:34:02 +0000 Subject: [PATCH 1/3] Fix review findings: SBOM cache, checksum guards, tea-sync hardening - Only cache SBOMs for github_release sources (not docker/chainguard) so tea-sync can detect image digest changes without version bumps - Add empty-input guards before sha256sum -c for crane and cosign - Add checksum verification to yq install in tea-sync.yml - Add max-parallel: 10 to tea-sync matrix to avoid Docker Hub rate limits - Add header comments to 11 new workflow files for consistency Co-Authored-By: Claude Opus 4.6 --- .github/workflows/sbom-amazonlinux.yml | 8 ++++++++ .github/workflows/sbom-builder.yml | 19 ++++++++++++------- .github/workflows/sbom-elixir.yml | 8 ++++++++ .github/workflows/sbom-erlang.yml | 8 ++++++++ .github/workflows/sbom-fedora.yml | 8 ++++++++ .github/workflows/sbom-haskell.yml | 8 ++++++++ .github/workflows/sbom-julia.yml | 8 ++++++++ .github/workflows/sbom-oraclelinux.yml | 8 ++++++++ .github/workflows/sbom-r-base.yml | 8 ++++++++ .github/workflows/sbom-rockylinux.yml | 8 ++++++++ .github/workflows/sbom-rust.yml | 8 ++++++++ .github/workflows/sbom-swift.yml | 8 ++++++++ .github/workflows/tea-sync.yml | 12 +++++++++--- 13 files changed, 109 insertions(+), 10 deletions(-) diff --git a/.github/workflows/sbom-amazonlinux.yml b/.github/workflows/sbom-amazonlinux.yml index faafcf8..4881391 100644 --- a/.github/workflows/sbom-amazonlinux.yml +++ b/.github/workflows/sbom-amazonlinux.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Amazon Linux (Docker Official) +# +# Triggers when amazonlinux config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/amazonlinux + name: "SBOM: amazonlinux" on: diff --git a/.github/workflows/sbom-builder.yml b/.github/workflows/sbom-builder.yml index 5d4f0ee..3f20ba5 100644 --- a/.github/workflows/sbom-builder.yml +++ b/.github/workflows/sbom-builder.yml @@ -60,8 +60,9 @@ jobs: CRANE_BASE="https://github.com/google/go-containerregistry/releases/download/${{ env.CRANE_VERSION }}" curl -fsSL -o /tmp/crane.tar.gz "${CRANE_BASE}/go-containerregistry_Linux_x86_64.tar.gz" curl -fsSL -o /tmp/crane_checksums.txt "${CRANE_BASE}/checksums.txt" - grep go-containerregistry_Linux_x86_64.tar.gz /tmp/crane_checksums.txt \ - | sed 's|go-containerregistry_Linux_x86_64.tar.gz|/tmp/crane.tar.gz|' \ + checkline=$(grep go-containerregistry_Linux_x86_64.tar.gz /tmp/crane_checksums.txt) + [[ -n "$checkline" ]] || { echo "crane checksum line not found"; exit 1; } + echo "$checkline" | sed 's|go-containerregistry_Linux_x86_64.tar.gz|/tmp/crane.tar.gz|' \ | sha256sum -c - tar -xzf /tmp/crane.tar.gz -C /tmp crane sudo install /tmp/crane /usr/local/bin/crane @@ -70,8 +71,9 @@ jobs: COSIGN_BASE="https://github.com/sigstore/cosign/releases/download/${{ env.COSIGN_VERSION }}" curl -fsSL -o /tmp/cosign "${COSIGN_BASE}/cosign-linux-amd64" curl -fsSL -o /tmp/cosign_checksums.txt "${COSIGN_BASE}/cosign_checksums.txt" - grep 'cosign-linux-amd64$' /tmp/cosign_checksums.txt \ - | sed 's|cosign-linux-amd64|/tmp/cosign|' \ + checkline=$(grep 'cosign-linux-amd64$' /tmp/cosign_checksums.txt) + [[ -n "$checkline" ]] || { echo "cosign checksum line not found"; exit 1; } + echo "$checkline" | sed 's|cosign-linux-amd64|/tmp/cosign|' \ | sha256sum -c - sudo install /tmp/cosign /usr/local/bin/cosign @@ -107,7 +109,7 @@ jobs: - name: Cache fetched SBOM id: sbom-cache - if: steps.config.outputs.source_type != 'lockfile' + if: steps.config.outputs.source_type == 'github_release' uses: actions/cache@v4 with: path: sbom.json @@ -135,14 +137,17 @@ jobs: maven-${{ inputs.app }}- maven- + # Always fetch for docker/chainguard (image digest may change without version bump). + # Only skip for github_release/lockfile when cache hits. - name: Fetch SBOM or lockfile if: >- - (steps.sbom-cache.outputs.cache-hit != 'true' && steps.config.outputs.source_type != 'lockfile') + (steps.sbom-cache.outputs.cache-hit != 'true' && steps.config.outputs.source_type == 'github_release') || (steps.lockfile-cache.outputs.cache-hit != 'true' && steps.config.outputs.source_type == 'lockfile') + || (steps.config.outputs.source_type != 'github_release' && steps.config.outputs.source_type != 'lockfile') run: ./scripts/fetch-sbom.sh "${{ inputs.app }}" - name: Upload input artifact - if: always() && steps.config.outputs.source_type != 'lockfile' + if: always() && hashFiles('sbom.json') != '' uses: actions/upload-artifact@v4 with: name: sbom-${{ inputs.app }}-${{ steps.config.outputs.version }} diff --git a/.github/workflows/sbom-elixir.yml b/.github/workflows/sbom-elixir.yml index faa609d..9b54d85 100644 --- a/.github/workflows/sbom-elixir.yml +++ b/.github/workflows/sbom-elixir.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Elixir (Docker Official) +# +# Triggers when elixir config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/elixir + name: "SBOM: elixir" on: diff --git a/.github/workflows/sbom-erlang.yml b/.github/workflows/sbom-erlang.yml index 0968861..adef0e7 100644 --- a/.github/workflows/sbom-erlang.yml +++ b/.github/workflows/sbom-erlang.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Erlang (Docker Official) +# +# Triggers when erlang config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/erlang + name: "SBOM: erlang" on: diff --git a/.github/workflows/sbom-fedora.yml b/.github/workflows/sbom-fedora.yml index ed3a1a4..e9d4ffe 100644 --- a/.github/workflows/sbom-fedora.yml +++ b/.github/workflows/sbom-fedora.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Fedora (Docker Official) +# +# Triggers when fedora config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/fedora + name: "SBOM: fedora" on: diff --git a/.github/workflows/sbom-haskell.yml b/.github/workflows/sbom-haskell.yml index e4d725e..7a8498c 100644 --- a/.github/workflows/sbom-haskell.yml +++ b/.github/workflows/sbom-haskell.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Haskell (Docker Official) +# +# Triggers when haskell config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/haskell + name: "SBOM: haskell" on: diff --git a/.github/workflows/sbom-julia.yml b/.github/workflows/sbom-julia.yml index 3fa09a2..0ff15c2 100644 --- a/.github/workflows/sbom-julia.yml +++ b/.github/workflows/sbom-julia.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Julia (Docker Official) +# +# Triggers when julia config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/julia + name: "SBOM: julia" on: diff --git a/.github/workflows/sbom-oraclelinux.yml b/.github/workflows/sbom-oraclelinux.yml index 78242db..5202df3 100644 --- a/.github/workflows/sbom-oraclelinux.yml +++ b/.github/workflows/sbom-oraclelinux.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Oracle Linux (Docker Official) +# +# Triggers when oraclelinux config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/oraclelinux + name: "SBOM: oraclelinux" on: diff --git a/.github/workflows/sbom-r-base.yml b/.github/workflows/sbom-r-base.yml index 12ed6f3..50d6364 100644 --- a/.github/workflows/sbom-r-base.yml +++ b/.github/workflows/sbom-r-base.yml @@ -1,3 +1,11 @@ +# SBOM workflow for R (Docker Official) +# +# Triggers when r-base config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/r-base + name: "SBOM: r-base" on: diff --git a/.github/workflows/sbom-rockylinux.yml b/.github/workflows/sbom-rockylinux.yml index b19ab91..fb596e6 100644 --- a/.github/workflows/sbom-rockylinux.yml +++ b/.github/workflows/sbom-rockylinux.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Rocky Linux (Docker Official) +# +# Triggers when rockylinux config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/rockylinux + name: "SBOM: rockylinux" on: diff --git a/.github/workflows/sbom-rust.yml b/.github/workflows/sbom-rust.yml index beeba1c..4322626 100644 --- a/.github/workflows/sbom-rust.yml +++ b/.github/workflows/sbom-rust.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Rust (Docker Official) +# +# Triggers when rust config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/rust + name: "SBOM: rust" on: diff --git a/.github/workflows/sbom-swift.yml b/.github/workflows/sbom-swift.yml index 575aa78..1173eca 100644 --- a/.github/workflows/sbom-swift.yml +++ b/.github/workflows/sbom-swift.yml @@ -1,3 +1,11 @@ +# SBOM workflow for Swift (Docker Official) +# +# Triggers when swift config is updated. +# Extracts SBOM from Docker OCI attestation (SPDX). +# Also picked up by tea-sync hourly for image digest changes. +# +# https://hub.docker.com/_/swift + name: "SBOM: swift" on: diff --git a/.github/workflows/tea-sync.yml b/.github/workflows/tea-sync.yml index 94beb5e..ce71275 100644 --- a/.github/workflows/tea-sync.yml +++ b/.github/workflows/tea-sync.yml @@ -28,9 +28,14 @@ jobs: - name: Install yq run: | - sudo curl -fsSL -o /usr/local/bin/yq \ - "https://github.com/mikefarah/yq/releases/download/${{ env.YQ_VERSION }}/yq_linux_amd64" - sudo chmod +x /usr/local/bin/yq + YQ_BASE="https://github.com/mikefarah/yq/releases/download/${{ env.YQ_VERSION }}" + curl -fsSL -o /tmp/yq "${YQ_BASE}/yq_linux_amd64" + curl -fsSL -o /tmp/yq_checksums "${YQ_BASE}/checksums-bsd" + expected=$(grep 'SHA256 (yq_linux_amd64)' /tmp/yq_checksums | awk '{print $NF}') + actual=$(sha256sum /tmp/yq | awk '{print $1}') + [[ -n "$expected" ]] || { echo "yq checksum not found"; exit 1; } + [[ "$expected" == "$actual" ]] || { echo "yq checksum mismatch"; exit 1; } + sudo install /tmp/yq /usr/local/bin/yq - name: Find eligible apps id: find-apps @@ -65,6 +70,7 @@ jobs: if: needs.detect.outputs.has_apps == 'true' strategy: fail-fast: false + max-parallel: 10 matrix: app: ${{ fromJson(needs.detect.outputs.apps) }} uses: ./.github/workflows/sbom-builder.yml From 6f32fb1a9185f4a4b0e81f812e4b1b82b48b4a36 Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Wed, 4 Mar 2026 15:36:13 +0000 Subject: [PATCH 2/3] Treat sbomify "already exists" as success during upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enrichment data from deps.dev can change between runs, producing a new SBOM hash that TEA hasn't seen. TEA says "upload", but sbomify rejects it because the component+version already has an SBOM. This is benign — the SBOM is already on the platform. Add continue-on-error to both upload steps so this doesn't fail the workflow. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/sbom-builder.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/sbom-builder.yml b/.github/workflows/sbom-builder.yml index 3f20ba5..fa7c40d 100644 --- a/.github/workflows/sbom-builder.yml +++ b/.github/workflows/sbom-builder.yml @@ -213,10 +213,14 @@ jobs: fi # Phase 3: Upload only if SBOM is new + # continue-on-error: enrichment changes can produce a new TEA hash for an + # already-uploaded component+version; sbomify returns "already exists" which + # is benign — the SBOM is already on the platform. - name: Upload SBOM (from existing SBOM) if: >- steps.config.outputs.component_id != '' && steps.config.outputs.source_type != 'lockfile' && !inputs.dry_run && steps.tea-check.outputs.should_upload == 'true' + continue-on-error: true uses: sbomify/sbomify-action@master env: TOKEN: ${{ secrets.SBOMIFY_TOKEN }} @@ -232,6 +236,7 @@ jobs: if: >- steps.config.outputs.component_id != '' && steps.config.outputs.source_type == 'lockfile' && !inputs.dry_run && steps.tea-check.outputs.should_upload == 'true' + continue-on-error: true uses: sbomify/sbomify-action@master env: TOKEN: ${{ secrets.SBOMIFY_TOKEN }} From 1c758f61f6ff2b25ed788fbb980d9e49e641b4a4 Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Wed, 4 Mar 2026 15:45:47 +0000 Subject: [PATCH 3/3] Remove continue-on-error from upload steps The root cause of the "already exists" failures was missing PURLs on sbomify products, which prevented TEA from indexing them. All 56 products now have PURLs set, so TEA dedup works correctly. The continue-on-error workaround is no longer needed. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/sbom-builder.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/sbom-builder.yml b/.github/workflows/sbom-builder.yml index fa7c40d..3f20ba5 100644 --- a/.github/workflows/sbom-builder.yml +++ b/.github/workflows/sbom-builder.yml @@ -213,14 +213,10 @@ jobs: fi # Phase 3: Upload only if SBOM is new - # continue-on-error: enrichment changes can produce a new TEA hash for an - # already-uploaded component+version; sbomify returns "already exists" which - # is benign — the SBOM is already on the platform. - name: Upload SBOM (from existing SBOM) if: >- steps.config.outputs.component_id != '' && steps.config.outputs.source_type != 'lockfile' && !inputs.dry_run && steps.tea-check.outputs.should_upload == 'true' - continue-on-error: true uses: sbomify/sbomify-action@master env: TOKEN: ${{ secrets.SBOMIFY_TOKEN }} @@ -236,7 +232,6 @@ jobs: if: >- steps.config.outputs.component_id != '' && steps.config.outputs.source_type == 'lockfile' && !inputs.dry_run && steps.tea-check.outputs.should_upload == 'true' - continue-on-error: true uses: sbomify/sbomify-action@master env: TOKEN: ${{ secrets.SBOMIFY_TOKEN }}