"""Contracts for the read-only leoclean GCP staging preflight.""" from __future__ import annotations import copy import hashlib import json import subprocess import sys from collections.abc import Callable from pathlib import Path import pytest from ops import check_gcp_leoclean_nosend_preflight as preflight REVISION = "a" * 40 PRIVATE_IP = "10.61.0.3" def live_fixtures() -> dict[str, object]: return { "project": {"lifecycleState": "ACTIVE", "projectId": "teleo-501523"}, "instance": { "name": "teleo-staging-1", "status": "RUNNING", "zone": "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a", "machineType": ( "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/" "machineTypes/e2-standard-4" ), "cpuPlatform": "Intel Broadwell", "serviceAccounts": [ {"email": "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com"} ], "disks": [ { "boot": True, "source": ( "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/" "disks/teleo-staging-1" ), } ], "networkInterfaces": [ { "network": ( "https://www.googleapis.com/compute/v1/projects/teleo-501523/global/networks/teleo-staging-net" ), "subnetwork": ( "https://www.googleapis.com/compute/v1/projects/teleo-501523/regions/europe-west6/" "subnetworks/teleo-staging-europe-west6" ), "networkIP": "10.60.0.2", "accessConfigs": [], "ipv6AccessConfigs": [], "stackType": "IPV4_ONLY", } ], "tags": {"items": ["teleo-staging-ssh"]}, }, "disk": { "name": "teleo-staging-1", "status": "READY", "sizeGb": "80", "zone": "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a", "architecture": "X86_64", "users": [ "https://www.googleapis.com/compute/v1/projects/teleo-501523/zones/europe-west6-a/" "instances/teleo-staging-1" ], }, "subnet": { "name": "teleo-staging-europe-west6", "region": "https://www.googleapis.com/compute/v1/projects/teleo-501523/regions/europe-west6", "network": "https://www.googleapis.com/compute/v1/projects/teleo-501523/global/networks/teleo-staging-net", "ipCidrRange": "10.60.0.0/20", "privateIpGoogleAccess": True, }, "artifact": { "name": "projects/teleo-501523/locations/europe-west6/repositories/teleo", "format": "DOCKER", "mode": "STANDARD_REPOSITORY", "dockerConfig": {"immutableTags": True}, }, "cloudsql": { "name": "teleo-pgvector-standby", "state": "RUNNABLE", "region": "europe-west6", "databaseVersion": "POSTGRES_16", "ipAddresses": [{"type": "PRIVATE", "ipAddress": PRIVATE_IP}], "settings": { "ipConfiguration": { "ipv4Enabled": False, "privateNetwork": "projects/teleo-501523/global/networks/teleo-staging-net", "sslMode": "ENCRYPTED_ONLY", } }, }, "host": { "architecture": "x86_64", "project": "teleo-501523", "instance": "teleo-staging-1", "service_account": "sa-teleo-staging-vm@teleo-501523.iam.gserviceaccount.com", "docker_server_version": "29.4.0", "docker_server_os": "linux", "docker_server_arch": "amd64", "docker_root_free_bytes": str(20 * 1024**3), "unit_load_state": "not-found", "container_collision_count": "0", "cloudsql_tcp": "reachable", }, } def fixture_runner( values: dict[str, object], calls: list[list[str]] | None = None, ) -> Callable[[list[str]], subprocess.CompletedProcess[str]]: def run(argv: list[str]) -> subprocess.CompletedProcess[str]: if calls is not None: calls.append(argv) if argv[:3] == ["gcloud", "projects", "describe"]: value: object = values["project"] elif argv[:4] == ["gcloud", "compute", "instances", "describe"]: value = values["instance"] elif argv[:4] == ["gcloud", "compute", "disks", "describe"]: value = values["disk"] elif argv[:5] == ["gcloud", "compute", "networks", "subnets", "describe"]: value = values["subnet"] elif argv[:4] == ["gcloud", "artifacts", "repositories", "describe"]: value = values["artifact"] elif argv[:4] == ["gcloud", "sql", "instances", "describe"]: value = values["cloudsql"] elif argv[:3] == ["gcloud", "compute", "ssh"]: host = values["host"] assert isinstance(host, dict) output = "".join(f"{key}={item}\n" for key, item in host.items()) return subprocess.CompletedProcess(argv, 0, stdout=output, stderr="") else: # pragma: no cover - the assertion shows unexpected command drift raise AssertionError(argv) return subprocess.CompletedProcess(argv, 0, stdout=json.dumps(value), stderr="") return run def test_exact_read_only_preflight_passes_without_exposing_private_ip() -> None: calls: list[list[str]] = [] payload = preflight.build_preflight(runner=fixture_runner(live_fixtures(), calls), source_revision=REVISION) assert payload["status"] == "pass" assert payload["source_revision"] == REVISION assert payload["target"] == { "project": "teleo-501523", "zone": "europe-west6-a", "instance": "teleo-staging-1", "service": "leoclean-gcp-nosend.service", "container": "livingip-leoclean-nosend", "platform": "linux/amd64", } assert payload["authority"] == { "mode": "bounded_observation_preflight", "secret_values_read": False, "database_credentials_used": False, "database_queries_run": False, "control_plane_observations_before_ssh_are_describe_only": True, "remote_host_runtime_mutations": False, "ssh_access_may_register_time_bounded_key": True, "ssh_key_maximum_lifetime": "5m", } assert payload["host"]["cloudsql_private_tcp_5432"] == "reachable" assert payload["control_plane"]["cloudsql"]["private_ip_sha256"] == hashlib.sha256( PRIVATE_IP.encode("ascii") ).hexdigest() assert PRIVATE_IP not in json.dumps(payload) assert len(calls) == 7 assert calls[:-1] == [ [ "gcloud", "sql", "instances", "describe", "teleo-pgvector-standby", "--project", "teleo-501523", "--format=json(name,state,region,databaseVersion,ipAddresses,settings.ipConfiguration)", ], [ "gcloud", "projects", "describe", "teleo-501523", "--format=json(projectId,lifecycleState)", ], [ "gcloud", "compute", "instances", "describe", "teleo-staging-1", "--project", "teleo-501523", "--zone", "europe-west6-a", "--format=json(name,status,zone,machineType,cpuPlatform,serviceAccounts,disks,networkInterfaces,tags.items)", ], [ "gcloud", "compute", "disks", "describe", "teleo-staging-1", "--project", "teleo-501523", "--zone", "europe-west6-a", "--format=json(name,status,sizeGb,zone,architecture,users)", ], [ "gcloud", "compute", "networks", "subnets", "describe", "teleo-staging-europe-west6", "--project", "teleo-501523", "--region", "europe-west6", "--format=json(name,region,network,ipCidrRange,privateIpGoogleAccess)", ], [ "gcloud", "artifacts", "repositories", "describe", "teleo", "--project", "teleo-501523", "--location", "europe-west6", "--format=json(name,format,mode,dockerConfig.immutableTags)", ], ] ssh = calls[-1] assert ssh == preflight._ssh_argv(PRIVATE_IP) assert "--tunnel-through-iap" in ssh assert "--ssh-key-expire-after=5m" in ssh remote = next(item.removeprefix("--command=") for item in ssh if item.startswith("--command=")) assert remote.startswith("set -euo pipefail\n") for forbidden in ( "docker run", "docker pull", "docker rm", "secretmanager", "secrets versions access", "systemctl start", "systemctl restart", "systemctl enable", ): assert forbidden not in remote.casefold() @pytest.mark.parametrize( ("path", "field", "unsafe", "message"), [ (("instance", "serviceAccounts"), None, [{"email": "postgres-admin@example.com"}], "service account"), (("disk",), "sizeGb", "40", "boot disk"), (("disk",), "architecture", "ARM64", "architecture"), (("disk",), "users", [], "attachment"), (("instance", "networkInterfaces"), None, [], "network interface"), (("instance", "networkInterfaces", 0), "accessConfigs", [{"natIP": "203.0.113.10"}], "public"), (("instance", "networkInterfaces", 0), "ipv6AccessConfigs", [{"externalIpv6": "2001:db8::1"}], "IPv6"), (("instance", "networkInterfaces", 0), "externalIpv6", "2001:db8::1", "IPv6"), (("instance", "networkInterfaces", 0), "networkIP", "10.99.0.2", "reviewed subnet"), (("subnet",), "privateIpGoogleAccess", False, "Private Google Access"), (("subnet",), "ipCidrRange", "10.99.0.0/24", "CIDR drifted"), (("artifact", "dockerConfig"), "immutableTags", False, "not immutable"), (("cloudsql", "settings", "ipConfiguration"), "ipv4Enabled", True, "public IPv4"), (("cloudsql",), "databaseVersion", "POSTGRES_160", "exactly PostgreSQL 16"), (("host",), "container_collision_count", "1", "container name"), (("host",), "unit_load_state", "loaded", "unit name"), (("host",), "docker_root_free_bytes", "1024", "free space"), (("host",), "cloudsql_tcp", "unreachable", "TCP/5432"), ], ) def test_preflight_fails_closed_on_prerequisite_drift( path: tuple[str | int, ...], field: str | None, unsafe: object, message: str, ) -> None: values = copy.deepcopy(live_fixtures()) target: object = values for part in path: if isinstance(part, int): assert isinstance(target, list) target = target[part] else: assert isinstance(target, dict) target = target[part] if field is None: assert isinstance(target, list) parent: object = values for part in path[:-1]: if isinstance(part, int): assert isinstance(parent, list) parent = parent[part] else: assert isinstance(parent, dict) parent = parent[part] assert isinstance(parent, dict) last = path[-1] assert isinstance(last, str) parent[last] = unsafe else: assert isinstance(target, dict) target[field] = unsafe with pytest.raises(preflight.PreflightError, match=message): preflight.build_preflight(runner=fixture_runner(values), source_revision=REVISION) def test_authentication_failure_is_classified_without_echoing_error() -> None: sentinel = "sensitive-browser-session-sentinel" def fail(argv: list[str]) -> subprocess.CompletedProcess[str]: return subprocess.CompletedProcess( argv, 1, stdout="", stderr=f"Reauthentication failed. {sentinel}. Run gcloud auth login.", ) with pytest.raises(preflight.PreflightError) as caught: preflight.build_preflight(runner=fail, source_revision=REVISION) assert caught.value.category == "authentication_required" assert sentinel not in str(caught.value) @pytest.mark.parametrize( ("mutation", "message"), [ ({"ipAddresses": [{"type": "PRIVATE", "ipAddress": "10.99.0.2"}]}, "address drifted"), ( { "ipAddresses": [ {"type": "PRIVATE", "ipAddress": PRIVATE_IP}, {"type": "PRIMARY", "ipAddress": "203.0.113.10"}, ] }, "private-only", ), ( {"settings": {"ipConfiguration": {"sslMode": "TRUSTED_CLIENT_CERTIFICATE_REQUIRED"}}}, "TLS mode", ), ], ) def test_cloudsql_endpoint_and_tls_are_exact(mutation: dict[str, object], message: str) -> None: values = copy.deepcopy(live_fixtures()) cloudsql = values["cloudsql"] assert isinstance(cloudsql, dict) if "settings" in mutation: settings = cloudsql["settings"] assert isinstance(settings, dict) ip_config = settings["ipConfiguration"] assert isinstance(ip_config, dict) changed = mutation["settings"] assert isinstance(changed, dict) changed_ip_config = changed["ipConfiguration"] assert isinstance(changed_ip_config, dict) ip_config.update(changed_ip_config) else: cloudsql.update(mutation) with pytest.raises(preflight.PreflightError, match=message): preflight.build_preflight(runner=fixture_runner(values), source_revision=REVISION) def test_host_output_rejects_extra_duplicate_or_unbounded_fields() -> None: valid = live_fixtures()["host"] assert isinstance(valid, dict) encoded = "".join(f"{key}={item}\n" for key, item in valid.items()) for unsafe in ( encoded + "unexpected=value\n", encoded + "project=duplicate\n", encoded.replace("docker_server_os=linux", "docker_server_os=linux value with spaces"), ): with pytest.raises(preflight.PreflightError, match="host preflight"): preflight._parse_host_output(unsafe) def test_command_gate_rejects_mutating_or_unreviewed_gcloud_calls() -> None: for command in ( ["gcloud", "compute", "instances", "start", "teleo-staging-1"], ["gcloud", "secrets", "versions", "access", "latest"], ["gcloud", "compute", "scp", "file", "teleo-staging-1:/tmp/file"], ["gcloud", "compute", "ssh", "teleo-staging-1", "--command=sudo systemctl restart x"], ["gcloud", "compute", "ssh", "teleo-staging-1", "--command=touch /tmp/preflight-was-here"], ["bash", "-c", "true"], ): with pytest.raises(preflight.PreflightError): preflight._assert_read_only_command(command) def test_source_revision_requires_clean_tracked_commit(tmp_path: Path) -> None: subprocess.run(["git", "init", "--quiet", str(tmp_path)], check=True) tracked = tmp_path / "tracked.txt" tracked.write_text("committed\n", encoding="utf-8") subprocess.run(["git", "-C", str(tmp_path), "add", "tracked.txt"], check=True) subprocess.run( [ "git", "-C", str(tmp_path), "-c", "user.name=Preflight Test", "-c", "user.email=preflight@example.invalid", "commit", "--quiet", "-m", "fixture", ], check=True, ) revision = subprocess.run( ["git", "-C", str(tmp_path), "rev-parse", "HEAD"], text=True, capture_output=True, check=True, ).stdout.strip() assert preflight._source_revision(tmp_path, critical_paths=("tracked.txt",)) == revision (tmp_path / "untracked.txt").write_text("drift\n", encoding="utf-8") with pytest.raises(preflight.PreflightError, match="clean tracked"): preflight._source_revision(tmp_path, critical_paths=("tracked.txt",)) def test_preflight_rejects_an_unrelated_clean_repository(tmp_path: Path) -> None: with pytest.raises(preflight.PreflightError, match="containing this script"): preflight._validated_repo_root(tmp_path) def test_direct_cli_imports_then_rejects_an_unrelated_repository(tmp_path: Path) -> None: completed = subprocess.run( [sys.executable, str(preflight.SOURCE_ROOT / "ops" / "check_gcp_leoclean_nosend_preflight.py"), "--repo-root", str(tmp_path)], text=True, capture_output=True, check=False, ) assert completed.returncode == 1 assert json.loads(completed.stdout) == { "category": "invalid_source_root", "schema": "livingip.leocleanNoSendGcpPreflight.v1", "status": "blocked", }