diff --git a/aish.spec b/aish.spec index 4f3b021..ade05c7 100644 --- a/aish.spec +++ b/aish.spec @@ -201,6 +201,7 @@ a_sandbox = Analysis( datas=[], hiddenimports=[ 'aish.security.sandbox_daemon', + 'aish.security.sandbox_worker', ], hookspath=[], hooksconfig={}, diff --git a/src/aish/sandboxd.py b/src/aish/sandboxd.py index d2f8584..98ab0c5 100644 --- a/src/aish/sandboxd.py +++ b/src/aish/sandboxd.py @@ -13,6 +13,11 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( prog="aish-sandboxd", description="Privileged sandbox daemon for aish" ) + parser.add_argument( + "--sandbox-worker", + action="store_true", + help=argparse.SUPPRESS, + ) parser.add_argument( "--socket-path", default=str(DEFAULT_SANDBOX_SOCKET_PATH), @@ -21,6 +26,11 @@ def main(argv: list[str] | None = None) -> int: args = parser.parse_args(argv) + if args.sandbox_worker: + from aish.security import sandbox_worker + + return sandbox_worker.main() + if os.geteuid() != 0: print("aish-sandboxd must run as root", file=sys.stderr) return 2 diff --git a/src/aish/security/sandbox_daemon.py b/src/aish/security/sandbox_daemon.py index 01612f6..2276ee5 100644 --- a/src/aish/security/sandbox_daemon.py +++ b/src/aish/security/sandbox_daemon.py @@ -31,6 +31,23 @@ _LOG_DETAIL_MAX_CHARS = 4096 +def _build_worker_command() -> list[str]: + worker_cmd = [ + "unshare", + "--mount", + "--propagation", + "private", + "--", + sys.executable, + ] + if getattr(sys, "frozen", False): + worker_cmd.append("--sandbox-worker") + return worker_cmd + + worker_cmd.extend(["-m", "aish.security.sandbox_worker"]) + return worker_cmd + + def _elapsed_ms(start_ts: float) -> int: return int((time.monotonic() - start_ts) * 1000) @@ -743,16 +760,7 @@ def _simulate_for_user( "timeout_s": timeout_s, } - worker_cmd = [ - "unshare", - "--mount", - "--propagation", - "private", - "--", - sys.executable, - "-m", - "aish.security.sandbox_worker", - ] + worker_cmd = _build_worker_command() exec_timeout: Optional[float] = None if timeout_s is not None: exec_timeout = float(timeout_s) + 10.0 diff --git a/tests/security/sandbox/test_daemon_isolation.py b/tests/security/sandbox/test_daemon_isolation.py index 54b0800..219cf32 100644 --- a/tests/security/sandbox/test_daemon_isolation.py +++ b/tests/security/sandbox/test_daemon_isolation.py @@ -2,11 +2,13 @@ import json import subprocess +import sys from pathlib import Path import pytest from aish.security.sandbox import SandboxUnavailableError +from aish.security import sandbox_daemon from aish.security.sandbox_daemon import SandboxDaemon, SandboxDaemonConfig @@ -14,6 +16,41 @@ def _make_daemon() -> SandboxDaemon: return SandboxDaemon(SandboxDaemonConfig(socket_path=Path("/tmp/aish-test.sock"))) +def test_build_worker_command_uses_module_entrypoint(monkeypatch): + monkeypatch.delattr(sys, "frozen", raising=False) + monkeypatch.setattr(sys, "executable", "/usr/bin/python3") + + cmd = sandbox_daemon._build_worker_command() + + assert cmd == [ + "unshare", + "--mount", + "--propagation", + "private", + "--", + "/usr/bin/python3", + "-m", + "aish.security.sandbox_worker", + ] + + +def test_build_worker_command_uses_internal_entrypoint_when_frozen(monkeypatch): + monkeypatch.setattr(sys, "frozen", True, raising=False) + monkeypatch.setattr(sys, "executable", "/usr/bin/aish-sandbox") + + cmd = sandbox_daemon._build_worker_command() + + assert cmd == [ + "unshare", + "--mount", + "--propagation", + "private", + "--", + "/usr/bin/aish-sandbox", + "--sandbox-worker", + ] + + def test_simulate_for_user_uses_unshare_worker(monkeypatch): daemon = _make_daemon() diff --git a/tests/security/sandbox/test_sandboxd.py b/tests/security/sandbox/test_sandboxd.py new file mode 100644 index 0000000..196766f --- /dev/null +++ b/tests/security/sandbox/test_sandboxd.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from aish import sandboxd +from aish.security import sandbox_worker + + +def test_sandboxd_main_dispatches_internal_worker(monkeypatch): + monkeypatch.setattr(sandbox_worker, "main", lambda: 17) + + assert sandboxd.main(["--sandbox-worker"]) == 17