Public Access
generated from Philipp/template-git
Philipp/Global-Workflows: Pre-commit Lint (Scoped) / pre-commit (pull_request) Successful in 36s
204 lines
8.4 KiB
YAML
204 lines
8.4 KiB
YAML
name: Trivy Multi-Arch CVE Delta Comparison
|
|
description: Downloads and verifies Trivy, extracts multi-platform SBOMs, and computes a consolidated set-based CVE delta report
|
|
|
|
inputs:
|
|
head-image:
|
|
description: Head container image reference (e.g. host/server/gitea:git-12345678)
|
|
required: true
|
|
target-image:
|
|
description: Target baseline container image reference (e.g. host/server/gitea:latest)
|
|
required: true
|
|
target-branch:
|
|
description: Target branch name for delta heading (e.g. main)
|
|
required: false
|
|
default: main
|
|
output-delta:
|
|
description: Output markdown file for CVE delta report
|
|
required: false
|
|
default: cve-delta.md
|
|
output-summary:
|
|
description: Output markdown file for full vulnerability summary
|
|
required: false
|
|
default: cve-summary.md
|
|
write-summary:
|
|
description: Whether to append the generated reports to the job summary ($GITHUB_STEP_SUMMARY)
|
|
required: false
|
|
default: 'true'
|
|
|
|
outputs:
|
|
delta-report:
|
|
description: Path to CVE delta markdown report
|
|
value: ${{ inputs.output-delta }}
|
|
summary-report:
|
|
description: Path to full vulnerability markdown report
|
|
value: ${{ inputs.output-summary }}
|
|
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- name: Prepare Cache Keys and Environment
|
|
id: vars
|
|
shell: bash
|
|
env:
|
|
TRIVY_VERSION: 0.74.0 # trivy-version
|
|
run: |
|
|
echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
|
|
echo "version=${TRIVY_VERSION}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache Trivy (Binary & Vulnerability DB)
|
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
|
with:
|
|
path: |
|
|
~/.cache/trivy-bin
|
|
~/.cache/trivy
|
|
key: trivy-${{ steps.vars.outputs.version }}-${{ runner.os }}-${{ runner.arch }}-${{ steps.vars.outputs.date }}
|
|
restore-keys: |
|
|
trivy-${{ steps.vars.outputs.version }}-${{ runner.os }}-${{ runner.arch }}-
|
|
|
|
- name: Install and Verify Trivy
|
|
shell: bash
|
|
run: |
|
|
mkdir -p "$HOME/.cache/trivy-bin" "$HOME/.local/bin"
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
|
|
ARCH=$(uname -m)
|
|
case "$ARCH" in
|
|
x86_64) T_ARCH="64bit" ;;
|
|
aarch64|arm64) T_ARCH="ARM64" ;;
|
|
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
|
esac
|
|
|
|
checksums_file="${{ github.action_path }}/checksums.txt"
|
|
tarball="trivy_${{ steps.vars.outputs.version }}_Linux-${T_ARCH}.tar.gz"
|
|
|
|
if [ -f "$HOME/.cache/trivy-bin/${tarball}" ] && (cd "$HOME/.cache/trivy-bin" && grep " ${tarball}$" "$checksums_file" | sha256sum -c -); then
|
|
echo "Cached Trivy archive verified against lockfile."
|
|
else
|
|
echo "Archive missing or checksum mismatch; downloading..."
|
|
curl -sfLO "https://github.com/aquasecurity/trivy/releases/download/v${{ steps.vars.outputs.version }}/${tarball}" --output-dir "$HOME/.cache/trivy-bin"
|
|
(cd "$HOME/.cache/trivy-bin" && grep " ${tarball}$" "$checksums_file" | sha256sum -c -)
|
|
fi
|
|
|
|
tar -xzf "$HOME/.cache/trivy-bin/${tarball}" -C "$HOME/.local/bin" trivy
|
|
chmod +x "$HOME/.local/bin/trivy"
|
|
|
|
- name: Extract Multi-Platform SBOMs
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
mkdir -p sboms/head sboms/target
|
|
|
|
cleanup_file() {
|
|
local f="$1"
|
|
if [ ! -s "$f" ] || [ "$(cat "$f" 2>/dev/null)" = "null" ]; then
|
|
rm -f "$f"
|
|
fi
|
|
}
|
|
|
|
# Extract platform SBOMs for head image
|
|
echo "Extracting platform SBOMs for head image: ${{ inputs.head-image }}..."
|
|
docker buildx imagetools inspect "${{ inputs.head-image }}" \
|
|
--format '{{ json (index .SBOM "linux/amd64").SPDX }}' > sboms/head/sbom-amd64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/head/sbom-amd64.spdx.json
|
|
|
|
docker buildx imagetools inspect "${{ inputs.head-image }}" \
|
|
--format '{{ json (index .SBOM "linux/arm64").SPDX }}' > sboms/head/sbom-arm64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/head/sbom-arm64.spdx.json
|
|
|
|
# If single-arch, extract root .SPDX
|
|
if [ ! -f sboms/head/sbom-amd64.spdx.json ] && [ ! -f sboms/head/sbom-arm64.spdx.json ]; then
|
|
docker buildx imagetools inspect "${{ inputs.head-image }}" \
|
|
--format '{{ json .SBOM.SPDX }}' > sboms/head/sbom-amd64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/head/sbom-amd64.spdx.json
|
|
fi
|
|
|
|
# Fallback to generating head SBOM with Trivy if attestation missing
|
|
if [ ! -f sboms/head/sbom-amd64.spdx.json ] && [ ! -f sboms/head/sbom-arm64.spdx.json ]; then
|
|
echo "Head image lacks SBOM attestation; generating with Trivy..."
|
|
trivy image --format spdx-json --output sboms/head/sbom-amd64.spdx.json "${{ inputs.head-image }}" || true
|
|
cleanup_file sboms/head/sbom-amd64.spdx.json
|
|
fi
|
|
|
|
# Extract platform SBOMs for target image
|
|
echo "Extracting platform SBOMs for target image: ${{ inputs.target-image }}..."
|
|
docker buildx imagetools inspect "${{ inputs.target-image }}" \
|
|
--format '{{ json (index .SBOM "linux/amd64").SPDX }}' > sboms/target/sbom-amd64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/target/sbom-amd64.spdx.json
|
|
|
|
docker buildx imagetools inspect "${{ inputs.target-image }}" \
|
|
--format '{{ json (index .SBOM "linux/arm64").SPDX }}' > sboms/target/sbom-arm64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/target/sbom-arm64.spdx.json
|
|
|
|
if [ ! -f sboms/target/sbom-amd64.spdx.json ] && [ ! -f sboms/target/sbom-arm64.spdx.json ]; then
|
|
docker buildx imagetools inspect "${{ inputs.target-image }}" \
|
|
--format '{{ json .SBOM.SPDX }}' > sboms/target/sbom-amd64.spdx.json 2>/dev/null || true
|
|
cleanup_file sboms/target/sbom-amd64.spdx.json
|
|
fi
|
|
|
|
# Fallback to generating target SBOM with Trivy if attestation missing
|
|
if [ ! -f sboms/target/sbom-amd64.spdx.json ] && [ ! -f sboms/target/sbom-arm64.spdx.json ]; then
|
|
echo "Target image lacks SBOM attestation; attempting to generate with Trivy..."
|
|
trivy image --format spdx-json --output sboms/target/sbom-amd64.spdx.json "${{ inputs.target-image }}" || true
|
|
cleanup_file sboms/target/sbom-amd64.spdx.json
|
|
fi
|
|
|
|
# Expose extracted head SBOMs to workspace root for workflow artifact uploads
|
|
[ -f sboms/head/sbom-amd64.spdx.json ] && cp sboms/head/sbom-amd64.spdx.json sbom-amd64.spdx.json || true
|
|
[ -f sboms/head/sbom-arm64.spdx.json ] && cp sboms/head/sbom-arm64.spdx.json sbom-arm64.spdx.json || true
|
|
|
|
- name: Scan Multi-Platform SBOMs and Generate Delta
|
|
shell: bash
|
|
run: |
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
mkdir -p cve-reports/head cve-reports/target
|
|
|
|
# Scan each available head SBOM
|
|
for f in sboms/head/sbom-*.spdx.json; do
|
|
[ -f "$f" ] || continue
|
|
arch=$(basename "$f" .spdx.json | sed 's/sbom-//')
|
|
echo "Scanning head SBOM for architecture: $arch"
|
|
trivy sbom --format json --output "cve-reports/head/cve-${arch}.json" "$f" || true
|
|
done
|
|
|
|
# Scan each available target SBOM
|
|
for f in sboms/target/sbom-*.spdx.json; do
|
|
[ -f "$f" ] || continue
|
|
arch=$(basename "$f" .spdx.json | sed 's/sbom-//')
|
|
echo "Scanning target SBOM for architecture: $arch"
|
|
trivy sbom --format json --output "cve-reports/target/cve-${arch}.json" "$f" || true
|
|
done
|
|
|
|
# Run multi-arch set-based diff
|
|
node "${{ github.action_path }}/cve-diff.js" \
|
|
"cve-reports/target" \
|
|
"cve-reports/head" \
|
|
"${{ inputs.target-branch }}" > "${{ inputs.output-delta }}" || true
|
|
|
|
# Generate full markdown summary report for primary platform
|
|
primary_sbom=""
|
|
if [ -f sboms/head/sbom-amd64.spdx.json ]; then
|
|
primary_sbom="sboms/head/sbom-amd64.spdx.json"
|
|
elif [ -f sboms/head/sbom-arm64.spdx.json ]; then
|
|
primary_sbom="sboms/head/sbom-arm64.spdx.json"
|
|
fi
|
|
|
|
if [ -n "$primary_sbom" ]; then
|
|
trivy sbom \
|
|
--format template \
|
|
--template "@${{ github.action_path }}/markdown.tpl" \
|
|
--output "${{ inputs.output-summary }}" \
|
|
"$primary_sbom" || true
|
|
fi
|
|
|
|
- name: Publish Reports to Step Summary
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ inputs.write-summary }}" = "true" ] && [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
|
|
if [ -f "${{ inputs.output-delta }}" ]; then
|
|
cat "${{ inputs.output-delta }}" >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
if [ -f "${{ inputs.output-summary }}" ]; then
|
|
cat "${{ inputs.output-summary }}" >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
fi
|