From fd10463069d8e4d218696510709f16e26359492d Mon Sep 17 00:00:00 2001 From: Yukipukii1 Date: Fri, 24 Apr 2026 14:38:00 +0300 Subject: [PATCH] fix(env): safely quote ~/ subpaths in wrapped cd commands --- tests/tools/test_base_environment.py | 16 ++++++++++++++++ tools/environments/base.py | 18 ++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/tools/test_base_environment.py b/tests/tools/test_base_environment.py index 913ad0387..28ce08e84 100644 --- a/tests/tools/test_base_environment.py +++ b/tests/tools/test_base_environment.py @@ -60,6 +60,22 @@ class TestWrapCommand: assert "cd ~" in wrapped assert "cd '~'" not in wrapped + def test_tilde_subpath_with_spaces_uses_home_and_quotes_suffix(self): + env = _TestableEnv() + env._snapshot_ready = True + wrapped = env._wrap_command("ls", "~/my repo") + + assert "cd $HOME/'my repo'" in wrapped + assert "cd ~/my repo" not in wrapped + + def test_tilde_slash_maps_to_home(self): + env = _TestableEnv() + env._snapshot_ready = True + wrapped = env._wrap_command("ls", "~/") + + assert "cd $HOME" in wrapped + assert "cd ~/" not in wrapped + def test_cd_failure_exit_126(self): env = _TestableEnv() env._snapshot_ready = True diff --git a/tools/environments/base.py b/tools/environments/base.py index d89b66f19..4510b1749 100644 --- a/tools/environments/base.py +++ b/tools/environments/base.py @@ -368,6 +368,17 @@ class BaseEnvironment(ABC): # Command wrapping # ------------------------------------------------------------------ + @staticmethod + def _quote_cwd_for_cd(cwd: str) -> str: + """Quote a ``cd`` target while preserving ``~`` expansion.""" + if cwd == "~": + return cwd + if cwd == "~/": + return "$HOME" + if cwd.startswith("~/"): + return f"$HOME/{shlex.quote(cwd[2:])}" + return shlex.quote(cwd) + def _wrap_command(self, command: str, cwd: str) -> str: """Build the full bash script that sources snapshot, cd's, runs command, re-dumps env vars, and emits CWD markers.""" @@ -379,10 +390,9 @@ class BaseEnvironment(ABC): if self._snapshot_ready: parts.append(f"source {self._snapshot_path} 2>/dev/null || true") - # cd to working directory — let bash expand ~ natively - quoted_cwd = ( - shlex.quote(cwd) if cwd != "~" and not cwd.startswith("~/") else cwd - ) + # Preserve bare ``~`` expansion, but rewrite ``~/...`` through + # ``$HOME`` so suffixes with spaces remain a single shell word. + quoted_cwd = self._quote_cwd_for_cd(cwd) parts.append(f"builtin cd {quoted_cwd} || exit 126") # Run the actual command