Files
reyna-cli/tests/test_prebuilt_app_install.py
Adolfo Reyna 9fd04b0ce4 feat(privacy-host): add signed native calendar contacts and reminders host
Add the owner-only AF_UNIX Reyna CLI privacy host, strict signed-app installation, and typed native routing for Calendar, Contacts, and Reminders.\n\nAdd bounded system-status paths and config-only direct local-service wrappers. Preserve MacMiniMCP pending explicit cutover approval.\n\nApple Notes is intentionally deferred: no native Notes operations, Apple Events declaration, or Automation helper are included; legacy Notes handling remains untouched.
2026-08-03 20:27:54 -04:00

541 lines
20 KiB
Python

"""TDD for secure explicit prebuilt signed app install bridge — no DerivedData scan, no secret leak."""
from __future__ import annotations
import os
import plistlib
import stat
from pathlib import Path
import pytest
from typer.testing import CliRunner
from reyna_cli.cli import app
runner = CliRunner()
def _mock_proc(rc=0, stdout="", stderr=""):
from types import SimpleNamespace
return SimpleNamespace(returncode=rc, stdout=stdout, stderr=stderr)
def _make_fake_app_bundle(base: Path, bundle_name: str = "Reyna CLI.app", bundle_id: str = "com.reyna.cli.privacy-host"):
"""Create a minimal .app bundle directory with Info.plist and executable."""
from reyna_cli import app_bundle as ab
bundle = base / bundle_name
contents = bundle / "Contents"
macos = contents / "MacOS"
macos.mkdir(parents=True, exist_ok=True)
exe = macos / "ReynaCLIHost"
exe.write_bytes(b"fakebinarycontent")
exe.chmod(0o755)
plist_dict = ab.build_app_bundle_info_plist_dict()
# allow custom bundle_id for negative tests
if bundle_id != plist_dict.get("CFBundleIdentifier"):
plist_dict = dict(plist_dict)
plist_dict["CFBundleIdentifier"] = bundle_id
with open(contents / "Info.plist", "wb") as f:
plistlib.dump(plist_dict, f)
return bundle
# ----------------------------------------------------------------------
# app_bundle layer
# ----------------------------------------------------------------------
def test_validate_app_bundle_at_path_success(tmp_path):
from reyna_cli import app_bundle as ab
src_root = tmp_path / "src"
src_root.mkdir()
bundle = _make_fake_app_bundle(src_root)
def run_ok(args, cwd=None, **kwargs):
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0)
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=TEAM123\nAuthority=Apple Development: Foo (TEAM123)\n")
return _mock_proc(rc=0)
res = ab.validate_app_bundle_at_path(bundle, runner=run_ok)
assert res["ok"] is True
assert res["signature_verified"] is True
assert res["is_ad_hoc"] is False
assert res["bundle_identifier_matches"] is True
def test_validate_app_bundle_at_path_rejects_relative(tmp_path):
from reyna_cli import app_bundle as ab
rel = Path("relative/Reyna CLI.app")
res = ab.validate_app_bundle_at_path(rel)
assert res["ok"] is False
assert any("absolute" in e.lower() for e in res["errors"])
def test_validate_app_bundle_at_path_rejects_wrong_suffix(tmp_path):
from reyna_cli import app_bundle as ab
p = tmp_path / "notapp"
p.mkdir()
res = ab.validate_app_bundle_at_path(p)
assert res["ok"] is False
assert any(".app" in e.lower() for e in res["errors"])
def test_validate_app_bundle_at_path_rejects_symlink(tmp_path):
from reyna_cli import app_bundle as ab
real_root = tmp_path / "real"
real_root.mkdir()
bundle = _make_fake_app_bundle(real_root)
link = tmp_path / "Reyna CLI.app"
link.symlink_to(bundle)
res = ab.validate_app_bundle_at_path(link)
assert res["ok"] is False
assert any("symlink" in e.lower() for e in res["errors"])
def test_validate_app_bundle_at_path_rejects_ad_hoc(tmp_path):
from reyna_cli import app_bundle as ab
src_root = tmp_path / "src"
src_root.mkdir()
bundle = _make_fake_app_bundle(src_root)
def run_adhoc(args, cwd=None, **kwargs):
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0)
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="Signature=adhoc\nTeamIdentifier=not set\n")
return _mock_proc(rc=0)
res = ab.validate_app_bundle_at_path(bundle, runner=run_adhoc)
assert res["ok"] is False
assert res["is_ad_hoc"] is True
def test_install_prebuilt_app_bundle_success_no_xcodebuild(tmp_path):
from reyna_cli import app_bundle as ab
src_root = tmp_path / "gui-build"
src_root.mkdir()
bundle = _make_fake_app_bundle(src_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
calls = []
def runner(args, cwd=None, **kwargs):
assert isinstance(args, list)
calls.append(list(args))
if args and args[0] == "xcodebuild":
raise AssertionError("xcodebuild must NOT be called on prebuilt path")
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0)
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=TEAM123\n")
return _mock_proc(rc=0)
res = ab.install_prebuilt_app_bundle(source_bundle_path=bundle, repo_root=repo_root, runner=runner)
assert res["ok"] is True
assert res["action"] == "install_prebuilt_app_bundle"
assert "signature_verified" in res
# Must have copied to dist
dist_bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app"
assert dist_bundle.exists()
assert (dist_bundle / "Contents" / "MacOS" / "ReynaCLIHost").exists()
# No xcodebuild calls
assert all(c[0] != "xcodebuild" for c in calls)
# Must have called codesign validation for source and copy (at least 2 verifies)
verify_calls = [c for c in calls if c[:3] == ["codesign", "--verify", "--deep"]]
assert len(verify_calls) >= 2
def test_install_prebuilt_app_bundle_invalid_prevents_copy(tmp_path, monkeypatch):
from reyna_cli import app_bundle as ab
src_root = tmp_path / "gui-build"
src_root.mkdir()
bundle = _make_fake_app_bundle(src_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
def runner_bad(args, cwd=None, **kwargs):
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=1, stderr="verify fail")
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=not set\n")
return _mock_proc(rc=0)
# Patch copy to detect if called
copied = {"called": False}
original_copy = ab._copy_app_bundle_atomic
def tracking_copy(src, dst):
copied["called"] = True
return original_copy(src, dst)
monkeypatch.setattr(ab, "_copy_app_bundle_atomic", tracking_copy)
res = ab.install_prebuilt_app_bundle(source_bundle_path=bundle, repo_root=repo_root, runner=runner_bad)
assert res["ok"] is False
assert copied["called"] is False, "must not copy if source validation fails"
dist_bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app"
assert not dist_bundle.exists()
def test_install_prebuilt_app_bundle_rejects_symlink_and_relative(tmp_path):
from reyna_cli import app_bundle as ab
real_root = tmp_path / "real"
real_root.mkdir()
bundle = _make_fake_app_bundle(real_root)
link = tmp_path / "Reyna CLI.app"
link.symlink_to(bundle)
repo_root = tmp_path / "repo"
repo_root.mkdir()
def runner(args, cwd=None, **kwargs):
return _mock_proc(rc=0)
res_link = ab.install_prebuilt_app_bundle(source_bundle_path=link, repo_root=repo_root, runner=runner)
assert res_link["ok"] is False
assert "symlink" in res_link["error"].lower()
# Relative
rel = Path("relative/Reyna CLI.app")
res_rel = ab.install_prebuilt_app_bundle(source_bundle_path=rel, repo_root=repo_root, runner=runner)
assert res_rel["ok"] is False
assert "absolute" in res_rel["error"].lower()
# Wrong suffix
wrong = tmp_path / "wrong.appstuff"
wrong.mkdir()
res_suffix = ab.install_prebuilt_app_bundle(source_bundle_path=wrong, repo_root=repo_root, runner=runner)
assert res_suffix["ok"] is False
assert ".app" in res_suffix["error"].lower()
def test_install_prebuilt_no_secret_leak_in_result(tmp_path):
from reyna_cli import app_bundle as ab
src_root = tmp_path / "src"
src_root.mkdir()
bundle = _make_fake_app_bundle(src_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
secret_team = "TEAM_SUPERSECRET123"
def runner(args, cwd=None, **kwargs):
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0, stdout=f"Authority=Secret {secret_team}")
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr=f"TeamIdentifier={secret_team}\nAuthority=Apple Development: Foo ({secret_team})\n")
return _mock_proc(rc=0)
res = ab.install_prebuilt_app_bundle(source_bundle_path=bundle, repo_root=repo_root, runner=runner)
assert res["ok"] is True
# Stringified result must not contain raw team identifier output (we filter generically)
# Our runner purposely returns secret in stderr, but result should not echo stderr verbatim
import json
res_str = json.dumps(res)
# The implementation stores no raw codesign output, only booleans
assert "Authority=Apple" not in res_str
assert secret_team not in res_str or res.get("validation", {}).get("signature_verified") is True and secret_team not in str(res.get("validation", {}).get("errors", []))
# ----------------------------------------------------------------------
# privacy_host layer — prebuilt route
# ----------------------------------------------------------------------
def test_privacy_host_install_prebuilt_success_no_xcodebuild(tmp_path):
from reyna_cli import privacy_host as ph_mod, app_bundle as ab_mod
src_root = tmp_path / "gui"
src_root.mkdir()
src_bundle = _make_fake_app_bundle(src_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
sock = tmp_path / "priv" / "reyna-cli.sock"
plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist"
logd = tmp_path / "Logs"
calls = []
def fake_runner(args, cwd=None, **kwargs):
assert isinstance(args, list)
calls.append(list(args))
if args and args[0] == "xcodebuild":
raise AssertionError("xcodebuild must not be called when prebuilt path supplied")
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0)
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=TEAM123\n")
return _mock_proc(rc=0)
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,
prebuilt_app_bundle_path=src_bundle,
)
assert result["ok"] is True
assert plist.exists()
# No xcodebuild
assert all(c[0] != "xcodebuild" for c in calls)
# Must have bootout+bootstrap
assert ["launchctl", "bootout", "gui/501/com.reyna.cli.privacy-host"] in calls
assert ["launchctl", "bootstrap", "gui/501", str(plist)] in calls
# Dist bundle exists
dist_bundle = repo_root / "native" / "ReynaCLIHost" / "dist" / "Reyna CLI.app"
assert dist_bundle.exists()
def test_privacy_host_install_prebuilt_invalid_prevents_plist_and_launchctl(tmp_path):
from reyna_cli import privacy_host as ph_mod
src_root = tmp_path / "gui"
src_root.mkdir()
src_bundle = _make_fake_app_bundle(src_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
sock = tmp_path / "priv" / "reyna-cli.sock"
plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist"
logd = tmp_path / "Logs"
calls = []
def fake_runner(args, cwd=None, **kwargs):
calls.append(list(args))
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=1, stderr="fail")
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=not set\nSignature=adhoc\n")
return _mock_proc(rc=0)
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,
prebuilt_app_bundle_path=src_bundle,
)
assert result["ok"] is False
assert not plist.exists(), "plist must not be written if prebuilt validation fails"
# No launchctl should have been called
launch_calls = [c for c in calls if c and c[0] == "launchctl"]
assert len(launch_calls) == 0, f"launchctl must not be called on invalid source, got {launch_calls}"
def test_privacy_host_install_prebuilt_rejects_symlink_and_relative(tmp_path):
from reyna_cli import privacy_host as ph_mod
real_root = tmp_path / "real"
real_root.mkdir()
real_bundle = _make_fake_app_bundle(real_root)
repo_root = tmp_path / "repo"
repo_root.mkdir()
plist = tmp_path / "LaunchAgents" / "com.reyna.cli.privacy-host.plist"
link = tmp_path / "Reyna CLI.app"
link.symlink_to(real_bundle)
def runner(args, cwd=None, **kwargs):
return _mock_proc(rc=0)
res_link = ph_mod.install_privacy_host_service(
runner=runner,
uid=501,
repo_root=repo_root,
plist_path=plist,
prebuilt_app_bundle_path=link,
)
assert res_link["ok"] is False
assert "symlink" in res_link["error"].lower()
# Relative
rel = Path("relative/Reyna CLI.app")
res_rel = ph_mod.install_privacy_host_service(
runner=runner,
uid=501,
repo_root=repo_root,
plist_path=plist,
prebuilt_app_bundle_path=rel,
)
assert res_rel["ok"] is False
assert "absolute" in res_rel["error"].lower()
def test_privacy_host_install_default_still_calls_xcodebuild(tmp_path):
from reyna_cli import privacy_host as ph_mod, app_bundle as ab_mod
repo_root = tmp_path / "repo"
repo_root.mkdir()
(repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost.xcodeproj").mkdir(parents=True)
(repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost").mkdir(parents=True, exist_ok=True)
(repo_root / "native" / "ReynaCLIHost" / "ReynaCLIHost" / "Info.plist").write_text("<plist></plist>")
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"bin")
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"
calls = []
def fake_runner(args, cwd=None, **kwargs):
calls.append(list(args))
if args and args[0] == "xcodebuild":
return _mock_proc(rc=0)
if args[:3] == ["codesign", "--verify", "--deep"]:
return _mock_proc(rc=0)
if args[:2] == ["codesign", "-dv"]:
return _mock_proc(rc=0, stderr="TeamIdentifier=TEAM123\n")
return _mock_proc(rc=0)
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 ID",
prebuilt_app_bundle_path=None,
)
assert result["ok"] is True
xcode_calls = [c for c in calls if c and c[0] == "xcodebuild"]
assert len(xcode_calls) >= 1, "default install must still call xcodebuild"
# ----------------------------------------------------------------------
# CLI wiring
# ----------------------------------------------------------------------
def test_cli_install_has_app_bundle_option():
res = runner.invoke(app, ["privacy-host", "install", "--help"])
assert res.exit_code == 0
out = res.stdout.lower()
assert "app-bundle" in out
def test_cli_install_app_bundle_prebuilt_success_mocked(tmp_path, monkeypatch):
from reyna_cli import privacy_host as ph_mod
src_root = tmp_path / "gui"
src_root.mkdir()
src_bundle = _make_fake_app_bundle(src_root)
captured = {}
def fake_install(prebuilt_app_bundle_path=None, **kwargs):
captured["prebuilt"] = prebuilt_app_bundle_path
assert prebuilt_app_bundle_path is not None
assert Path(prebuilt_app_bundle_path).is_absolute()
assert str(prebuilt_app_bundle_path).endswith(".app")
return {"ok": True, "action": "install", "app_bundle_path": str(prebuilt_app_bundle_path)}
monkeypatch.setattr(ph_mod, "install_privacy_host_service", lambda **kw: fake_install(**kw))
res = runner.invoke(app, ["privacy-host", "install", "--app-bundle", str(src_bundle), "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["prebuilt"] == src_bundle
def test_cli_install_app_bundle_rejects_relative_and_symlink(tmp_path, monkeypatch):
from reyna_cli import privacy_host as ph_mod
# Should fail at CLI layer before calling service, for relative
def should_not_be_called(**kwargs):
raise AssertionError("service must not be called when CLI rejects path")
monkeypatch.setattr(ph_mod, "install_privacy_host_service", lambda **kw: should_not_be_called(**kw))
# Relative
res_rel = runner.invoke(app, ["privacy-host", "install", "--app-bundle", "relative/Reyna CLI.app", "--json"])
assert res_rel.exit_code != 0
assert "absolute" in res_rel.stdout.lower()
# Symlink
real_root = tmp_path / "real"
real_root.mkdir()
real_bundle = _make_fake_app_bundle(real_root)
link = tmp_path / "Reyna CLI.app"
link.symlink_to(real_bundle)
res_link = runner.invoke(app, ["privacy-host", "install", "--app-bundle", str(link), "--json"])
assert res_link.exit_code != 0
assert "symlink" in res_link.stdout.lower()
def test_cli_install_default_no_app_bundle_calls_build(tmp_path, monkeypatch):
from reyna_cli import privacy_host as ph_mod
captured = {}
def fake_install(prebuilt_app_bundle_path=None, **kwargs):
captured["prebuilt"] = prebuilt_app_bundle_path
return {"ok": True, "action": "install", "app_bundle_path": "/fake/dist/Reyna CLI.app"}
monkeypatch.setattr(ph_mod, "install_privacy_host_service", lambda **kw: fake_install(**kw))
res = runner.invoke(app, ["privacy-host", "install", "--json"])
assert res.exit_code == 0, res.stdout + res.stderr
assert captured["prebuilt"] is None, "default should not pass prebuilt path"
def test_no_deriveddata_scan_in_prebuilt_code():
from pathlib import Path
src_app_bundle = Path("/Users/adolforeyna/Projects/reyna-cli/src/reyna_cli/app_bundle.py").read_text()
src_privacy_host = Path("/Users/adolforeyna/Projects/reyna-cli/src/reyna_cli/privacy_host.py").read_text()
# Prebuilt functions must not scan DerivedData automatically
# They should not listdir or glob DerivedData without explicit path
# Check that install_prebuilt_app_bundle does not reference DerivedData path discovery
assert "install_prebuilt_app_bundle" in src_app_bundle
# Ensure function body does not contain DerivedData scan (like os.walk or glob of DerivedData)
# Simple heuristic: function definition area should not contain "DerivedData" search
import re
# Extract install_prebuilt function
m = re.search(r"def install_prebuilt_app_bundle.*?^def ", src_app_bundle, flags=re.DOTALL | re.MULTILINE)
if m:
func_text = m.group(0)
# Should not contain "DerivedData" except maybe in comments about NOT using it
# Allow at most trivial mention, but not listdir/glob
assert "glob" not in func_text.lower() or "deriveddata" not in func_text.lower()
assert "os.scandir" not in func_text.lower()
assert "os.walk" not in func_text.lower()
# privacy_host prebuilt path should not call build_app_bundle
# It should have conditional: if prebuilt_app_bundle_path is not None -> install_prebuilt, else build
assert "prebuilt_app_bundle_path" in src_privacy_host