77 lines
2.7 KiB
Bash
77 lines
2.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Verifies repro-bundle fail-closed policy controls:
|
|
# - build-attestation-bundle.sh enforces digest-pinned images
|
|
# - deterministic env defaults are present
|
|
# - release Dockerfiles fail without @sha256 pinning
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
BUNDLE_SCRIPT="${REPO_ROOT}/devops/tools/build-attestation-bundle.sh"
|
|
DOTNET_DOCKERFILE="${REPO_ROOT}/devops/release/docker/Dockerfile.dotnet-service"
|
|
ANGULAR_DOCKERFILE="${REPO_ROOT}/devops/release/docker/Dockerfile.angular-ui"
|
|
|
|
fail() {
|
|
echo "[repro-policy] $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_line() {
|
|
local file=$1
|
|
local pattern=$2
|
|
if ! grep -Fq "$pattern" "$file"; then
|
|
fail "Missing required pattern in ${file}: ${pattern}"
|
|
fi
|
|
}
|
|
|
|
[[ -f "${BUNDLE_SCRIPT}" ]] || fail "Missing script: ${BUNDLE_SCRIPT}"
|
|
[[ -f "${DOTNET_DOCKERFILE}" ]] || fail "Missing Dockerfile: ${DOTNET_DOCKERFILE}"
|
|
[[ -f "${ANGULAR_DOCKERFILE}" ]] || fail "Missing Dockerfile: ${ANGULAR_DOCKERFILE}"
|
|
|
|
bash -n "${BUNDLE_SCRIPT}"
|
|
|
|
require_line "${BUNDLE_SCRIPT}" "export LC_ALL=C"
|
|
require_line "${BUNDLE_SCRIPT}" "export TZ=UTC"
|
|
require_line "${BUNDLE_SCRIPT}" "SOURCE_DATE_EPOCH=\${SOURCE_DATE_EPOCH:-0}"
|
|
require_line "${BUNDLE_SCRIPT}" "must be digest-pinned (@sha256:...)"
|
|
|
|
require_line "${DOTNET_DOCKERFILE}" 'RUN case "${SDK_IMAGE}" in *@sha256:*)'
|
|
require_line "${DOTNET_DOCKERFILE}" 'RUN case "${RUNTIME_IMAGE}" in *@sha256:*)'
|
|
require_line "${ANGULAR_DOCKERFILE}" 'RUN case "${NODE_IMAGE}" in *@sha256:*)'
|
|
require_line "${ANGULAR_DOCKERFILE}" 'RUN case "${NGINX_IMAGE}" in *@sha256:*)'
|
|
|
|
tmp_dir="$(mktemp -d)"
|
|
trap 'rm -rf "${tmp_dir}"' EXIT
|
|
|
|
attest_dir="${tmp_dir}/attest"
|
|
bundle_out="${tmp_dir}/out"
|
|
mkdir -p "${attest_dir}"
|
|
printf '{"fixture":"ok"}\n' > "${attest_dir}/fixture.json"
|
|
|
|
# Positive path (pinned image)
|
|
BUILDER_IMG='registry.example.org/build/my-builder@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' \
|
|
SOURCE_DATE_EPOCH=0 \
|
|
bash "${BUNDLE_SCRIPT}" "${attest_dir}" "${bundle_out}" > /dev/null
|
|
|
|
[[ -f "${bundle_out}/attestation-bundle-0.tgz" ]] || fail "Expected deterministic bundle archive was not created"
|
|
|
|
# Negative path (unpinned image must fail closed with exit 65)
|
|
set +e
|
|
BUILDER_IMG='registry.example.org/build/my-builder:latest' \
|
|
SOURCE_DATE_EPOCH=0 \
|
|
bash "${BUNDLE_SCRIPT}" "${attest_dir}" "${bundle_out}" > "${tmp_dir}/negative.out" 2> "${tmp_dir}/negative.err"
|
|
status=$?
|
|
set -e
|
|
|
|
if [[ ${status} -ne 65 ]]; then
|
|
fail "Expected unpinned image run to fail with exit 65, got ${status}"
|
|
fi
|
|
|
|
if ! grep -Fq "must be digest-pinned" "${tmp_dir}/negative.err"; then
|
|
fail "Expected digest pinning failure message was not emitted"
|
|
fi
|
|
|
|
echo "[repro-policy] PASS"
|