import json import os import subprocess import sys import time from collections.abc import Mapping from pathlib import Path import pytest from ops import verify_gcp_leoclean_service_environment as verifier def environment_block(environment: Mapping[str, str]) -> bytes: return b"\0".join(f"{key}={value}".encode() for key, value in environment.items()) + b"\0" def wait_for_process_environment( process: subprocess.Popen[bytes], expected: Mapping[str, str], *, timeout_seconds: float = 5.0, ) -> None: deadline = time.monotonic() + timeout_seconds while time.monotonic() < deadline: if process.poll() is not None: pytest.fail("environment fixture process exited before becoming readable") try: observed = verifier.parse_environment(Path(f"/proc/{process.pid}/environ").read_bytes()) except (OSError, verifier.VerificationError): time.sleep(0.01) continue if observed == expected: return time.sleep(0.01) pytest.fail("environment fixture process did not become readable") def test_all_expected_fields_pass_and_receipt_contains_no_values() -> None: environment = verifier.expected_environment() environment.update({"HOME": "/home/teleo", "LANG": "C.UTF-8"}) parsed = verifier.parse_environment(environment_block(environment)) validated = verifier.validate_environment(parsed) receipt = { "runtime_environment": "scoped", "status": "pass", "validated_fields": list(validated), } rendered = verifier.canonical_json(receipt) assert validated == tuple(sorted(verifier.expected_environment())) assert json.loads(rendered) == receipt assert rendered == json.dumps(receipt, ensure_ascii=True, separators=(",", ":"), sort_keys=True) + "\n" for value in verifier.expected_environment().values(): assert value not in rendered @pytest.mark.parametrize("field", sorted(verifier.expected_environment())) def test_each_expected_field_mismatch_fails_closed(field: str) -> None: environment = verifier.expected_environment() environment[field] = "wrong-value-that-is-not-a-secret" with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) assert caught.value.code == "environment_mismatch" assert caught.value.fields == (field,) @pytest.mark.parametrize( ("field", "unsafe_value", "expected_code"), [ ("TELEO_CLOUDSQL_USER", "postgres", "environment_mismatch"), ( "TELEO_CLOUDSQL_PASSWORD_SECRET", verifier.ADMINISTRATOR_PASSWORD_SECRET, "administrator_secret_reference", ), ], ) def test_later_dropin_effective_override_fails_without_outputting_value( field: str, unsafe_value: str, expected_code: str, ) -> None: environment = verifier.expected_environment() environment[field] = unsafe_value with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) rendered = verifier.canonical_json(verifier.failure_receipt(caught.value)) assert caught.value.code == expected_code assert unsafe_value not in rendered @pytest.mark.parametrize( ("field", "safe_label"), [ ("PGPASSWORD", "PG*"), ("pgservice", "PG*"), ("CLOUDSDK_CORE_PROJECT", "CLOUDSDK_*"), ("CLOUDSDK_AUTH_ACCESS_TOKEN", "CLOUDSDK_*"), ("cloudsdk_auth_refresh_token", "CLOUDSDK_*"), ("GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_APPLICATION_CREDENTIALS"), ("LD_PRELOAD", "LD_*"), ("LD_LIBRARY_PATH", "LD_*"), ("ld_audit", "LD_*"), ("BASH_ENV", "BASH_ENV"), ("ENV", "ENV"), ("BASH_FUNC_injected%%", "BASH_FUNC_*"), ("PYTHONHOME", "PYTHON*"), ("PYTHONPATH", "PYTHON*"), ("PYTHONHTTPSVERIFY", "PYTHON*"), ("pythonstartup", "PYTHON*"), ("HTTP_PROXY", "HTTP_PROXY"), ("https_proxy", "HTTPS_PROXY"), ("ALL_PROXY", "ALL_PROXY"), ("no_proxy", "NO_PROXY"), ("SSL_CERT_FILE", "SSL_CERT_FILE"), ("ssl_cert_dir", "SSL_CERT_DIR"), ("REQUESTS_CA_BUNDLE", "REQUESTS_CA_BUNDLE"), ("curl_ca_bundle", "CURL_CA_BUNDLE"), ("SSLKEYLOGFILE", "SSLKEYLOGFILE"), ("TELEO_CLOUDSQL_CREDENTIAL_MODE", "TELEO_CLOUDSQL_CREDENTIAL_MODE"), ("TELEO_KB_CLAIM_BASE_URL", "TELEO_KB_CLAIM_BASE_URL"), ("TELEO_KB_READ_ATTEMPTS", "TELEO_KB_READ_ATTEMPTS"), ("TELEO_MEMORY_INCLUDE_EXCERPTS", "TELEO_MEMORY_INCLUDE_EXCERPTS"), ("TELEO_MEMORY_REDACT", "TELEO_MEMORY_REDACT"), ], ) def test_environment_file_broader_credentials_and_forbidden_fields_fail_closed( field: str, safe_label: str, ) -> None: environment = verifier.expected_environment() injected_value = "sensitive-injected-value" environment[field] = injected_value with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) rendered = verifier.canonical_json(verifier.failure_receipt(caught.value)) assert caught.value.code == "forbidden_environment_fields" assert caught.value.fields == (safe_label,) assert injected_value not in rendered def test_arbitrary_forbidden_key_suffix_is_not_an_output_channel() -> None: environment = verifier.expected_environment() environment["PG_SECRET_MARKER"] = "not-output" with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) rendered = verifier.canonical_json(verifier.failure_receipt(caught.value)) assert "PG_SECRET_MARKER" not in rendered assert "PG*" in rendered def test_pythonunbuffered_is_the_only_allowed_python_runtime_tuning() -> None: environment = verifier.expected_environment() environment["PYTHONUNBUFFERED"] = "1" assert verifier.validate_environment(environment) == tuple(sorted(verifier.expected_environment())) def test_bash_env_executes_before_a_script_but_effective_verifier_rejects_it(tmp_path: Path) -> None: marker = "BASH_ENV_EXECUTED" payload = tmp_path / "bash-env.sh" payload.write_text(f"printf '{marker}\\n'\n", encoding="utf-8") completed = subprocess.run( ["/bin/bash", "-c", ":"], env={"BASH_ENV": str(payload), "PATH": verifier.RUNTIME_PATH}, check=False, capture_output=True, text=True, ) assert completed.returncode == 0 assert completed.stdout.strip() == marker environment = verifier.expected_environment() environment["BASH_ENV"] = str(payload) with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) assert caught.value.fields == ("BASH_ENV",) def test_admin_secret_reference_in_any_value_fails_without_field_or_value_leak() -> None: environment = verifier.expected_environment() environment["UNRELATED"] = f"prefix/{verifier.ADMINISTRATOR_PASSWORD_SECRET}/suffix" with pytest.raises(verifier.VerificationError) as caught: verifier.validate_environment(environment) rendered = verifier.canonical_json(verifier.failure_receipt(caught.value)) assert caught.value.code == "administrator_secret_reference" assert verifier.ADMINISTRATOR_PASSWORD_SECRET not in rendered assert "UNRELATED" not in rendered @pytest.mark.parametrize( "raw", [ b"A=1", b"A=1\0\0", b"A=1\0BROKEN\0", b"=value\0", b"A=1\0A=2\0", b"A=\xff\0", b"", ], ) def test_malformed_nul_environment_is_rejected(raw: bytes) -> None: with pytest.raises(verifier.VerificationError) as caught: verifier.parse_environment(raw) assert caught.value.code == "environment_malformed" def test_parser_preserves_equals_inside_values() -> None: assert verifier.parse_environment(b"A=one=two\0B=\0") == {"A": "one=two", "B": ""} @pytest.mark.parametrize("value", [0, -1, 1.5, "0", "01", "+1", "-3", "not-a-pid", True]) def test_pid_must_be_a_positive_integer(value: object) -> None: with pytest.raises(verifier.VerificationError) as caught: verifier.validate_pid(value) # type: ignore[arg-type] assert caught.value.code == "invalid_pid" def test_cli_errors_are_canonical_sanitized_json_and_exit_one(capsys: pytest.CaptureFixture[str]) -> None: secret_input = "not-a-pid-secret-marker" returncode = verifier.main(["--pid", secret_input]) output = capsys.readouterr() assert returncode == 1 assert output.err == "" assert json.loads(output.out) == {"error": {"code": "invalid_pid"}, "status": "fail"} assert secret_input not in output.out @pytest.mark.skipif(not sys.platform.startswith("linux"), reason="requires Linux /proc/PID/environ") def test_cli_reads_real_process_environment_and_detects_effective_conflict( capsys: pytest.CaptureFixture[str], ) -> None: expected = verifier.expected_environment() good_process = subprocess.Popen( ["/bin/sleep", "30"], env=expected, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) try: wait_for_process_environment(good_process, expected) assert verifier.main(["--pid", str(good_process.pid)]) == 0 good_output = capsys.readouterr() assert good_output.err == "" assert json.loads(good_output.out)["status"] == "pass" finally: good_process.terminate() good_process.wait(timeout=5) conflicting = dict(expected) conflicting["TELEO_CLOUDSQL_USER"] = "postgres" conflicting["GOOGLE_APPLICATION_CREDENTIALS"] = "/tmp/broader-credential.json" bad_process = subprocess.Popen( ["/bin/sleep", "30"], env=conflicting, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) try: wait_for_process_environment(bad_process, conflicting) assert verifier.main(["--pid", str(bad_process.pid)]) == 1 bad_output = capsys.readouterr() receipt = json.loads(bad_output.out) assert bad_output.err == "" assert receipt == { "error": {"code": "forbidden_environment_fields", "fields": ["GOOGLE_APPLICATION_CREDENTIALS"]}, "status": "fail", } assert "postgres" not in bad_output.out assert "/tmp/broader-credential.json" not in bad_output.out finally: bad_process.terminate() bad_process.wait(timeout=5) def test_unexpected_read_error_is_generic_and_does_not_echo_path( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], ) -> None: sensitive_marker = "sensitive-proc-read-detail" def fail_read(_self: object) -> bytes: raise OSError(sensitive_marker) monkeypatch.setattr(verifier.Path, "read_bytes", fail_read) assert verifier.main(["--pid", str(os.getpid())]) == 1 output = capsys.readouterr() assert json.loads(output.out) == {"error": {"code": "internal_error"}, "status": "fail"} assert sensitive_marker not in output.out