From ad514f8cec9ae80016897c6747916a5f0d42e78e Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 11:50:02 +0200 Subject: [PATCH 01/14] Added copa to the images build Changed publishing process to publish only patched images --- .github/workflows/release.yml | 100 ++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0534d82..829648d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: publish: description: 'Publish images to registries' required: false - default: true + default: false type: boolean push: tags: @@ -55,6 +55,46 @@ jobs: - name: Login to GitHub Container Registry run: echo ${{ secrets.IMAGES_REPO_TOKEN }} | docker login ghcr.io -u ${{ secrets.IMAGES_REPO_USERNAME }} --password-stdin + - name: Install Copa and Trivy + run: | + set -eux + # Install Trivy + sudo apt-get update + sudo apt-get install -y wget apt-transport-https gnupg lsb-release + wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null + echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list + sudo apt-get update + sudo apt-get install -y trivy + + # Install Copa + COPA_VERSION=$(curl -s https://api.github.com/repos/project-copacetic/copacetic/releases/latest | jq -r '.tag_name' | sed 's/^v//') + curl -fsSL -o copa.tar.gz "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copa_${COPA_VERSION}_linux_$(dpkg --print-architecture).tar.gz" + tar -xzf copa.tar.gz copa + sudo mv copa /usr/local/bin/copa + rm copa.tar.gz + + - name: Start buildkit daemon + run: | + docker run --detach --rm --privileged \ + -p 127.0.0.1:8888:8888/tcp \ + --name buildkitd \ + --entrypoint buildkitd \ + moby/buildkit:latest \ + --addr tcp://0.0.0.0:8888 + + # Wait for buildkit to be ready + for i in $(seq 1 30); do + if docker exec buildkitd buildctl debug workers >/dev/null 2>&1; then + echo "BuildKit is ready" + break + fi + if [ "$i" -eq 30 ]; then + echo "::error::BuildKit failed to start within 30 seconds" + exit 1 + fi + sleep 1 + done + - name: Configure and build images id: vars env: @@ -63,7 +103,6 @@ jobs: PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }} run: | set -eux; - sudo apt-get update echo ${{ matrix.runner}} @@ -121,13 +160,56 @@ jobs: TAGS="$TAGS --tag $GHCR_TAG_MAJOR" fi - docker build --output "type=image,push=$PUSH" \ + # Build and load image locally + docker build --load \ --provenance=false \ --platform "linux/${ARCH_TAG}" \ --target="pimcore_php_$imageVariant" \ --build-arg PHP_VERSION="${PHP_VERSION}" \ --build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \ - ${TAGS} . + --tag "${IMAGE_NAME}:${TAG}" . + + # Patch OS-level vulnerabilities with Copa + echo "Scanning and patching image ${IMAGE_NAME}:${TAG}" + trivy image --vuln-type os --ignore-unfixed --format json \ + -o /tmp/trivy-report.json "${IMAGE_NAME}:${TAG}" + + if [ -s /tmp/trivy-report.json ] && jq -e '.Results[]? | select(.Vulnerabilities != null and (.Vulnerabilities | length > 0))' /tmp/trivy-report.json > /dev/null 2>&1; then + copa patch -i "${IMAGE_NAME}:${TAG}" \ + -r /tmp/trivy-report.json \ + -t "${TAG}-patched" \ + -a tcp://127.0.0.1:8888 + + # Verify the patched image exists + if ! docker image inspect "${IMAGE_NAME}:${TAG}-patched" > /dev/null 2>&1; then + echo "::error::Patched image not found for ${IMAGE_NAME}:${TAG}" + exit 1 + fi + + docker rmi "${IMAGE_NAME}:${TAG}" + docker tag "${IMAGE_NAME}:${TAG}-patched" "${IMAGE_NAME}:${TAG}" + docker rmi "${IMAGE_NAME}:${TAG}-patched" + echo "Successfully patched ${IMAGE_NAME}:${TAG}" + else + echo "No fixable OS vulnerabilities found, skipping Copa patch" + fi + rm -f /tmp/trivy-report.json + + # Apply all tags to the (patched) image + CLEAN_TAGS_FOR_TAGGING="${TAGS//--tag /}" + read -r -a ALL_TAGS <<< "$CLEAN_TAGS_FOR_TAGGING" + for additional_tag in "${ALL_TAGS[@]}"; do + if [ "$additional_tag" != "${IMAGE_NAME}:${TAG}" ]; then + docker tag "${IMAGE_NAME}:${TAG}" "$additional_tag" + fi + done + + # Push if publishing + if [[ "$PUSH" == "true" ]]; then + for additional_tag in "${ALL_TAGS[@]}"; do + docker push "$additional_tag" + done + fi docker inspect ${IMAGE_NAME}:${TAG} || true; @@ -145,8 +227,18 @@ jobs: done fi + # Clean up to save disk space + docker rmi "${IMAGE_NAME}:${TAG}" || true + for additional_tag in "${ALL_TAGS[@]}"; do + docker rmi "$additional_tag" 2>/dev/null || true + done + done + - name: Stop buildkit daemon + if: always() + run: docker stop buildkitd || true + - name: Upload aggregated tags if: github.event_name != 'workflow_dispatch' || inputs.publish uses: actions/upload-artifact@v7 From f21770e97ae711a05a8730a6c2a5f35bf216387a Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 15:44:52 +0200 Subject: [PATCH 02/14] Buildkit pinned to version --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 829648d..aa7c4f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: -p 127.0.0.1:8888:8888/tcp \ --name buildkitd \ --entrypoint buildkitd \ - moby/buildkit:latest \ + moby/buildkit:0.30.0 \ --addr tcp://0.0.0.0:8888 # Wait for buildkit to be ready From 8bda2a1e1a1687865384fcdc94c643f358c2d124 Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 16:06:26 +0200 Subject: [PATCH 03/14] Buildkit startup increased --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa7c4f2..e095f37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,12 +83,12 @@ jobs: --addr tcp://0.0.0.0:8888 # Wait for buildkit to be ready - for i in $(seq 1 30); do + for i in $(seq 1 60); do if docker exec buildkitd buildctl debug workers >/dev/null 2>&1; then echo "BuildKit is ready" break fi - if [ "$i" -eq 30 ]; then + if [ "$i" -eq 60 ]; then echo "::error::BuildKit failed to start within 30 seconds" exit 1 fi From fe0c54a5c22d76aafb0bf1da06303eeefb950245 Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 16:08:17 +0200 Subject: [PATCH 04/14] Buildkit startup increased --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e095f37..e505e5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: -p 127.0.0.1:8888:8888/tcp \ --name buildkitd \ --entrypoint buildkitd \ - moby/buildkit:0.30.0 \ + moby/buildkit:v0.30.0 \ --addr tcp://0.0.0.0:8888 # Wait for buildkit to be ready @@ -89,7 +89,7 @@ jobs: break fi if [ "$i" -eq 60 ]; then - echo "::error::BuildKit failed to start within 30 seconds" + echo "::error::BuildKit failed to start within 60 seconds" exit 1 fi sleep 1 From f311e6fd88291cd192d5e3767be0e5492fbb1c7e Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 16:12:45 +0200 Subject: [PATCH 05/14] Buildkit probe --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e505e5a..1e5b175 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: # Wait for buildkit to be ready for i in $(seq 1 60); do - if docker exec buildkitd buildctl debug workers >/dev/null 2>&1; then + if docker exec buildkitd buildctl --addr tcp://127.0.0.1:8888 debug workers >/dev/null 2>&1; then echo "BuildKit is ready" break fi From a063264acb518aad54cfbd3a001b057cb2e6b7da Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 16:37:14 +0200 Subject: [PATCH 06/14] Trivy severity gate --- .github/workflows/release.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e5b175..b971975 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,11 @@ on: required: false default: false type: boolean + fail_on_severity: + description: 'Fail build if post-patch CVEs remain at this severity (CRITICAL, HIGH, MEDIUM, LOW, or NONE to disable)' + required: false + default: 'CRITICAL' + type: string push: tags: - 'v*.*' @@ -101,6 +106,7 @@ jobs: VERSION_OVERRIDE: "${{ matrix.build.version-override }}" ARCH_TAG: ${{ contains(matrix.runner, 'arm') && 'arm64' || 'amd64' }} PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }} + FAIL_ON_SEVERITY: ${{ inputs.fail_on_severity || 'CRITICAL' }} run: | set -eux; @@ -195,6 +201,16 @@ jobs: fi rm -f /tmp/trivy-report.json + # Post-patch vulnerability gate + FAIL_SEVERITY="${FAIL_ON_SEVERITY:-CRITICAL}" + if [ "$FAIL_SEVERITY" != "NONE" ]; then + echo "Running post-patch scan (fail on ${FAIL_SEVERITY}+)" + trivy image --vuln-type os --ignore-unfixed \ + --exit-code 1 \ + --severity "$FAIL_SEVERITY" \ + "${IMAGE_NAME}:${TAG}" + fi + # Apply all tags to the (patched) image CLEAN_TAGS_FOR_TAGGING="${TAGS//--tag /}" read -r -a ALL_TAGS <<< "$CLEAN_TAGS_FOR_TAGGING" From 34f7c618d99fc66c264352abfccb201304d2b18b Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 17:13:27 +0200 Subject: [PATCH 07/14] Trivy severity gate --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b971975..7ef08df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ on: fail_on_severity: description: 'Fail build if post-patch CVEs remain at this severity (CRITICAL, HIGH, MEDIUM, LOW, or NONE to disable)' required: false - default: 'CRITICAL' + default: 'CRITICAL,HIGH' type: string push: tags: @@ -209,6 +209,12 @@ jobs: --exit-code 1 \ --severity "$FAIL_SEVERITY" \ "${IMAGE_NAME}:${TAG}" + + echo "Running filesystem/library scan (fail on ${FAIL_SEVERITY}+)" + trivy image --vuln-type library --ignore-unfixed \ + --exit-code 1 \ + --severity "$FAIL_SEVERITY" \ + "${IMAGE_NAME}:${TAG}" fi # Apply all tags to the (patched) image From 348d92f12f5086ae499bc88c943f51115c758311 Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 17:15:27 +0200 Subject: [PATCH 08/14] Trivy severity gate --- .github/workflows/release.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ef08df..101ca30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -205,16 +205,46 @@ jobs: FAIL_SEVERITY="${FAIL_ON_SEVERITY:-CRITICAL}" if [ "$FAIL_SEVERITY" != "NONE" ]; then echo "Running post-patch scan (fail on ${FAIL_SEVERITY}+)" + + # OS scan + trivy image --vuln-type os --ignore-unfixed \ + --severity "$FAIL_SEVERITY" \ + --format table \ + -o /tmp/trivy-os-${TAG}.txt \ + "${IMAGE_NAME}:${TAG}" || true trivy image --vuln-type os --ignore-unfixed \ --exit-code 1 \ --severity "$FAIL_SEVERITY" \ "${IMAGE_NAME}:${TAG}" + # Library scan echo "Running filesystem/library scan (fail on ${FAIL_SEVERITY}+)" + trivy image --vuln-type library --ignore-unfixed \ + --severity "$FAIL_SEVERITY" \ + --format table \ + -o /tmp/trivy-lib-${TAG}.txt \ + "${IMAGE_NAME}:${TAG}" || true trivy image --vuln-type library --ignore-unfixed \ --exit-code 1 \ --severity "$FAIL_SEVERITY" \ "${IMAGE_NAME}:${TAG}" + + # Attach scan results to GitHub Actions job summary + { + echo "## Trivy Scan: ${IMAGE_NAME}:${TAG}" + echo "" + echo "### OS Vulnerabilities (${FAIL_SEVERITY}+)" + echo '```' + cat /tmp/trivy-os-${TAG}.txt 2>/dev/null || echo "No results" + echo '```' + echo "" + echo "### Library Vulnerabilities (${FAIL_SEVERITY}+)" + echo '```' + cat /tmp/trivy-lib-${TAG}.txt 2>/dev/null || echo "No results" + echo '```' + echo "" + } >> "$GITHUB_STEP_SUMMARY" + rm -f /tmp/trivy-os-${TAG}.txt /tmp/trivy-lib-${TAG}.txt fi # Apply all tags to the (patched) image From 7329f31de0424e647982a988c4715c8ccf472ebf Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 17:38:33 +0200 Subject: [PATCH 09/14] Trivy severity gate --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 101ca30..3e9ab8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,7 +72,7 @@ jobs: sudo apt-get install -y trivy # Install Copa - COPA_VERSION=$(curl -s https://api.github.com/repos/project-copacetic/copacetic/releases/latest | jq -r '.tag_name' | sed 's/^v//') + COPA_VERSION="0.14.1" curl -fsSL -o copa.tar.gz "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copa_${COPA_VERSION}_linux_$(dpkg --print-architecture).tar.gz" tar -xzf copa.tar.gz copa sudo mv copa /usr/local/bin/copa From 5f084204ebc582640bb63f8845c8267fc1be005c Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 17:46:44 +0200 Subject: [PATCH 10/14] Trivy severity gate --- .github/workflows/release.yml | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e9ab8d..29a8feb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -177,7 +177,7 @@ jobs: # Patch OS-level vulnerabilities with Copa echo "Scanning and patching image ${IMAGE_NAME}:${TAG}" - trivy image --vuln-type os --ignore-unfixed --format json \ + trivy image --pkg-types os --ignore-unfixed --format json \ -o /tmp/trivy-report.json "${IMAGE_NAME}:${TAG}" if [ -s /tmp/trivy-report.json ] && jq -e '.Results[]? | select(.Vulnerabilities != null and (.Vulnerabilities | length > 0))' /tmp/trivy-report.json > /dev/null 2>&1; then @@ -206,25 +206,12 @@ jobs: if [ "$FAIL_SEVERITY" != "NONE" ]; then echo "Running post-patch scan (fail on ${FAIL_SEVERITY}+)" - # OS scan - trivy image --vuln-type os --ignore-unfixed \ + trivy image --pkg-types os --ignore-unfixed \ --severity "$FAIL_SEVERITY" \ --format table \ -o /tmp/trivy-os-${TAG}.txt \ "${IMAGE_NAME}:${TAG}" || true - trivy image --vuln-type os --ignore-unfixed \ - --exit-code 1 \ - --severity "$FAIL_SEVERITY" \ - "${IMAGE_NAME}:${TAG}" - - # Library scan - echo "Running filesystem/library scan (fail on ${FAIL_SEVERITY}+)" - trivy image --vuln-type library --ignore-unfixed \ - --severity "$FAIL_SEVERITY" \ - --format table \ - -o /tmp/trivy-lib-${TAG}.txt \ - "${IMAGE_NAME}:${TAG}" || true - trivy image --vuln-type library --ignore-unfixed \ + trivy image --pkg-types os --ignore-unfixed \ --exit-code 1 \ --severity "$FAIL_SEVERITY" \ "${IMAGE_NAME}:${TAG}" @@ -238,13 +225,8 @@ jobs: cat /tmp/trivy-os-${TAG}.txt 2>/dev/null || echo "No results" echo '```' echo "" - echo "### Library Vulnerabilities (${FAIL_SEVERITY}+)" - echo '```' - cat /tmp/trivy-lib-${TAG}.txt 2>/dev/null || echo "No results" - echo '```' - echo "" } >> "$GITHUB_STEP_SUMMARY" - rm -f /tmp/trivy-os-${TAG}.txt /tmp/trivy-lib-${TAG}.txt + rm -f /tmp/trivy-os-${TAG}.txt fi # Apply all tags to the (patched) image From 12f5c12987642f85eae37101864591e6d826a22d Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Tue, 26 May 2026 18:07:45 +0200 Subject: [PATCH 11/14] Trivy severity gate --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29a8feb..e2af4b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,6 +107,7 @@ jobs: ARCH_TAG: ${{ contains(matrix.runner, 'arm') && 'arm64' || 'amd64' }} PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }} FAIL_ON_SEVERITY: ${{ inputs.fail_on_severity || 'CRITICAL' }} + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db:2 run: | set -eux; From 8fdacb9bc93a49c22852f4663fef55dcc072ee06 Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Wed, 27 May 2026 10:31:46 +0200 Subject: [PATCH 12/14] Applied codereview remarks --- .github/workflows/release.yml | 19 +++-- Dockerfile.trivy-test | 24 +++++++ testimage.sh | 128 ++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 Dockerfile.trivy-test create mode 100755 testimage.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2af4b4..9039875 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ on: default: false type: boolean fail_on_severity: - description: 'Fail build if post-patch CVEs remain at this severity (CRITICAL, HIGH, MEDIUM, LOW, or NONE to disable)' + description: 'Comma-separated list of severities that fail the build if post-patch CVEs remain (e.g. CRITICAL,HIGH). Valid values: CRITICAL, HIGH, MEDIUM, LOW. Use NONE to disable the gate entirely.' required: false default: 'CRITICAL,HIGH' type: string @@ -65,7 +65,7 @@ jobs: set -eux # Install Trivy sudo apt-get update - sudo apt-get install -y wget apt-transport-https gnupg lsb-release + sudo apt-get install -y wget curl apt-transport-https gnupg lsb-release jq wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list sudo apt-get update @@ -73,10 +73,19 @@ jobs: # Install Copa COPA_VERSION="0.14.1" - curl -fsSL -o copa.tar.gz "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copa_${COPA_VERSION}_linux_$(dpkg --print-architecture).tar.gz" + COPA_ARCH="$(dpkg --print-architecture)" + curl -fsSL -o copa.tar.gz "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copa_${COPA_VERSION}_linux_${COPA_ARCH}.tar.gz" + curl -fsSL -o copacetic_checksums.txt "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copacetic_checksums.txt" + # Verify checksum before extracting + EXPECTED_SHA=$(grep "copa_${COPA_VERSION}_linux_${COPA_ARCH}.tar.gz" copacetic_checksums.txt | awk '{print $1}') + ACTUAL_SHA=$(sha256sum copa.tar.gz | awk '{print $1}') + if [ "$EXPECTED_SHA" != "$ACTUAL_SHA" ]; then + echo "::error::Copa checksum mismatch! Expected ${EXPECTED_SHA}, got ${ACTUAL_SHA}" + exit 1 + fi tar -xzf copa.tar.gz copa sudo mv copa /usr/local/bin/copa - rm copa.tar.gz + rm copa.tar.gz copacetic_checksums.txt - name: Start buildkit daemon run: | @@ -106,7 +115,7 @@ jobs: VERSION_OVERRIDE: "${{ matrix.build.version-override }}" ARCH_TAG: ${{ contains(matrix.runner, 'arm') && 'arm64' || 'amd64' }} PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }} - FAIL_ON_SEVERITY: ${{ inputs.fail_on_severity || 'CRITICAL' }} + FAIL_ON_SEVERITY: ${{ inputs.fail_on_severity || 'CRITICAL,HIGH' }} TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db:2 run: | set -eux; diff --git a/Dockerfile.trivy-test b/Dockerfile.trivy-test new file mode 100644 index 0000000..49487cb --- /dev/null +++ b/Dockerfile.trivy-test @@ -0,0 +1,24 @@ +FROM pimcore/pimcore:php8.1-v1-dev + +USER root + +RUN apt-get update && \ + apt-get install -y wget apt-transport-https gnupg lsb-release && \ + wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor -o /usr/share/keyrings/trivy.gpg && \ + echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | tee /etc/apt/sources.list.d/trivy.list && \ + apt-get update && \ + apt-get install -y trivy && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /var/www/.cache && \ + chown -R www-data:www-data /var/www/.cache + +ENV XDG_CACHE_HOME=/var/www/.cache + +USER www-data + +WORKDIR /var/www/html + +# Run: docker exec trivy filesystem --severity HIGH,CRITICAL --format table / +CMD ["tail", "-f", "/dev/null"] diff --git a/testimage.sh b/testimage.sh new file mode 100755 index 0000000..2ed7aa0 --- /dev/null +++ b/testimage.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +set -euo pipefail + +REF="origin/1.x" +IMAGE_NAME="pimcore/pimcore" +LOCAL_TAG="php8.1-v1-dev" +WORKFLOW_TAG="php8.1-v1-dev-amd64" +PATCHED_TAG="${LOCAL_TAG}-copa" +PHP_VERSION="8.1" +DEBIAN_VERSION="bullseye" +TARGET="pimcore_php_fpm" +ARCH="amd64" +BUILDKIT_CONTAINER="buildkitd-copa-local" +WORKDIR="$(mktemp -d)" + +for bin in git tar docker trivy jq copa diff sort mktemp; do + command -v "$bin" >/dev/null 2>&1 || { + echo "Missing required command: $bin" >&2 + exit 1 + } +done + +cleanup() { + docker rm -f "$BUILDKIT_CONTAINER" >/dev/null 2>&1 || true + rm -rf "$WORKDIR" +} +trap cleanup EXIT + +echo "== Fetch 2.x and export build context ==" +git fetch origin 2.x +git archive "$REF" | tar -x -C "$WORKDIR" + +echo +echo "== Build original image from 2.x ==" +docker build --load \ + --provenance=false \ + --platform "linux/${ARCH}" \ + --target "${TARGET}" \ + --build-arg PHP_VERSION="${PHP_VERSION}" \ + --build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \ + --tag "${IMAGE_NAME}:${WORKFLOW_TAG}" \ + --tag "${IMAGE_NAME}:${LOCAL_TAG}" \ + "$WORKDIR" + +echo +echo "== Trivy scan without Copa ==" +trivy image --pkg-types os --ignore-unfixed \ + --format table \ + -o /tmp/trivy-before.txt \ + "${IMAGE_NAME}:${LOCAL_TAG}" || true +cat /tmp/trivy-before.txt + +echo +echo "== Save package inventory before patch ==" +docker run --rm "${IMAGE_NAME}:${LOCAL_TAG}" \ + dpkg-query -W -f='${Package} ${Version}\n' | sort > /tmp/pkg-before.txt + +echo +echo "== Export Trivy JSON report ==" +trivy image --pkg-types os --ignore-unfixed \ + --format json \ + -o /tmp/trivy-report.json \ + "${IMAGE_NAME}:${LOCAL_TAG}" + +if jq -e '.Results[]? | select(.Vulnerabilities != null and (.Vulnerabilities | length > 0))' /tmp/trivy-report.json >/dev/null 2>&1; then + echo + echo "== Start BuildKit for Copa ==" + docker rm -f "$BUILDKIT_CONTAINER" >/dev/null 2>&1 || true + docker run --detach --rm --privileged \ + -p 127.0.0.1:8889:8888/tcp \ + --name "$BUILDKIT_CONTAINER" \ + --entrypoint buildkitd \ + moby/buildkit:v0.30.0 \ + --addr tcp://0.0.0.0:8888 >/dev/null + + # for i in $(seq 1 60); do + # if docker exec "$BUILDKIT_CONTAINER" buildctl --addr tcp://127.0.0.1:8889 debug workers >/dev/null 2>&1; then + # break + # fi + # if [ "$i" -eq 60 ]; then + # echo "BuildKit failed to start within 60 seconds" >&2 + # exit 1 + # fi + # sleep 1 + # done + + echo + echo "== Patch image with Copa ==" + copa patch \ + -i "${IMAGE_NAME}:${LOCAL_TAG}" \ + -r /tmp/trivy-report.json \ + -t "${PATCHED_TAG}" \ + -a tcp://127.0.0.1:8889 + + echo + echo "== Trivy scan with Copa ==" + trivy image --pkg-types os --ignore-unfixed \ + --format table \ + -o /tmp/trivy-after.txt \ + "${IMAGE_NAME}:${PATCHED_TAG}" || true + cat /tmp/trivy-after.txt + + echo + echo "== Save package inventory after patch ==" + docker run --rm "${IMAGE_NAME}:${PATCHED_TAG}" \ + dpkg-query -W -f='${Package} ${Version}\n' | sort > /tmp/pkg-after.txt + + echo + echo "== Package diff: original vs patched ==" + diff -u /tmp/pkg-before.txt /tmp/pkg-after.txt || true + + echo + echo "== Image IDs ==" + docker image inspect "${IMAGE_NAME}:${LOCAL_TAG}" --format 'original {{.RepoTags}} {{.Id}}' + docker image inspect "${IMAGE_NAME}:${PATCHED_TAG}" --format 'patched {{.RepoTags}} {{.Id}}' +else + echo + echo "No OS vulnerabilities reported by Trivy. Copa patch step skipped." +fi + +echo +echo "Artifacts written to:" +echo " /tmp/trivy-before.txt" +echo " /tmp/trivy-report.json" +echo " /tmp/pkg-before.txt" +echo " /tmp/trivy-after.txt" +echo " /tmp/pkg-after.txt" \ No newline at end of file From 0438ccd9995e174e94722738d288abfee0fad40f Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Wed, 27 May 2026 10:38:19 +0200 Subject: [PATCH 13/14] Improvements --- .github/workflows/release.yml | 29 +++++++- Dockerfile.trivy-test | 24 ------- testimage.sh | 128 ---------------------------------- 3 files changed, 26 insertions(+), 155 deletions(-) delete mode 100644 Dockerfile.trivy-test delete mode 100755 testimage.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9039875..6fa1c95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,9 @@ on: env: IMAGE_NAME: pimcore/pimcore + COPA_VERSION: "0.14.1" + BUILDKIT_VERSION: "0.30.0" + TRIVY_DB_REPOSITORY: "ghcr.io/aquasecurity/trivy-db:2" jobs: build-php: @@ -72,7 +75,6 @@ jobs: sudo apt-get install -y trivy # Install Copa - COPA_VERSION="0.14.1" COPA_ARCH="$(dpkg --print-architecture)" curl -fsSL -o copa.tar.gz "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copa_${COPA_VERSION}_linux_${COPA_ARCH}.tar.gz" curl -fsSL -o copacetic_checksums.txt "https://github.com/project-copacetic/copacetic/releases/download/v${COPA_VERSION}/copacetic_checksums.txt" @@ -93,7 +95,7 @@ jobs: -p 127.0.0.1:8888:8888/tcp \ --name buildkitd \ --entrypoint buildkitd \ - moby/buildkit:v0.30.0 \ + moby/buildkit:v${{ env.BUILDKIT_VERSION }} \ --addr tcp://0.0.0.0:8888 # Wait for buildkit to be ready @@ -116,10 +118,11 @@ jobs: ARCH_TAG: ${{ contains(matrix.runner, 'arm') && 'arm64' || 'amd64' }} PUSH: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }} FAIL_ON_SEVERITY: ${{ inputs.fail_on_severity || 'CRITICAL,HIGH' }} - TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db:2 + TRIVY_DB_REPOSITORY: ${{ env.TRIVY_DB_REPOSITORY }} run: | set -eux; + mkdir -p trivy-reports echo ${{ matrix.runner}} if [[ "${{ matrix.build.tag }}" =~ ^v?1.[0-9x]+$ ]]; then @@ -216,11 +219,23 @@ jobs: if [ "$FAIL_SEVERITY" != "NONE" ]; then echo "Running post-patch scan (fail on ${FAIL_SEVERITY}+)" + # Get the image hash for report naming + IMAGE_HASH=$(docker image inspect "${IMAGE_NAME}:${TAG}" --format '{{.Id}}' | sed 's/sha256://' | head -c 12) + trivy image --pkg-types os --ignore-unfixed \ --severity "$FAIL_SEVERITY" \ --format table \ -o /tmp/trivy-os-${TAG}.txt \ "${IMAGE_NAME}:${TAG}" || true + + # Save report with image hash for artifact upload + trivy image --pkg-types os --ignore-unfixed \ + --severity "$FAIL_SEVERITY" \ + --format json \ + -o "trivy-reports/${TAG}_${IMAGE_HASH}.json" \ + "${IMAGE_NAME}:${TAG}" || true + cp /tmp/trivy-os-${TAG}.txt "trivy-reports/${TAG}_${IMAGE_HASH}.txt" 2>/dev/null || true + trivy image --pkg-types os --ignore-unfixed \ --exit-code 1 \ --severity "$FAIL_SEVERITY" \ @@ -283,6 +298,14 @@ jobs: if: always() run: docker stop buildkitd || true + - name: Upload trivy reports + if: always() + uses: actions/upload-artifact@v7 + with: + name: trivy-reports_${{ matrix.runner }}_${{ matrix.build.tag }}_${{ matrix.build.php }} + path: trivy-reports/ + if-no-files-found: ignore + - name: Upload aggregated tags if: github.event_name != 'workflow_dispatch' || inputs.publish uses: actions/upload-artifact@v7 diff --git a/Dockerfile.trivy-test b/Dockerfile.trivy-test deleted file mode 100644 index 49487cb..0000000 --- a/Dockerfile.trivy-test +++ /dev/null @@ -1,24 +0,0 @@ -FROM pimcore/pimcore:php8.1-v1-dev - -USER root - -RUN apt-get update && \ - apt-get install -y wget apt-transport-https gnupg lsb-release && \ - wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor -o /usr/share/keyrings/trivy.gpg && \ - echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | tee /etc/apt/sources.list.d/trivy.list && \ - apt-get update && \ - apt-get install -y trivy && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -RUN mkdir -p /var/www/.cache && \ - chown -R www-data:www-data /var/www/.cache - -ENV XDG_CACHE_HOME=/var/www/.cache - -USER www-data - -WORKDIR /var/www/html - -# Run: docker exec trivy filesystem --severity HIGH,CRITICAL --format table / -CMD ["tail", "-f", "/dev/null"] diff --git a/testimage.sh b/testimage.sh deleted file mode 100755 index 2ed7aa0..0000000 --- a/testimage.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -REF="origin/1.x" -IMAGE_NAME="pimcore/pimcore" -LOCAL_TAG="php8.1-v1-dev" -WORKFLOW_TAG="php8.1-v1-dev-amd64" -PATCHED_TAG="${LOCAL_TAG}-copa" -PHP_VERSION="8.1" -DEBIAN_VERSION="bullseye" -TARGET="pimcore_php_fpm" -ARCH="amd64" -BUILDKIT_CONTAINER="buildkitd-copa-local" -WORKDIR="$(mktemp -d)" - -for bin in git tar docker trivy jq copa diff sort mktemp; do - command -v "$bin" >/dev/null 2>&1 || { - echo "Missing required command: $bin" >&2 - exit 1 - } -done - -cleanup() { - docker rm -f "$BUILDKIT_CONTAINER" >/dev/null 2>&1 || true - rm -rf "$WORKDIR" -} -trap cleanup EXIT - -echo "== Fetch 2.x and export build context ==" -git fetch origin 2.x -git archive "$REF" | tar -x -C "$WORKDIR" - -echo -echo "== Build original image from 2.x ==" -docker build --load \ - --provenance=false \ - --platform "linux/${ARCH}" \ - --target "${TARGET}" \ - --build-arg PHP_VERSION="${PHP_VERSION}" \ - --build-arg DEBIAN_VERSION="${DEBIAN_VERSION}" \ - --tag "${IMAGE_NAME}:${WORKFLOW_TAG}" \ - --tag "${IMAGE_NAME}:${LOCAL_TAG}" \ - "$WORKDIR" - -echo -echo "== Trivy scan without Copa ==" -trivy image --pkg-types os --ignore-unfixed \ - --format table \ - -o /tmp/trivy-before.txt \ - "${IMAGE_NAME}:${LOCAL_TAG}" || true -cat /tmp/trivy-before.txt - -echo -echo "== Save package inventory before patch ==" -docker run --rm "${IMAGE_NAME}:${LOCAL_TAG}" \ - dpkg-query -W -f='${Package} ${Version}\n' | sort > /tmp/pkg-before.txt - -echo -echo "== Export Trivy JSON report ==" -trivy image --pkg-types os --ignore-unfixed \ - --format json \ - -o /tmp/trivy-report.json \ - "${IMAGE_NAME}:${LOCAL_TAG}" - -if jq -e '.Results[]? | select(.Vulnerabilities != null and (.Vulnerabilities | length > 0))' /tmp/trivy-report.json >/dev/null 2>&1; then - echo - echo "== Start BuildKit for Copa ==" - docker rm -f "$BUILDKIT_CONTAINER" >/dev/null 2>&1 || true - docker run --detach --rm --privileged \ - -p 127.0.0.1:8889:8888/tcp \ - --name "$BUILDKIT_CONTAINER" \ - --entrypoint buildkitd \ - moby/buildkit:v0.30.0 \ - --addr tcp://0.0.0.0:8888 >/dev/null - - # for i in $(seq 1 60); do - # if docker exec "$BUILDKIT_CONTAINER" buildctl --addr tcp://127.0.0.1:8889 debug workers >/dev/null 2>&1; then - # break - # fi - # if [ "$i" -eq 60 ]; then - # echo "BuildKit failed to start within 60 seconds" >&2 - # exit 1 - # fi - # sleep 1 - # done - - echo - echo "== Patch image with Copa ==" - copa patch \ - -i "${IMAGE_NAME}:${LOCAL_TAG}" \ - -r /tmp/trivy-report.json \ - -t "${PATCHED_TAG}" \ - -a tcp://127.0.0.1:8889 - - echo - echo "== Trivy scan with Copa ==" - trivy image --pkg-types os --ignore-unfixed \ - --format table \ - -o /tmp/trivy-after.txt \ - "${IMAGE_NAME}:${PATCHED_TAG}" || true - cat /tmp/trivy-after.txt - - echo - echo "== Save package inventory after patch ==" - docker run --rm "${IMAGE_NAME}:${PATCHED_TAG}" \ - dpkg-query -W -f='${Package} ${Version}\n' | sort > /tmp/pkg-after.txt - - echo - echo "== Package diff: original vs patched ==" - diff -u /tmp/pkg-before.txt /tmp/pkg-after.txt || true - - echo - echo "== Image IDs ==" - docker image inspect "${IMAGE_NAME}:${LOCAL_TAG}" --format 'original {{.RepoTags}} {{.Id}}' - docker image inspect "${IMAGE_NAME}:${PATCHED_TAG}" --format 'patched {{.RepoTags}} {{.Id}}' -else - echo - echo "No OS vulnerabilities reported by Trivy. Copa patch step skipped." -fi - -echo -echo "Artifacts written to:" -echo " /tmp/trivy-before.txt" -echo " /tmp/trivy-report.json" -echo " /tmp/pkg-before.txt" -echo " /tmp/trivy-after.txt" -echo " /tmp/pkg-after.txt" \ No newline at end of file From fa172d8b2607f34830b3e1a8f3ff32e9a64897ca Mon Sep 17 00:00:00 2001 From: "nebojsa.ilic" Date: Wed, 27 May 2026 11:02:51 +0200 Subject: [PATCH 14/14] Applied codereview remarks --- .github/workflows/release.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6fa1c95..4c6e670 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,7 +100,7 @@ jobs: # Wait for buildkit to be ready for i in $(seq 1 60); do - if docker exec buildkitd buildctl --addr tcp://127.0.0.1:8888 debug workers >/dev/null 2>&1; then + if docker exec buildkitd buildctl --addr tcp://127.0.0.1:8888 debug workers >/dev/null 2>&1; then echo "BuildKit is ready" break fi @@ -215,7 +215,7 @@ jobs: rm -f /tmp/trivy-report.json # Post-patch vulnerability gate - FAIL_SEVERITY="${FAIL_ON_SEVERITY:-CRITICAL}" + FAIL_SEVERITY="$FAIL_ON_SEVERITY" if [ "$FAIL_SEVERITY" != "NONE" ]; then echo "Running post-patch scan (fail on ${FAIL_SEVERITY}+)" @@ -263,11 +263,9 @@ jobs: fi done - # Push if publishing + # Push if publishing (parallel for speed) if [[ "$PUSH" == "true" ]]; then - for additional_tag in "${ALL_TAGS[@]}"; do - docker push "$additional_tag" - done + printf '%s\n' "${ALL_TAGS[@]}" | xargs -P 4 -I {} docker push "{}" fi docker inspect ${IMAGE_NAME}:${TAG} || true; @@ -302,7 +300,7 @@ jobs: if: always() uses: actions/upload-artifact@v7 with: - name: trivy-reports_${{ matrix.runner }}_${{ matrix.build.tag }}_${{ matrix.build.php }} + name: trivy-reports_${{ matrix.runner }}_${{ matrix.build.tag }}_${{ matrix.build.php }}_${{ matrix.build.distro }}_${{ matrix.build.version-override }}_${{ matrix.build.latest-tag }} path: trivy-reports/ if-no-files-found: ignore