name: "Trivy scan (misconfiguration)" description: "Run trivy config in a locked-down Docker container (misconfiguration / IaC); report is always SARIF" author: "Timo Behrendt " branding: icon: "shield" color: "blue" inputs: scan-path: description: "Host path to the directory to scan (mounted read-only at /scan in the container)" required: true cache-dir: description: "Host path to the Trivy cache directory (mounted read-only at /cache; created if missing; use the same path as setup-db)" required: false default: "${{ runner.temp }}/trivy" output-dir: description: "Host directory where the report file is written (mounted read-write at /out; created if missing)" required: true output-file: description: "SARIF report file name only (no slashes); written under output-dir" required: true trivy-version: description: "Trivy Docker image reference (digest pin recommended)" required: false default: "ghcr.io/aquasecurity/trivy:0.69.3@sha256:bcc376de8d77cfe086a917230e818dc9f8528e3c852f7b1aff648949b6258d1c" outputs: output-path: description: "Host path to the generated report file" value: ${{ steps.scan.outputs.output-path }} runs: using: "composite" steps: - id: scan shell: bash env: SCAN_PATH_IN: ${{ inputs.scan-path }} CACHE_DIR_IN: ${{ inputs.cache-dir }} OUTPUT_DIR_IN: ${{ inputs.output-dir }} OUTPUT_FILE: ${{ inputs.output-file }} TRIVY_IMAGE: ${{ inputs.trivy-version }} run: | set -euo pipefail case "$OUTPUT_FILE" in */*|".."*) echo "FAIL: output-file must be a single file name (no path separators)"; exit 1 ;; esac mkdir -p "$CACHE_DIR_IN" "$OUTPUT_DIR_IN" scan_path=$(realpath "$SCAN_PATH_IN") cache_dir=$(realpath "$CACHE_DIR_IN") output_dir=$(realpath "$OUTPUT_DIR_IN") test -d "$scan_path" || { echo "FAIL: scan-path is not a directory: $scan_path"; exit 1; } test -d "$cache_dir" || { echo "FAIL: cache-dir is not a directory: $cache_dir"; exit 1; } test -d "$output_dir" || { echo "FAIL: output-dir is not a directory: $output_dir"; exit 1; } docker run --rm \ --name trivy-scan-config \ --user "$(id -u):$(id -g)" \ --read-only \ --env-file /dev/null \ --cap-drop ALL \ --pids-limit 64 \ --memory=512m \ --memory-swap=512m \ --cpus=1 \ --ipc private \ --cgroupns private \ --security-opt no-new-privileges \ --security-opt apparmor=docker-default \ --tmpfs /tmp:rw,noexec,nosuid,nodev,size=1g \ --network none \ --mount type=bind,src="$scan_path",dst=/scan,ro \ --mount type=bind,src="$cache_dir",dst=/cache,ro \ --mount type=bind,src="$output_dir",dst=/out \ "$TRIVY_IMAGE" \ config /scan \ --cache-dir /cache \ --skip-check-update \ --offline-scan \ --format sarif \ --exit-code 0 \ -o "/out/$OUTPUT_FILE" echo "output-path=$output_dir/$OUTPUT_FILE" >> "$GITHUB_OUTPUT"