diff --git a/Dockerfile b/Dockerfile index 3d50f91..544df98 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ # Runtime image for goreleaser dockers_v2 (binary layout: $TARGETPLATFORM/kollect-render). -FROM gcr.io/distroless/static-debian12:nonroot +# Index digest (multi-arch). Scorecard Pinned-Dependencies; do not float on :nonroot. +FROM gcr.io/distroless/static-debian12:nonroot@sha256:1b7b9f0f0e0a1d2155f531db587cc48ec26aaf97ab64364225f5bf18a054e66a ARG TARGETPLATFORM COPY ${TARGETPLATFORM}/kollect-render /kollect-render diff --git a/hack/test/dockerfile_pin_test.sh b/hack/test/dockerfile_pin_test.sh new file mode 100755 index 0000000..490ae96 --- /dev/null +++ b/hack/test/dockerfile_pin_test.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Fail if Dockerfile FROM lines are not pinned to a sha256 digest. +# Scorecard Pinned-Dependencies treats a floating tag as mutable supply chain. +# Run: bash hack/test/dockerfile_pin_test.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +DF="${ROOT}/Dockerfile" + +fail=0 +ok() { echo "ok: $*"; } +bad() { echo "FAIL: $*" >&2; fail=1; } + +[[ -f "${DF}" ]] || { echo "FAIL: Dockerfile missing at ${DF}" >&2; exit 1; } + +from_count=0 +while IFS= read -r line || [[ -n "${line}" ]]; do + [[ "${line}" =~ ^[[:space:]]*FROM[[:space:]] ]] || continue + from_count=$((from_count + 1)) + # Drop trailing comments. + ref="${line#FROM }" + ref="${ref%%#*}" + ref="${ref%"${ref##*[![:space:]]}"}" + # Stage alias (AS builder) is allowed after the digest. + if [[ ! "${ref}" =~ @sha256:[0-9a-f]{64}([[:space:]]|$) ]]; then + bad "unpinned FROM: ${line}" + else + ok "pinned FROM: ${ref}" + fi +done <"${DF}" + +if [[ "${from_count}" -eq 0 ]]; then + echo "FAIL: no FROM lines in Dockerfile — assert is vacuous" >&2 + exit 1 +fi + +if [[ "${fail}" -ne 0 ]]; then + echo "dockerfile_pin_test: RED" >&2 + exit 1 +fi +echo "dockerfile_pin_test: GREEN"