"""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("") 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