from __future__ import annotations import importlib.util import json from pathlib import Path from types import SimpleNamespace import pytest _SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "run_repopulse_adoption.py" _SPEC = importlib.util.spec_from_file_location("run_repopulse_adoption", _SCRIPT) assert _SPEC is not None and _SPEC.loader is not None handoff = importlib.util.module_from_spec(_SPEC) _SPEC.loader.exec_module(handoff) def test_fixed_operator_sequence_keeps_credential_out_of_result( monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setattr(handoff, "_require_root", lambda: None) intent_id = "rpa-" + "a" * 20 calls: list[str] = [] secret = "operator-secret-value" * 4 def appops( *arguments: str, payload: bytes | None = None, **_kwargs: object ) -> dict: if "prepare" in arguments: calls.append("prepare") return {"status": "prepared", "intentId": intent_id} if "backend.app.services.repopulse_proxy_stage" in arguments: calls.append("proxy") return {"status": "ready", "intentId": intent_id} if "-c" in arguments: calls.append("handoff") return { "intentId": intent_id, "idempotencySha256": "b" * 64, "preflightSha256": "c" * 64, "preflightProjection": {}, "targetCredential": secret, } assert "finalize" in arguments assert payload is not None and secret.encode() not in payload calls.append("finalize") return {"status": "completed", "intentId": intent_id} def controller( command: list[str], *, payload: bytes | None = None, **_kwargs: object ) -> dict: if command[-1].endswith("stage_repopulse_target_attestation.py"): calls.append("attestation") return {"status": "staged", "intentId": intent_id} assert command[-1].endswith("repopulse_adoption_cli.py") assert json.loads(payload or b"{}")["targetCredential"] == secret calls.append("controller") return {"status": "deployment_verified", "intentId": intent_id} monkeypatch.setattr(handoff, "_appops", appops) monkeypatch.setattr(handoff, "_run", controller) result = handoff.run() assert result == {"status": "completed", "intentId": intent_id} assert secret not in json.dumps(result) assert calls == [ "prepare", "proxy", "attestation", "handoff", "controller", "finalize", ] def test_proxy_failure_prevents_secret_handoff(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(handoff, "_require_root", lambda: None) calls: list[str] = [] def appops(*arguments: str, **_kwargs: object) -> dict: calls.append(arguments[-1]) if "prepare" in arguments: return {"status": "prepared", "intentId": "rpa-" + "a" * 20} return {"status": "blocked"} monkeypatch.setattr(handoff, "_appops", appops) monkeypatch.setattr( handoff, "_run", lambda *_args, **_kwargs: pytest.fail("controller must not execute"), ) with pytest.raises(handoff.AdoptionHandoffError, match="operator_step_blocked"): handoff.run() assert len(calls) == 2 def test_operator_subprocess_uses_a_fixed_environment( monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setenv("PYTHONPATH", "/untrusted/modules") observed: dict = {} def invoke(command, **kwargs): observed.update(kwargs) assert command == ["python3", "fixed.py"] return SimpleNamespace(returncode=0, stdout=b'{"status":"ready"}', stderr=b"") monkeypatch.setattr(handoff.subprocess, "run", invoke) assert handoff._run(["python3", "fixed.py"]) == {"status": "ready"} assert "PYTHONPATH" not in observed["env"] assert observed["env"]["PATH"].startswith("/usr/local/sbin:") def test_operator_rejects_ambiguous_child_json(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( handoff.subprocess, "run", lambda *_args, **_kwargs: SimpleNamespace( returncode=0, stdout=b'{"status":"ready","status":"blocked"}', stderr=b"", ), ) with pytest.raises(handoff.AdoptionHandoffError, match="operator_result_invalid"): handoff._run(["python3", "fixed.py"])