teleo-infrastructure/tests/test_gcp_leoclean_service_environment.py
2026-07-15 04:40:26 +02:00

281 lines
9.8 KiB
Python

import json
import os
import subprocess
import sys
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 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_CONFIG", "CLOUDSDK_*"),
("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"),
],
)
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:
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:
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