a73x

scripts/scan-image.sh

Ref:   Size: 2.3 KiB   History

#!/usr/bin/env bash
# Scan a built container image for known OS/library vulnerabilities, and fail
# on the severities we refuse to publish.
#
# This is the gate the Go scanner cannot be. `make vuln` (govulncheck) covers
# the module graph, which is the whole of the server image's contents — it is a
# static binary on distroless. The site image is nginx:alpine, and that layer
# has a package manifest with its own advisories that nothing else here reads.
#
# Runs trivy AS A CONTAINER, so there is no scanner to install and no second
# toolchain to keep pinned. The image is handed over as a tarball rather than
# through a daemon socket: podman and docker disagree about where that socket
# is and whether it exists, and `save` works identically on both.
#
#   scripts/scan-image.sh <image:tag>
#
#   SCAN_SEVERITY   severities that fail (default HIGH,CRITICAL)
#   SKIP_IMAGE_SCAN=1  skip, loudly — for an offline build, never as a habit
#   TRIVY_IMAGE     pinned scanner image
set -euo pipefail

IMAGE="${1:?scan-image: usage: scan-image.sh <image:tag>}"
SEVERITY="${SCAN_SEVERITY:-HIGH,CRITICAL}"
# 0.74, not 0.58: the older scanner could not parse the version string this
# toolchain stamps into every Go binary — go1.27.0-X:nodwarf5, the experiment
# tag baked into the packaged toolchain — and answered by printing one
# "Version matching error" per module and matching NONE of them. The Go half of
# every image scan was quietly doing nothing while the OS half carried the gate.
# A scanner that cannot read a version does not fail; it passes. Bump this when
# the toolchain moves again, and check the summary names each gobinary target.
TRIVY_IMAGE="${TRIVY_IMAGE:-docker.io/aquasec/trivy:0.74.0}"
DOCKER="$(command -v docker || command -v podman)"

if [ "${SKIP_IMAGE_SCAN:-}" = "1" ]; then
  echo "scan-image: SKIPPED for $IMAGE (SKIP_IMAGE_SCAN=1) — publishing unscanned" >&2
  exit 0
fi

TAR="$(mktemp -d)/image.tar"
trap 'rm -rf "$(dirname "$TAR")"' EXIT
echo "scan-image: scanning $IMAGE for $SEVERITY"
"$DOCKER" save "$IMAGE" -o "$TAR"

# The vulnerability DB is cached in a named volume so a second scan in the same
# session does not re-download it.
"$DOCKER" run --rm \
  -v "$TAR:/image.tar:ro" \
  -v eitri-trivy-cache:/root/.cache/trivy \
  "$TRIVY_IMAGE" image \
  --input /image.tar \
  --severity "$SEVERITY" \
  --ignore-unfixed \
  --exit-code 1 \
  --scanners vuln