from __future__ import annotations import os import tempfile import unittest from pathlib import Path from unittest.mock import patch from core import procs from core.proc_wrapper import _shell_argv as wrapper_shell_argv from tools.shell import ShellTool class ShellSecurityTests(unittest.TestCase): def test_shell_argv_uses_explicit_interpreter(self) -> None: argv = procs.shell_argv("echo ok") wrapper_argv = wrapper_shell_argv("echo ok") self.assertEqual(argv, wrapper_argv) self.assertEqual(argv[-1], "echo ok") if os.name == "nt": self.assertEqual(argv[1:4], ["/d", "/s", "/c"]) else: self.assertEqual(argv[:2], ["/bin/sh", "-c"]) def test_foreground_shell_still_executes_command(self) -> None: with tempfile.TemporaryDirectory() as tmp: result = ShellTool(base_dir=Path(tmp)).execute("echo shell-ok") self.assertIn("shell-ok", result) self.assertIn("[exit 0]", result) def test_background_shell_persists_explicit_argv(self) -> None: with tempfile.TemporaryDirectory() as tmp, patch( "tools.shell.procs.count_running", return_value=0 ), patch("tools.shell.procs.launch_host", return_value=("proc-1", Path(tmp))) as launch: result = ShellTool(base_dir=Path(tmp)).execute( "echo background-ok", background=True ) self.assertIn("proc_id=proc-1", result) kwargs = launch.call_args.kwargs self.assertEqual(kwargs["argv"], procs.shell_argv("echo background-ok")) self.assertNotIn("shell_cmd", kwargs) if __name__ == "__main__": unittest.main()