124 lines
4.3 KiB
Python
124 lines
4.3 KiB
Python
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"])
|