From d201e491c0b0a201b41814b9e00880824fdbddbb Mon Sep 17 00:00:00 2001 From: kina0630 Date: Tue, 8 Sep 2026 09:58:35 +0800 Subject: [PATCH] fix(shell): skip git URLs in path-escape check bash_run sandbox_mode=none treated https Git URLs as absolute paths and blocked clone. --- coding-agent/src/coding_agent/tools/shell.py | 20 +++++ coding-agent/tests/tools/test_shell.py | 85 ++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/coding-agent/src/coding_agent/tools/shell.py b/coding-agent/src/coding_agent/tools/shell.py index 97fbd7417..4f6e33de4 100644 --- a/coding-agent/src/coding_agent/tools/shell.py +++ b/coding-agent/src/coding_agent/tools/shell.py @@ -17,6 +17,16 @@ from agentkit.tools import tool _DISALLOWED_TOKENS = {"&&", "||", "|", ";", ">", ">>", "<", "2>", "&"} +_REMOTE_URL_SCHEMES = ( + "https://", + "http://", + "git+https://", + "git+http://", + "git+ssh://", + "ssh://", + "git://", +) + _STRUCTURED_RESULTS: ContextVar[bool] = ContextVar( "coding_agent_shell_structured_results", default=False ) @@ -295,12 +305,22 @@ def _validated_execution_cwd( return str(resolved_cwd) +def _looks_like_remote_url(arg: str) -> bool: + stripped = arg.strip().strip("'\"") + lowered = stripped.lower() + if lowered.startswith(_REMOTE_URL_SCHEMES): + return True + return lowered.startswith("git@") and ":" in stripped[4:] + + def _validate_no_path_escape( args: list[str], workspace_root: Path, additional_roots: tuple[Path, ...] = (), ) -> None: for arg in args[1:]: + if _looks_like_remote_url(arg): + continue for match in re.findall(r"(?