Files
certctl/scripts/ci-guards/H-002-bare-compose-image.sh
shankar0123 360eaa75bc fix(compose): DEPL-002 — pin alpine/openssl + postgres:16-alpine by digest + H-002 CI guard
Sprint 3 unified-master-audit closure. The production-shaped compose
(deploy/docker-compose.yml) — explicitly self-described as
'PRODUCTION-SHAPED (Bundle 2)' in its header — pulled two images by
floating tag:

    image: alpine/openssl:latest
    image: postgres:16-alpine

The certctl Dockerfiles have been digest-pinned for two bundles
(see Bundle A / H-001 + the digest-validity.sh CI guard). Compose
shipped on the lower bar — a registry-side tag swap could change
what an operator deploys without their seeing the diff in their
infra repo.

Fix:
  - Pin both images by @sha256: (alpine/openssl looked up via Docker
    Hub tag API on 2026-05-16; postgres:16-alpine the same).
  - New scripts/ci-guards/H-002-bare-compose-image.sh — analogous
    to H-001 — fails the build if any 'image:' line in
    deploy/docker-compose.yml lacks a @sha256 digest. Test compose
    files (deploy/docker-compose.test.yml + the loadtest stack)
    and examples/ stay scoped out by design: those are throwaway
    development-loop tooling where floating tags are intentional.
  - The existing digest-validity.sh CI guard auto-discovers
    digests via grep across deploy/ so the new pins get verified
    on the same run that pulls them, without a separate change.

Closes DEPL-002.
2026-05-16 04:31:14 +00:00

51 lines
1.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# scripts/ci-guards/H-002-bare-compose-image.sh
#
# DEPL-002 closure (Sprint 3, 2026-05-16). Companion to H-001-bare-from.sh
# (which enforces digest pins on every Dockerfile FROM): every `image:`
# line in the production-shaped compose file MUST carry an @sha256
# digest. Pre-fix `deploy/docker-compose.yml` had two floating tags
# (alpine/openssl:latest and postgres:16-alpine), so a registry-side
# tag swap could change what an operator deploys without their seeing
# the diff.
#
# Scope is intentionally narrow: only the production-shaped compose
# under deploy/. Test compose files (deploy/docker-compose.test.yml,
# deploy/test/loadtest/docker-compose.yml) and the examples/ directory
# stay free of digest pins because they're development-loop tooling
# whose floating tags are intentional (developer pulls latest, runs
# the loop, throws it away). If a finding ever escalates one of those
# files to "ships in production," extend SCAN_FILES below.
set -e
SCAN_FILES=(
"deploy/docker-compose.yml"
)
failed=0
for f in "${SCAN_FILES[@]}"; do
if [ ! -f "$f" ]; then
echo "::error::H-002 misconfig: $f not found"
failed=1
continue
fi
# Match `image: something:tag` (with optional indent / quotes) that
# does NOT contain @sha256. Strip commented lines and YAML anchors.
BAD=$(grep -nE '^\s*image:\s+[^#@]+$' "$f" | grep -v '@sha256' || true)
if [ -n "$BAD" ]; then
echo "::error file=${f}::H-002 regression: compose has bare image (no @sha256 digest pin):"
echo "$BAD"
failed=1
fi
done
if [ "$failed" -ne 0 ]; then
echo ""
echo "Pin every production-compose image to an immutable digest."
echo "Look up current digests via:"
echo " curl -sS https://hub.docker.com/v2/repositories/<org>/<image>/tags/<tag> | jq -r .digest"
exit 1
fi
echo "H-002 bare-compose-image: clean."