From 1f1dbd65ce95e4b849201c435d65990ba200fb9b Mon Sep 17 00:00:00 2001 From: georgex8001 Date: Fri, 24 Apr 2026 03:32:19 +0800 Subject: [PATCH] fix(modal): normalize cwd and timeout in shared modal exec prep Harden modal_utils command preparation by trimming blank cwd values and coercing invalid or non-positive timeout inputs to safe defaults, preventing transport-level failures from malformed runtime parameters. Made-with: Cursor --- tests/tools/test_modal_utils.py | 49 +++++++++++++++++++++++++++++++ tools/environments/modal_utils.py | 22 ++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/tools/test_modal_utils.py diff --git a/tests/tools/test_modal_utils.py b/tests/tools/test_modal_utils.py new file mode 100644 index 000000000..eaa7d4a25 --- /dev/null +++ b/tests/tools/test_modal_utils.py @@ -0,0 +1,49 @@ +from tools.environments.modal_utils import BaseModalExecutionEnvironment, ModalExecStart + + +class _DummyModalEnv(BaseModalExecutionEnvironment): + def __init__(self, cwd: str = "/root", timeout: int = 60): + super().__init__(cwd=cwd, timeout=timeout) + + def _prepare_command(self, command: str): + return command, None + + def _start_modal_exec(self, prepared): + _ = prepared + return ModalExecStart(immediate_result={"output": "", "returncode": 0}) + + def _poll_modal_exec(self, handle): + _ = handle + return None + + def _cancel_modal_exec(self, handle): + _ = handle + + def cleanup(self): + pass + + +def test_prepare_modal_exec_trims_blank_cwd(): + env = _DummyModalEnv(cwd="/root") + prepared = env._prepare_modal_exec("echo hi", cwd=" ", timeout=5) + assert prepared.cwd == "/root" + + +def test_prepare_modal_exec_uses_default_timeout_when_non_positive(): + env = _DummyModalEnv(timeout=60) + prepared_zero = env._prepare_modal_exec("echo hi", timeout=0) + prepared_negative = env._prepare_modal_exec("echo hi", timeout=-3) + assert prepared_zero.timeout == 60 + assert prepared_negative.timeout == 60 + + +def test_prepare_modal_exec_coerces_string_timeout(): + env = _DummyModalEnv(timeout=60) + prepared = env._prepare_modal_exec("echo hi", timeout="7") # type: ignore[arg-type] + assert prepared.timeout == 7 + + +def test_prepare_modal_exec_invalid_timeout_falls_back(): + env = _DummyModalEnv(timeout=45) + prepared = env._prepare_modal_exec("echo hi", timeout="bad") # type: ignore[arg-type] + assert prepared.timeout == 45 diff --git a/tools/environments/modal_utils.py b/tools/environments/modal_utils.py index 4d68399e4..468d0ecbb 100644 --- a/tools/environments/modal_utils.py +++ b/tools/environments/modal_utils.py @@ -155,8 +155,8 @@ class BaseModalExecutionEnvironment(BaseEnvironment): timeout: int | None = None, stdin_data: str | None = None, ) -> PreparedModalExec: - effective_cwd = cwd or self.cwd - effective_timeout = timeout or self.timeout + effective_cwd = self._normalize_cwd(cwd) + effective_timeout = self._normalize_timeout(timeout) exec_command = command exec_stdin = stdin_data if self._stdin_mode == "payload" else None @@ -174,6 +174,24 @@ class BaseModalExecutionEnvironment(BaseEnvironment): stdin_data=exec_stdin, ) + def _normalize_cwd(self, cwd: str) -> str: + candidate = (cwd or "").strip() + if candidate: + return candidate + fallback = (self.cwd or "").strip() + return fallback or "/root" + + def _normalize_timeout(self, timeout: int | None) -> int: + candidate = self.timeout if timeout is None else timeout + try: + parsed = int(candidate) + except (TypeError, ValueError): + parsed = int(self.timeout) if isinstance(self.timeout, int) else 0 + if parsed <= 0: + fallback = int(self.timeout) if isinstance(self.timeout, int) else 0 + parsed = fallback if fallback > 0 else 60 + return parsed + def _result(self, output: str, returncode: int) -> dict: return { "output": output,