From bcd023ec36d775574af6c55f95a49c3b56b2570a Mon Sep 17 00:00:00 2001 From: Jens Date: Sun, 30 Aug 2026 10:50:12 +0200 Subject: [PATCH] fix(deploy): bind prepared source and scanned image exactly --- backend/tests/test_rc5_release_deployment.py | 8 +++-- deploy/unraid/deploy-release.sh | 31 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/backend/tests/test_rc5_release_deployment.py b/backend/tests/test_rc5_release_deployment.py index 9b55d33f..3430fd3e 100644 --- a/backend/tests/test_rc5_release_deployment.py +++ b/backend/tests/test_rc5_release_deployment.py @@ -74,8 +74,8 @@ def test_release_starts_only_the_locally_attested_ai_image() -> None: assert 'GEOINTEL_INSTALL_AI="${GEOINTEL_INSTALL_AI:-true}"' in script assert "Production release deployment requires the gated AI image" in script - assert 'bash scripts/generate_container_sbom.sh "$GEOINTEL_RELEASE_IMAGE"' in script - assert 'bash scripts/scan_container_image.sh "$GEOINTEL_RELEASE_IMAGE"' in script + assert 'bash scripts/generate_container_sbom.sh "$scanned_image_id"' in script + assert 'bash scripts/scan_container_image.sh "$scanned_image_id"' in script assert 'running_image_id="$(docker inspect --format \'{{.Image}}\' geointel)"' in script assert 'if [ "$running_image_id" != "$image" ]' in script assert "deployment-attestation.json" in script @@ -83,6 +83,10 @@ def test_release_starts_only_the_locally_attested_ai_image() -> None: assert "GITEA_COMMIT_SHA" in script assert "GITHUB_SHA" in script assert "must contain one full 40-character Git commit SHA" in script + assert 'marker_path="$ROOT/.gitea-deploy/revision"' in script + assert "git rev-parse --show-toplevel" in script + assert "Prepared source revision marker does not match" in script + assert "neither an exact Git checkout nor bound" in script assert 'running_revision" != "$GEOINTEL_BUILD_SHA"' in script assert 'running_ai" != "true"' in script assert '"revision": revision' in script diff --git a/deploy/unraid/deploy-release.sh b/deploy/unraid/deploy-release.sh index 26456dc2..94ef5d38 100644 --- a/deploy/unraid/deploy-release.sh +++ b/deploy/unraid/deploy-release.sh @@ -62,6 +62,7 @@ source_tree_hash() { resolve_build_sha() { local head="" content="" controller_sha="" controller_source="" + local git_top="" marker_sha="" marker_path="$ROOT/.gitea-deploy/revision" if [ -n "${GITEA_COMMIT_SHA:-}" ]; then controller_sha="$GITEA_COMMIT_SHA" @@ -89,7 +90,21 @@ resolve_build_sha() { echo "Explicit build revision differs from the controller revision." >&2 return 2 fi + if [ -f "$marker_path" ]; then + marker_sha="$(tr -d '[:space:]' < "$marker_path")" + if ! [[ "$marker_sha" =~ ^[0-9A-Fa-f]{40}$ ]]; then + echo "Prepared source revision marker is invalid." >&2 + return 2 + fi + if [ "${marker_sha,,}" != "$controller_sha" ]; then + echo "Prepared source revision marker does not match the controller revision." >&2 + return 2 + fi + fi if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then + git_top="$(git rev-parse --show-toplevel 2>/dev/null || true)" + fi + if [ -n "$git_top" ] && [ "$(cd "$git_top" && pwd -P)" = "$(pwd -P)" ]; then head="$(git rev-parse HEAD 2>/dev/null || true)" if [ "${head,,}" != "$controller_sha" ]; then echo "Prepared Git checkout does not match the controller revision." >&2 @@ -99,6 +114,9 @@ resolve_build_sha() { echo "Prepared Git checkout contains changes outside the controller revision." >&2 return 2 fi + elif [ -z "$marker_sha" ]; then + echo "Prepared source is neither an exact Git checkout nor bound by a controller revision marker." >&2 + return 2 fi printf '%s' "$controller_sha" return 0 @@ -242,8 +260,17 @@ scan_release_image() { test -n "$scanned_image_id" mkdir -p "$ROOT/$GEOINTEL_DEPLOY_EVIDENCE_DIR" docker image inspect "$GEOINTEL_RELEASE_IMAGE" > "$ROOT/$inspect_output" - bash scripts/generate_container_sbom.sh "$GEOINTEL_RELEASE_IMAGE" "$sbom_output" - bash scripts/scan_container_image.sh "$GEOINTEL_RELEASE_IMAGE" "$vulnerability_output" + ( + export GEOINTEL_IMAGE_ARCHIVE="${GEOINTEL_DEPLOY_EVIDENCE_DIR}/geointel-image.tar" + export GEOINTEL_KEEP_IMAGE_ARCHIVE=true + export SYFT_PARALLELISM=1 + trap 'rm -f -- \ + "$ROOT/$GEOINTEL_IMAGE_ARCHIVE" \ + "$ROOT/$GEOINTEL_IMAGE_ARCHIVE.image-id" \ + "$ROOT/$GEOINTEL_IMAGE_ARCHIVE".partial.*' EXIT + bash scripts/generate_container_sbom.sh "$scanned_image_id" "$sbom_output" + bash scripts/scan_container_image.sh "$scanned_image_id" "$vulnerability_output" + ) current_image_id="$(docker image inspect --format '{{.Id}}' "$GEOINTEL_RELEASE_IMAGE")" if [ "$current_image_id" != "$scanned_image_id" ]; then echo "Release image tag changed while SBOM/scan evidence was being generated." >&2