From 5f499cd0aca0b952cecb888c9f0bb7080da037b8 Mon Sep 17 00:00:00 2001 From: peet Date: Sat, 29 Aug 2026 09:38:38 +0200 Subject: [PATCH 1/2] podman-build-publish: gate pushes on a Trivy vulnerability scan New scan step between build and push: the built image is exported and scanned with Trivy; fixable HIGH/CRITICAL findings fail the action and block the push. Configurable via new scan / scan-severity inputs. Implements noctrl.podman security review finding #16 (CI level). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01P5NjXQ3jmyVjimHQxnyKhg --- README.md | 2 ++ podman-build-publish/action.yaml | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/README.md b/README.md index 60e5a87..14b7423 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Reusable Gitea Actions composite workflows for noctrl infrastructure automation. Builds and optionally pushes OCI container images to `registry.noctrl.eu` using Podman with isolated storage context. +Since `v10`, the built image is scanned with Trivy before the push and the action fails on fixable HIGH/CRITICAL vulnerabilities (tune with `scan-severity`, opt out with `scan: "false"`). Requires the `trivy` binary on the runner (present in `noctrl/gitea-runner` since `v9`). + **Location:** [`./podman-build-publish`](./podman-build-publish) **Use in workflows:** diff --git a/podman-build-publish/action.yaml b/podman-build-publish/action.yaml index e06b4a8..b8b1b76 100644 --- a/podman-build-publish/action.yaml +++ b/podman-build-publish/action.yaml @@ -37,6 +37,20 @@ inputs: Set to "false" for build-only verification workflows. required: false default: "true" + scan: + description: | + Whether to scan the built image with Trivy before pushing. The step + fails — blocking the push — when vulnerabilities at or above the + configured severities with an available fix are found (unfixed CVEs + are ignored: nothing actionable, and Debian/Alpine bases always carry + some). Requires the trivy binary on the runner. Set to "false" only + with a documented reason. + required: false + default: "true" + scan-severity: + description: Comma-separated Trivy severities that fail the scan. + required: false + default: HIGH,CRITICAL runs: using: composite @@ -141,6 +155,45 @@ runs: build_cmd+=("${{ inputs.context }}") "${build_cmd[@]}" + - if: ${{ inputs.scan != 'false' }} + id: scan + shell: bash + run: | + set -euo pipefail + + : "${PODMAN_STORAGE_DRIVER:=vfs}" + : "${PODMAN_STORAGE_OPT:=}" + + podman_args=( + --root "${RUNNER_TEMP}/podman-root" + --runroot "${RUNNER_TEMP}/podman-runroot" + --storage-driver "${PODMAN_STORAGE_DRIVER}" + ) + + if [[ -n "${PODMAN_STORAGE_OPT}" ]]; then + podman_args+=(--storage-opt "${PODMAN_STORAGE_OPT}") + fi + + image_base="registry.noctrl.eu/${{ inputs.image-name }}" + first_tag="$(printf '%s\n' "${{ inputs.tags }}" | tr ', ' '\n\n' | sed '/^$/d' | head -n1)" + + # Trivy cannot read podman's storage under a custom --root, so scan + # an exported archive of the image instead. + archive="${RUNNER_TEMP}/trivy-scan-image.tar" + rm -f "${archive}" + podman "${podman_args[@]}" save --format oci-archive -o "${archive}" "${image_base}:${first_tag}" + + echo "Scanning ${image_base}:${first_tag} (fail on: ${{ inputs.scan-severity }}, fixed only)" + trivy image \ + --input "${archive}" \ + --scanners vuln \ + --severity "${{ inputs.scan-severity }}" \ + --ignore-unfixed \ + --exit-code 1 \ + --no-progress + + rm -f "${archive}" + - if: ${{ inputs.push != 'false' }} id: push shell: bash From ec5a49826693d9334def504bc8122b68901372e5 Mon Sep 17 00:00:00 2001 From: peet Date: Sat, 29 Aug 2026 09:39:49 +0200 Subject: [PATCH 2/2] Use docker-archive for the Trivy scan export (oci-archive unsupported) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01P5NjXQ3jmyVjimHQxnyKhg --- podman-build-publish/action.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/podman-build-publish/action.yaml b/podman-build-publish/action.yaml index b8b1b76..bd2063e 100644 --- a/podman-build-publish/action.yaml +++ b/podman-build-publish/action.yaml @@ -178,10 +178,11 @@ runs: first_tag="$(printf '%s\n' "${{ inputs.tags }}" | tr ', ' '\n\n' | sed '/^$/d' | head -n1)" # Trivy cannot read podman's storage under a custom --root, so scan - # an exported archive of the image instead. + # an exported archive instead. Must be docker-archive: trivy's + # --input does not understand oci-archive tarballs (verified). archive="${RUNNER_TEMP}/trivy-scan-image.tar" rm -f "${archive}" - podman "${podman_args[@]}" save --format oci-archive -o "${archive}" "${image_base}:${first_tag}" + podman "${podman_args[@]}" save --format docker-archive -o "${archive}" "${image_base}:${first_tag}" echo "Scanning ${image_base}:${first_tag} (fail on: ${{ inputs.scan-severity }}, fixed only)" trivy image \