diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index 1b69ead..6eb1bbc 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -745,7 +745,7 @@ jobs: deployment_label="$(printf '%s' "${DEPLOYMENT_LABEL}" | tr '[:upper:]' '[:lower:]')" gcloud auth configure-docker "${artifact_registry_hostname}" --quiet - docker build --pull -t "${image}" . + git archive HEAD | docker build --pull -t "${image}" - docker push "${image}" gcloud run deploy "${CLOUD_RUN_SERVICE}" \ diff --git a/tests/test_sync_cloud_run_env_workflow.sh b/tests/test_sync_cloud_run_env_workflow.sh index 94aaf6a..19c39bc 100644 --- a/tests/test_sync_cloud_run_env_workflow.sh +++ b/tests/test_sync_cloud_run_env_workflow.sh @@ -476,3 +476,47 @@ else: cases += 1 print(f"image-only no-traffic shell cases: {cases} passed") PY + +# Execute the real build command against a synthetic checkout. Authentication +# files created after checkout must never enter the container build context. +python3 - "$workflow_file" <<'PY' +import os +from pathlib import Path +import subprocess +import sys +import tempfile + +build_commands = [line.strip() for line in Path(sys.argv[1]).read_text().splitlines() if "docker build --pull" in line] +assert len(build_commands) == 2, "both image-only and legacy builds must be exercised" +with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + checkout = root / "checkout" + checkout.mkdir() + env = {"PATH": os.environ["PATH"], "HOME": str(root), "GIT_CONFIG_NOSYSTEM": "1", "GIT_CONFIG_GLOBAL": os.devnull} + def git(*args): + subprocess.run(["git", *args], cwd=checkout, env=env, check=True, capture_output=True) + git("init", "-q") + (checkout / "Dockerfile").write_text("FROM scratch\nCOPY . /app/\n") + (checkout / "tracked.txt").write_text("synthetic source\n") + git("add", "Dockerfile", "tracked.txt") + git("-c", "user.name=synthetic", "-c", "user.email=synthetic@example.invalid", "-c", "commit.gpgSign=false", "commit", "-qm", "synthetic") + (checkout / "gha-creds-synthetic.json").write_text('{"synthetic":true}\n') + bin_dir = root / "bin" + bin_dir.mkdir() + docker = bin_dir / "docker" + docker.write_text(f"#!{sys.executable}\n" + '''import sys, tarfile +if sys.argv[-1] != "-": + raise SystemExit("workspace build context can include generated credentials") +with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as archive: + names = {member.name for member in archive} +if names != {"Dockerfile", "tracked.txt"}: + raise SystemExit("build context must contain only the approved tracked source") +''') + docker.chmod(0o700) + env.update(PATH=f"{bin_dir}:{os.environ['PATH']}", image="synthetic:test") + for index, build_command in enumerate(build_commands, 1): + result = subprocess.run(["bash", "-c", "set -euo pipefail\n" + build_command], cwd=checkout, env=env, capture_output=True) + if result.returncode: + raise SystemExit(f"FAIL: build {index} admits a workspace context instead of tracked source") +print("PASS: both tracked-source builds exclude generated authentication files") +PY