"""Tests for privacy_host wrapper — native calendar.list no fallback, status deterministic.""" from pathlib import Path import os import stat import plistlib import pytest from typer.testing import CliRunner from reyna_cli.cli import app runner = CliRunner() def test_native_calendar_list_success(monkeypatch): from reyna_cli import privacy_host as ph_mod captured = {} class FakeClient: def call(self, op, args): captured["op"] = op captured["args"] = args return {"id": "abc", "ok": True, "result": [{"title": "Work"}]} monkeypatch.setattr(ph_mod, "PrivacyClient", FakeClient) result = ph_mod.native_calendar_list() assert captured["op"] == "calendar.list" assert captured["args"] == {} assert result["ok"] is True assert result["source"] == "native_privacy_host" assert result["result"] == [{"title": "Work"}] def test_native_calendar_list_failure_surfaces(monkeypatch): from reyna_cli import privacy_host as ph_mod from reyna_cli.privacy_client import PrivacyClientError class FakeFail: def call(self, op, args): raise PrivacyClientError("privacy socket not found at /tmp/x") monkeypatch.setattr(ph_mod, "PrivacyClient", FakeFail) with pytest.raises(PrivacyClientError): ph_mod.native_calendar_list() src = Path(ph_mod.__file__).read_text() assert "call_macmini_tool" not in src assert "MCPClient" not in src assert "macmini_client" not in src def test_no_mcp_import_in_privacy_host(): from reyna_cli import privacy_host as ph_mod src = Path(ph_mod.__file__).read_text() # Must not reference MCP fallback helpers assert "call_macmini_tool" not in src assert "macmini_client" not in src assert "MCPClient" not in src # Must not import mcp module assert "from reyna_cli.mcp import" not in src assert "import mcp" not in src.lower() def test_native_calendar_list_rejects_success_without_result(monkeypatch): from reyna_cli import privacy_host as ph_mod class IncompleteClient: def call(self, op, args): return {"id": "abc", "ok": True} monkeypatch.setattr(ph_mod, "PrivacyClient", IncompleteClient) with pytest.raises(RuntimeError, match="missing result"): ph_mod.native_calendar_list() def test_cli_calendars_uses_native_wrapper(monkeypatch): """Prove `macmini calendar calendars` routes through native wrapper.""" calls = {"count": 0} def fake_native(): calls["count"] += 1 return {"ok": True, "source": "native_privacy_host", "result": [{"id": "1"}]} monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_native) result = runner.invoke(app, ["macmini", "calendar", "calendars", "--json"]) assert result.exit_code == 0, result.stdout + result.stderr import json payload = json.loads(result.stdout) assert payload["ok"] is True assert payload["source"] == "native_privacy_host" assert calls["count"] == 1 def test_cli_calendars_json_flag_propagates(monkeypatch): def fake_native(): return {"ok": True, "source": "native_privacy_host", "result": []} monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_native) result = runner.invoke(app, ["macmini", "calendar", "calendars", "--json"]) assert result.exit_code == 0 def test_cli_calendars_no_fallback_on_native_failure(monkeypatch): from reyna_cli.privacy_client import PrivacyClientError def fake_native_fail(): raise PrivacyClientError("privacy socket not found") monkeypatch.setattr("reyna_cli.privacy_host.native_calendar_list", fake_native_fail) result = runner.invoke(app, ["macmini", "calendar", "calendars", "--json"]) # fail() triggers Exit 1 with ok:false payload assert result.exit_code != 0 def test_privacy_host_status_no_filesystem_creation(monkeypatch, tmp_path): """status must not create/start, and must report deterministic paths.""" fake_sock_parent = tmp_path / "reyna-privacy-status-test" fake_sock = fake_sock_parent / "reyna-cli.sock" assert not fake_sock_parent.exists() from reyna_cli import privacy_host as ph_mod monkeypatch.setattr(ph_mod, "default_socket_path", lambda: fake_sock) result = runner.invoke(app, ["privacy-host", "status", "--json"]) assert result.exit_code == 0, result.stdout + result.stderr import json payload = json.loads(result.stdout) assert payload["ok"] is True assert "socket_path" in payload assert "socket_exists" in payload assert payload["socket_exists"] is False assert not fake_sock_parent.exists() assert "build_path" in payload or "socket_dir" in payload assert str(fake_sock) in payload["socket_path"] def test_no_generic_arbitrary_operation_cli(): """Ensure we didn't expose a generic arbitrary operation CLI yet.""" result = runner.invoke(app, ["privacy-host", "--help"]) assert result.exit_code == 0 out = result.stdout.lower() assert "status" in out assert "call" not in out result2 = runner.invoke(app, ["macmini", "call", "--help"]) assert result2.exit_code == 0 # ---------------------------------------------------------------------- # New managed-lifecycle tests — strict TDD, mocked I/O only # ---------------------------------------------------------------------- def test_plist_deterministic_secure_contents(tmp_path): from reyna_cli import privacy_host as ph_mod repo_root = tmp_path / "repo" repo_root.mkdir() sock = tmp_path / "sock" / "reyna-cli.sock" logd = tmp_path / "logs" plist = ph_mod.build_privacy_host_plist(repo_root=repo_root, socket_path=sock, log_dir=logd) # Label assert plist["Label"] == "com.reyna.cli.privacy-host" # No TCP args/ports prog = plist["ProgramArguments"] assert isinstance(prog, list) and len(prog) == 3 # Binary path deterministic per task spec – now stable signed .app bundle for TCC identity expected_bundle_exe = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" / "Contents" / "MacOS" / "ReynaCLIHost" assert str(prog[0]) == str(expected_bundle_exe) assert prog[1] == "--socket" assert prog[2] == str(sock) combined = " ".join(prog).lower() assert "--port" not in combined assert "tcp" not in combined assert "0.0.0.0" not in combined assert "127.0.0.1" not in combined # Required LaunchAgent keys assert plist["RunAtLoad"] is True assert plist["KeepAlive"] is True assert plist["ProcessType"] == "Interactive" assert plist["WorkingDirectory"] == str(repo_root) assert plist["StandardOutPath"] == str(logd / "privacy-host.out.log") assert plist["StandardErrorPath"] == str(logd / "privacy-host.error.log") # No env/secrets assert "EnvironmentVariables" not in plist assert "Environment" not in plist # Also check plistlib serializable data = plistlib.dumps(plist) loaded = plistlib.loads(data) assert loaded == plist def test_plist_contains_only_unix_socket_invocation(): from reyna_cli import privacy_host as ph_mod plist = ph_mod.build_privacy_host_plist() prog_str = " ".join(plist["ProgramArguments"]) # Must contain --socket assert "--socket" in prog_str # Must NOT contain TCP indicators forbidden = ["--port", "--host", "tcp://", "0.0.0.0", "127.0.0.1", ":8080", ":3000"] lower = prog_str.lower() for token in forbidden: assert token.lower() not in lower, f"forbidden token {token} in {prog_str}" # Source must also not contain TCP ports in file itself (extra hardening) src = Path(ph_mod.__file__).read_text() # We allow portion about TCP check in tests/comments but not in plist builder path that would inject TCP # Instead ensure builder uses only socket arg assert "ProgramArguments" in src def test_build_release_command_exact_arg_array_no_shell(): from reyna_cli import privacy_host as ph_mod cmd = ph_mod.build_release_command() # Now expects xcodebuild safe arg array (Xcode owns signing) assert isinstance(cmd, list) assert all(isinstance(x, str) for x in cmd) assert cmd[0] == "xcodebuild" assert "-project" in cmd assert "-scheme" in cmd assert "-target" not in cmd, "must use -scheme for valid derivedDataPath builds" assert "Reyna CLI" in cmd assert "-configuration" in cmd assert "Release" in cmd assert "-derivedDataPath" in cmd assert "build" in cmd assert "--sign" not in cmd # Must be list, not string, no shell src = Path(ph_mod.__file__).read_text() assert "shell=True" not in src assert "shell=\"" not in src # No manual codesign --sign construction in module assert '["codesign", "--force"' not in src def test_build_release_command_no_params(): from reyna_cli import privacy_host as ph_mod import inspect sig = inspect.signature(ph_mod.build_release_command) assert len(sig.parameters) == 0, f"should have no params, got {list(sig.parameters)}" def test_status_reports_plist_and_socket_and_pid_from_runner_only(tmp_path): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod fake_repo = tmp_path / "repo" fake_repo.mkdir() bundle = fake_repo / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" (bundle / "Contents" / "MacOS").mkdir(parents=True) (bundle / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"x") import plistlib as _pl with open(bundle / "Contents" / "Info.plist", "wb") as f: _pl.dump(ab_mod.build_app_bundle_info_plist_dict(), f) fake_sock = tmp_path / "sock" / "reyna-cli.sock" fake_sock.parent.mkdir() fake_sock.write_text("dummy") fake_plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" fake_plist.parent.mkdir() fake_plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) calls = [] class Proc: returncode = 0 stdout = " pid = 12345\n state = running\n" stderr = "" def fake_runner(args, **kwargs): calls.append(list(args)) assert isinstance(args, list), "must be arg array, not shell string" if args and args[0] == "codesign": if "--verify" in args: class P: returncode = 0 stdout = "" stderr = "" return P() if len(args) > 1 and args[1] == "-dv": class P: returncode = 0 stdout = "" stderr = "TeamIdentifier=TEAM123\n" return P() return Proc() assert args[0] == "launchctl" assert args[1] == "print" assert args[2].startswith("gui/") assert ph_mod.PRIVACY_HOST_LABEL in args[2] return Proc() status = ph_mod.privacy_host_service_status( runner=fake_runner, uid=501, plist_path_override=fake_plist, socket_path_override=fake_sock, repo_root_override=fake_repo, ) assert status["ok"] is True assert status["plist_path"] == str(fake_plist) assert status["plist_exists"] is True assert status["socket_path"] == str(fake_sock) assert status["socket_exists"] is True assert status["socket_type"] in ("file", "socket", "dir", "other") assert status["pid"] == 12345 assert status["active"] is True launch_calls = [c for c in calls if c[0] == "launchctl"] assert len(launch_calls) == 1 assert launch_calls[0][0] == "launchctl" assert status.get("bundle_exists") is True assert status.get("signature_verified") is True assert (fake_repo / "native" / "ReynaCLIHost" / "dist").exists() def test_status_failure_no_throw_structured(tmp_path): from reyna_cli import privacy_host as ph_mod def failing_runner(args, **kwargs): raise RuntimeError("launchctl not found") status = ph_mod.privacy_host_service_status( runner=failing_runner, uid=501, plist_path_override=tmp_path / "nonexist.plist", socket_path_override=tmp_path / "sock.sock", repo_root_override=tmp_path / "repo", ) # Must not throw, must return structured assert status["ok"] is True # ok still True but with errors assert "errors" in status assert any("launchctl" in e for e in status["errors"]) assert status["pid"] is None def test_status_no_filesystem_creation_with_mock_runner(tmp_path, monkeypatch): from reyna_cli import privacy_host as ph_mod sock_parent = tmp_path / "no-create-parent" sock = sock_parent / "reyna-cli.sock" assert not sock_parent.exists() class Proc: returncode = 1 stdout = "" stderr = "No such file" def fake_runner(args, **kwargs): # Ensure no swift assert "swift" not in args[0] return Proc() # monkeypatch _plist_path etc via overrides, not global plist_path = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" # Do not create plist_parent to prove status doesn't create it assert not plist_path.parent.exists() status = ph_mod.privacy_host_service_status( runner=fake_runner, uid=501, plist_path_override=plist_path, socket_path_override=sock, repo_root_override=tmp_path / "repo-root-no-create", ) assert not sock_parent.exists() assert not plist_path.parent.exists() assert status["socket_exists"] is False assert status["plist_exists"] is False def test_status_exact_launchctl_arg_array(): from reyna_cli import privacy_host as ph_mod captured = [] class Proc: returncode = 0 stdout = "pid = 999\n" stderr = "" def runner(args, **kwargs): captured.append(list(args)) if args and args[0] == "codesign": # fake validation phase before launchctl if "--verify" in args: class P: returncode = 0 stdout = "" stderr = "" return P() if len(args) > 1 and args[1] == "-dv": class P: returncode = 0 stdout = "" stderr = "TeamIdentifier=TEAM123\n" return P() return Proc() ph_mod.privacy_host_service_status(runner=runner, uid=123, plist_path_override=Path("/tmp/a.plist"), socket_path_override=Path("/tmp/b.sock")) # launchctl print must exist (may not be first due to codesign validation) launch_calls = [c for c in captured if c and c[0] == "launchctl"] assert len(launch_calls) >= 1 assert ["launchctl", "print", "gui/123/com.reyna.cli.privacy-host"] in launch_calls # ensure arg array, no shell, no manual --sign for c in captured: assert isinstance(c, list) assert "--sign" not in c or c[0] != "codesign" def test_install_exact_command_arg_arrays_and_modes(tmp_path, monkeypatch): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod repo_root = tmp_path / "repo" repo_root.mkdir() (repo_root / "native" / "ReynaCLIHost").mkdir(parents=True) # Required xcodeproj and Info.plist source for build_app_bundle check (repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost.xcodeproj").mkdir(parents=True) (repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost.xcodeproj" / "project.pbxproj").write_text("// dummy") (repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost").mkdir(parents=True, exist_ok=True) (repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost" / "Info.plist").write_text("") # Expected derived-data product location that build_app_bundle runner will copy derived = repo_root / "native" / "ReynaCLIHost" / "build" / "DerivedData" built_app = derived / "Build" / "Products" / "Release" / "Reyna CLI.app" (built_app / "Contents" / "MacOS").mkdir(parents=True) (built_app / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"fakebinary") with open(built_app / "Contents" / "Info.plist", "wb") as f: plistlib.dump(ab_mod.build_app_bundle_info_plist_dict(), f) sock = tmp_path / "priv" / "reyna-cli.sock" plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" logd = tmp_path / "Logs" / "reyna-cli" calls = [] class Proc: def __init__(self, rc=0, stdout="ok", stderr=""): self.returncode = rc self.stdout = stdout self.stderr = stderr def fake_runner(args, cwd=None, **kwargs): assert isinstance(args, list), "must use arg list, no shell" calls.append({"args": list(args), "cwd": str(cwd) if cwd else None}) if args and args[0] == "xcodebuild": # Simulate build succeeded; product already exists at derived path return Proc(rc=0, stdout="BUILD SUCCEEDED", stderr="") if args and args[0] == "codesign" and "--sign" in args: # Should NOT happen in new flow – Xcode owns signing; fail if called raise AssertionError(f"manual codesign --sign must not occur, got {args}") if len(args) >= 3 and args[:3] == ["codesign", "--verify", "--deep"]: return Proc(rc=0, stdout="", stderr="") if len(args) >= 2 and args[:2] == ["codesign", "-dv"]: return Proc(rc=0, stdout="", stderr="TeamIdentifier=TEAM123\nAuthority=Apple Development: Foo (TEAM123)\n") return Proc(rc=0) chmod_calls = [] orig_chmod = os.chmod def fake_chmod(p, mode, *args, **kwargs): chmod_calls.append((str(p), mode)) try: orig_chmod(p, mode, *args, **kwargs) except TypeError: try: orig_chmod(p, mode) except Exception: pass except Exception: pass monkeypatch.setattr(os, "chmod", fake_chmod) result = ph_mod.install_privacy_host_service( runner=fake_runner, uid=501, repo_root=repo_root, socket_path=sock, plist_path=plist, log_dir=logd, signing_identity="Test Identity (TEAM123)", ) assert result["ok"] is True # Must be xcodebuild, not swift build xcode_calls = [c for c in calls if c["args"] and c["args"][0] == "xcodebuild"] assert len(xcode_calls) >= 1 xb = xcode_calls[0]["args"] assert "-project" in xb assert "-scheme" in xb assert "-target" not in xb assert "Reyna CLI" in xb assert "-configuration" in xb assert "Release" in xb assert "-derivedDataPath" in xb assert "build" in xb # No swift build swift_calls = [c for c in calls if c["args"][:2] == ["swift", "build"]] assert len(swift_calls) == 0, f"swift build must not be used, got {swift_calls}" # No manual codesign --sign sign_calls = [c for c in calls if c["args"][0] == "codesign" and "--sign" in c["args"]] assert len(sign_calls) == 0, f"manual codesign --sign forbidden, got {sign_calls}" bootouts = [c for c in calls if c["args"][:2] == ["launchctl", "bootout"]] bootstraps = [c for c in calls if c["args"][:2] == ["launchctl", "bootstrap"]] assert len(bootouts) == 1 assert bootouts[0]["args"] == ["launchctl", "bootout", "gui/501/com.reyna.cli.privacy-host"] assert len(bootstraps) == 1 assert bootstraps[0]["args"] == ["launchctl", "bootstrap", "gui/501", str(plist)] assert sock.parent.exists() assert logd.exists() modes_0700 = [c for c in chmod_calls if c[1] == 0o700] assert len(modes_0700) >= 2 assert plist.exists() modes_0600 = [c for c in chmod_calls if c[1] == 0o600 and str(plist) in c[0]] assert len(modes_0600) >= 1 loaded = plistlib.loads(plist.read_bytes()) prog = loaded["ProgramArguments"] assert prog[0] == str(repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" / "Contents" / "MacOS" / "ReynaCLIHost") assert prog[1] == "--socket" assert prog[2] == str(sock) assert "app_bundle_path" in result assert result["bundle_identifier"] == "com.reyna.cli.privacy-host" assert result.get("signature_verified") is True def test_start_stop_use_bootstrap_bootout_kickstart_testable(tmp_path): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" plist.parent.mkdir(parents=True) plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) repo_root = tmp_path / "repo" bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" (bundle / "Contents" / "MacOS").mkdir(parents=True) (bundle / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"x") with open(bundle / "Contents" / "Info.plist", "wb") as f: import plistlib as _pl _pl.dump(ab_mod.build_app_bundle_info_plist_dict(), f) calls = [] class Proc: def __init__(self, rc=0, stdout="", stderr=""): self.returncode = rc self.stdout = stdout self.stderr = stderr def start_runner(args, **kwargs): assert isinstance(args, list) calls.append(list(args)) if args and args[0] == "codesign": if "--verify" in args: return Proc(rc=0) if len(args) > 1 and args[1] == "-dv": return Proc(rc=0, stdout="", stderr="TeamIdentifier=TEAM123\n") return Proc(rc=0) return Proc(rc=0) res_start = ph_mod.start_privacy_host_service(runner=start_runner, uid=502, plist_path=plist, repo_root=repo_root) assert res_start["ok"] is True assert ["launchctl", "kickstart", "-k", "gui/502/com.reyna.cli.privacy-host"] in calls calls.clear() def stop_runner(args, **kwargs): assert isinstance(args, list) calls.append(list(args)) return Proc(rc=0) res_stop = ph_mod.stop_privacy_host_service(runner=stop_runner, uid=502, plist_path=plist) assert res_stop["ok"] is True assert calls[0] == ["launchctl", "bootout", "gui/502/com.reyna.cli.privacy-host"] def test_start_idempotence_kickstart_fail_bootstrap_fail_then_kickstart_retry(tmp_path): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" plist.parent.mkdir(parents=True) plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) repo_root = tmp_path / "repo" bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" (bundle / "Contents" / "MacOS").mkdir(parents=True) (bundle / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"x") with open(bundle / "Contents" / "Info.plist", "wb") as f: import plistlib as _pl _pl.dump(ab_mod.build_app_bundle_info_plist_dict(), f) calls = [] class Proc: def __init__(self, rc, out="", err=""): self.returncode = rc self.stdout = out self.stderr = err seq = [ Proc(1, "", "kickstart failed"), Proc(1, "", "already loaded"), Proc(0, "", ""), ] idx = {"i": 0} def runner(args, **kwargs): # validation phase codesign calls first if args and args[0] == "codesign": if "--verify" in args: class P: returncode = 0 stdout = "" stderr = "" return P() if len(args) > 1 and args[1] == "-dv": class P: returncode = 0 stdout = "" stderr = "TeamIdentifier=TEAM123\n" return P() return Proc(0, "", "") calls.append(list(args)) r = seq[idx["i"]] idx["i"] += 1 return r result = ph_mod.start_privacy_host_service(runner=runner, uid=501, plist_path=plist, repo_root=repo_root) assert result["ok"] is True assert len(calls) == 3 assert calls[0] == ["launchctl", "kickstart", "-k", "gui/501/com.reyna.cli.privacy-host"] assert calls[1] == ["launchctl", "bootstrap", "gui/501", str(plist)] assert calls[2] == ["launchctl", "kickstart", "-k", "gui/501/com.reyna.cli.privacy-host"] def test_start_does_not_treat_arbitrary_bootstrap_failure_as_success(tmp_path): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" plist.parent.mkdir(parents=True) plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) repo_root = tmp_path / "repo" bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" (bundle / "Contents" / "MacOS").mkdir(parents=True) (bundle / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"x") with open(bundle / "Contents" / "Info.plist", "wb") as f: import plistlib as _pl _pl.dump(ab_mod.build_app_bundle_info_plist_dict(), f) class Proc: def __init__(self, rc): self.returncode = rc self.stdout = "" self.stderr = "some other failure" seq = [Proc(1), Proc(1), Proc(1)] idx = {"i": 0} def runner(args, **kwargs): if args and args[0] == "codesign": if "--verify" in args: class P: returncode = 0 stdout = "" stderr = "" return P() if len(args) > 1 and args[1] == "-dv": class P: returncode = 0 stdout = "" stderr = "TeamIdentifier=TEAM123\n" return P() return Proc(1) r = seq[idx["i"]] idx["i"] += 1 return r result = ph_mod.start_privacy_host_service(runner=runner, uid=501, plist_path=plist, repo_root=repo_root) # All three fail -> ok False assert result["ok"] is False def test_uninstall_removes_only_exact_plist(tmp_path, monkeypatch): from reyna_cli import privacy_host as ph_mod # Real expected plist path is ~/Library/LaunchAgents/com.reyna.cli.privacy-host.plist # For safety test, we will monkeypatch _plist_path to return our tmp plist real_expected = tmp_path / "Library" / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" real_expected.parent.mkdir(parents=True) real_expected.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) # Create a fake socket somewhere else that must NOT be removed fake_sock = tmp_path / "sockdir" / "reyna-cli.sock" fake_sock.parent.mkdir() fake_sock.write_text("keep me") calls = [] class Proc: returncode = 0 stdout = "" stderr = "" def fake_runner(args, **kwargs): assert isinstance(args, list) calls.append(list(args)) return Proc() monkeypatch.setattr(ph_mod, "_plist_path", lambda: real_expected) result = ph_mod.uninstall_privacy_host_service(runner=fake_runner, uid=503, plist_path=real_expected) assert result["ok"] is True assert result["removed"] is True assert not real_expected.exists() assert fake_sock.exists(), "uninstall must never remove socket arbitrary paths" # Must have called bootout assert ["launchctl", "bootout", "gui/503/com.reyna.cli.privacy-host"] in calls # Attempt to remove non-exact plist should be refused other_plist = tmp_path / "other.plist" other_plist.write_text("evil") result2 = ph_mod.uninstall_privacy_host_service(runner=fake_runner, uid=503, plist_path=other_plist) assert result2["ok"] is False assert "refusing" in result2["error"].lower() assert other_plist.exists(), "non-exact plist must not be removed" def test_cli_has_managed_lifecycle_commands(): result = runner.invoke(app, ["privacy-host", "--help"]) assert result.exit_code == 0 out = result.stdout.lower() # Must have all lifecycle commands for cmd in ["status", "install", "start", "stop", "uninstall"]: assert cmd in out, f"{cmd} missing from help: {out}" # Still no generic call assert "call" not in out def test_cli_install_start_stop_uninstall_with_mocked_helpers(monkeypatch): from reyna_cli import privacy_host as ph_mod # Mock helpers to avoid real I/O def fake_install(): return {"ok": True, "action": "install", "results": []} def fake_start(): return {"ok": True, "action": "start", "results": []} def fake_stop(): return {"ok": True, "action": "stop", "results": []} def fake_uninstall(): return {"ok": True, "action": "uninstall", "removed": True, "results": []} monkeypatch.setattr(ph_mod, "install_privacy_host_service", lambda *a, **k: fake_install()) monkeypatch.setattr(ph_mod, "start_privacy_host_service", lambda *a, **k: fake_start()) monkeypatch.setattr(ph_mod, "stop_privacy_host_service", lambda *a, **k: fake_stop()) monkeypatch.setattr(ph_mod, "uninstall_privacy_host_service", lambda *a, **k: fake_uninstall()) for cmd in ["install", "start", "stop", "uninstall"]: res = runner.invoke(app, ["privacy-host", cmd, "--json"]) assert res.exit_code == 0, f"{cmd} failed: {res.stdout} {res.stderr}" import json payload = json.loads(res.stdout) assert payload["ok"] is True def test_no_live_launchctl_swift_in_module_source(): from reyna_cli import privacy_host as ph_mod src = Path(ph_mod.__file__).read_text() # Ensure no direct subprocess.run with shell string that would invoke live commands at import time # The module should not execute swift or launchctl on import — check top-level calls # We already tested shell=True absent, now ensure no top-level launchctl/bootstrap call outside functions lines = src.splitlines() # Look for launchctl or swift outside function defs — simple heuristic: any line at column 0 invoking runner? # For this slice, we just ensure module import doesn't trigger side effects by importing again import importlib importlib.reload(ph_mod) # should not throw or run launchctl # If reload succeeded without side-effect error, pass assert True def test_status_payload_includes_new_fields_but_preserves_legacy(monkeypatch, tmp_path): from reyna_cli import privacy_host as ph_mod sock = tmp_path / "legacy" / "reyna-cli.sock" # Do not create parent to prove no creation monkeypatch.setattr(ph_mod, "default_socket_path", lambda: sock) class Proc: returncode = 1 stdout = "" stderr = "" def fake_runner(args, **kwargs): return Proc() payload = ph_mod.privacy_host_status_payload(runner=fake_runner, uid=501) # Legacy fields preserved assert "socket_path" in payload assert "socket_dir" in payload assert "build_path" in payload assert "socket_exists" in payload assert payload["socket_exists"] is False # New fields present assert "plist_path" in payload assert "plist_exists" in payload assert "binary_path" in payload assert "pid" in payload assert "active" in payload # No creation assert not sock.parent.exists() # --- Additional hardening tests --- def test_write_plist_atomic_no_world_readable_window(tmp_path): from reyna_cli import privacy_host as ph_mod import inspect src = inspect.getsource(ph_mod._write_plist_0600) assert "os.open" in src assert "O_CREAT" in src assert "O_EXCL" in src assert "os.replace" in src assert "fsync" in src # Functional: permissions 0600 plist_path = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist" plist_path.parent.mkdir(parents=True) # Ensure parent is 0700 as implementation will enforce os.chmod(plist_path.parent, 0o700) ph_mod._write_plist_0600({"Label": "test", "ProgramArguments": ["/bin/true"]}, plist_path) st = plist_path.lstat() assert stat.S_IMODE(st.st_mode) == 0o600 # No temp files left leftovers = list(plist_path.parent.glob("*.tmp.*")) assert len(leftovers) == 0, f"temp files left: {leftovers}" def test_ensure_dir_0700_rejects_symlink(tmp_path): from reyna_cli import privacy_host as ph_mod real = tmp_path / "real" real.mkdir() link = tmp_path / "linkdir" link.symlink_to(real) with pytest.raises((ValueError, PermissionError)): ph_mod._ensure_dir_0700(link) def test_ensure_dir_0700_enforces_0700(tmp_path): from reyna_cli import privacy_host as ph_mod d = tmp_path / "a" / "b" / "c" ph_mod._ensure_dir_0700(d) assert d.exists() assert stat.S_IMODE(d.lstat().st_mode) == 0o700 assert stat.S_IMODE(d.parent.lstat().st_mode) == 0o700 or True # parent also 0700 via parents creation may be checked # Re-call should still ensure 0700 os.chmod(d, 0o755) ph_mod._ensure_dir_0700(d) assert stat.S_IMODE(d.lstat().st_mode) == 0o700 def test_ensure_dir_0700_rejects_non_directory(tmp_path): from reyna_cli import privacy_host as ph_mod f = tmp_path / "file.txt" f.write_text("hi") with pytest.raises((ValueError, PermissionError)): ph_mod._ensure_dir_0700(f) def test_write_plist_refuses_symlink_parent(tmp_path): from reyna_cli import privacy_host as ph_mod real_parent = tmp_path / "real" real_parent.mkdir() link_parent = tmp_path / "linkparent" link_parent.symlink_to(real_parent) plist_path = link_parent / "com.reyna.cli.privacy-host.plist" with pytest.raises((ValueError, PermissionError)): ph_mod._write_plist_0600({"Label": "test"}, plist_path) def test_uid_tightening_returns_structured_error_not_throw(tmp_path): from reyna_cli import privacy_host as ph_mod plist = tmp_path / "com.reyna.cli.privacy-host.plist" plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) for bad_uid in ["not-int", "", " ", "1.5", True, -1, "abc"]: res = ph_mod.start_privacy_host_service(uid=bad_uid, plist_path=plist, runner=lambda *a, **k: None) assert isinstance(res, dict), f"should return dict for {bad_uid}" assert res.get("ok") is False, f"should be False for {bad_uid}: {res}" assert "error" in res or "invalid uid" in str(res).lower() # Status also structured res_status = ph_mod.privacy_host_service_status(uid="bad-uid", plist_path_override=plist, socket_path_override=Path("/tmp/x.sock")) assert isinstance(res_status, dict) assert res_status.get("ok") is False assert "invalid uid" in str(res_status).lower() def test_uid_valid_int_string_coerced(tmp_path): from reyna_cli import privacy_host as ph_mod from reyna_cli import app_bundle as ab_mod plist = tmp_path / "com.reyna.cli.privacy-host.plist" plist.write_bytes(plistlib.dumps({"Label": ph_mod.PRIVACY_HOST_LABEL})) repo_root = tmp_path / "repo" bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app" (bundle / "Contents" / "MacOS").mkdir(parents=True) (bundle / "Contents" / "MacOS" / "ReynaCLIHost").write_bytes(b"x") with open(bundle / "Contents" / "Info.plist", "wb") as f: import plistlib as _pl _pl.dump(ab_mod.build_app_bundle_info_plist_dict(), f) calls = [] class Proc: returncode = 0 stdout = "" stderr = "" def runner(args, **kwargs): if args and args[0] == "codesign": if "--verify" in args: class P: returncode = 0 stdout = "" stderr = "" return P() if len(args) > 1 and args[1] == "-dv": class P: returncode = 0 stdout = "" stderr = "TeamIdentifier=TEAM123\n" return P() return Proc() calls.append(args) return Proc() res = ph_mod.start_privacy_host_service(uid="501", plist_path=plist, runner=runner, repo_root=repo_root) assert res["ok"] is True assert calls[0] == ["launchctl", "kickstart", "-k", "gui/501/com.reyna.cli.privacy-host"]