"""Contracts for bounded, screened command execution in the handoff harness.""" from __future__ import annotations import subprocess from testing.tests.test_hermes_handoff_support import ( FakeClock, FakeSpawn, load_handoff_module, ) runner_module = load_handoff_module("hermes_handoff_exec") policy = load_handoff_module("hermes_handoff_policy") def completed(stdout: str = "", stderr: str = "", returncode: int = 0): return subprocess.CompletedProcess(args=[], returncode=returncode, stdout=stdout, stderr=stderr) def build(results: list, **kwargs) -> tuple: clock = FakeClock() spawn = FakeSpawn(results) runner = runner_module.Runner(clock=clock, spawn=spawn, environ={}, **kwargs) return (runner, spawn, clock) OPERATOR = runner_module.operator_vantage() def test_a_successful_command_is_captured_and_screened() -> None: runner, spawn, _ = build([completed(stdout="password=a-long-enough-secret\n")]) outcome = runner.run(("kubectl", "get", "pods"), OPERATOR) assert outcome.ok and outcome.ran assert "[redacted]" in outcome.stdout assert outcome.returncode == 0 assert spawn.calls[0]["argv"] == ["kubectl", "get", "pods"] assert runner.commands_run == 1 def test_a_failing_command_is_not_ok_but_still_ran() -> None: runner, _, _ = build([completed(stderr="Error from server (Forbidden)", returncode=1)]) outcome = runner.run(("kubectl", "get", "pods"), OPERATOR) assert outcome.ran and not outcome.ok assert "Forbidden" in outcome.combined def test_a_policy_violation_never_reaches_a_subprocess() -> None: runner, spawn, _ = build([]) outcome = runner.run(("kubectl", "delete", "pods", "--all"), OPERATOR) assert outcome.error and outcome.error.startswith("policy:") assert not outcome.ran assert spawn.calls == [] assert runner.commands_run == 0 def test_a_timeout_is_recorded_rather_than_raised() -> None: runner, _, _ = build([subprocess.TimeoutExpired(cmd="kubectl", timeout=1.0)]) outcome = runner.run(("kubectl", "get", "pods"), OPERATOR) assert outcome.error == runner_module.TIMEOUT_ERROR assert not outcome.ran def test_a_missing_binary_is_recorded_rather_than_raised() -> None: runner, _, _ = build([FileNotFoundError(2, "No such file or directory")]) outcome = runner.run(("kubectl", "get", "pods"), OPERATOR) assert outcome.error and outcome.error.startswith("spawn:") def test_the_run_deadline_stops_further_probes_without_spawning() -> None: runner, spawn, clock = build([completed(stdout="first")], deadline_seconds=10.0) assert runner.run(("kubectl", "get", "pods"), OPERATOR).ok clock.advance(11.0) second = runner.run(("kubectl", "get", "nodes"), OPERATOR) assert second.error == runner_module.DEADLINE_ERROR assert len(spawn.calls) == 1 assert runner.remaining_seconds < 0 def test_the_command_timeout_is_clamped_by_the_remaining_deadline() -> None: runner, spawn, clock = build([completed()], command_timeout=30.0, deadline_seconds=10.0) clock.advance(6.0) runner.run(("kubectl", "get", "pods"), OPERATOR) assert spawn.calls[0]["timeout"] == 4.0 def test_output_is_bounded_per_command_and_can_be_raised_per_call() -> None: runner, _, _ = build([completed(stdout="y" * 200), completed(stdout="y" * 200)], max_bytes=50) small = runner.run(("kubectl", "get", "pods"), OPERATOR) large = runner.run(("kubectl", "get", "pods"), OPERATOR, max_bytes=4096) assert small.truncated assert not large.truncated def test_the_child_environment_is_rebuilt_from_an_allowlist() -> None: source = {"PATH": "/bin", "KUBECONFIG": "/tmp/kc", "ANTHROPIC_API_KEY": "leak", "HOME": "/root"} environment = runner_module.build_environment(OPERATOR, source) assert environment == {"PATH": "/bin", "KUBECONFIG": "/tmp/kc", "HOME": "/root", "LC_ALL": "C"} assert "ANTHROPIC_API_KEY" not in environment def test_a_vantage_can_override_the_environment_it_needs() -> None: vantage = runner_module.operator_vantage(kubeconfig="/tmp/operator.yaml") environment = runner_module.build_environment(vantage, {"KUBECONFIG": "/tmp/other"}) assert environment["KUBECONFIG"] == "/tmp/operator.yaml" assert "kubeconfig-pinned" in vantage.description def test_the_operator_context_is_inserted_only_for_context_aware_tools() -> None: vantage = runner_module.operator_vantage(context="atlas-operator") assert vantage.wrap(("kubectl", "get", "pods"))[:3] == ("kubectl", "--context", "atlas-operator") assert vantage.wrap(("git", "status")) == ("git", "status") assert vantage.wrap(()) == () def test_the_pod_vantage_execs_into_the_container_and_drops_the_context() -> None: operator = runner_module.operator_vantage(context="atlas-operator") vantage = runner_module.pod_vantage("hermes", "hermes-agent-1", "hermes", operator) wrapped = vantage.wrap(("kubectl", "get", "pods")) assert wrapped[:3] == ("kubectl", "--context", "atlas-operator") assert wrapped[3] == "exec" assert wrapped[-4:] == ("--", "kubectl", "get", "pods") assert vantage.description == "in-pod hermes/hermes-agent-1[hermes]" policy.check_argv(wrapped) def test_a_pod_vantage_without_an_operator_still_addresses_the_pod() -> None: vantage = runner_module.pod_vantage("hermes", "pod-a", "hermes") assert vantage.wrap(("kubectl", "version"))[0] == "kubectl" assert vantage.env == {} def test_outcome_serialisation_screens_the_command_line() -> None: outcome = runner_module.Outcome( argv=("git", "clone", "https://user:a-long-enough-secret@scm.example.dev/x"), vantage="operator", returncode=0, stdout="ok", ) payload = outcome.as_dict() assert "a-long-enough-secret" not in payload["command"] assert payload["vantage"] == "operator" assert payload["stdout"] == "ok" def test_combined_output_joins_only_the_streams_that_carry_text() -> None: assert runner_module.Outcome(argv=(), vantage="x", stdout="a", stderr="b").combined == "a\nb" assert runner_module.Outcome(argv=(), vantage="x", stdout="a").combined == "a" assert runner_module.Outcome(argv=(), vantage="x").combined == ""