From 1344be8ae7b6943547e0b09f10f8087ec7e509c8 Mon Sep 17 00:00:00 2001 From: can1357 Date: Tue, 23 Jun 2026 20:26:44 +0200 Subject: [PATCH] fix(python/robomp): restricted URLs starting with a hyphen in proxy server - Added a check to reject remote URLs that begin with a hyphen to prevent command-line option injection. - Updated the test suite to verify that option-shaped URLs are correctly blocked. --- python/robomp/src/proxy/server.py | 2 ++ python/robomp/tests/test_proxy_server.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/python/robomp/src/proxy/server.py b/python/robomp/src/proxy/server.py index 018cf8b07..e90022f6f 100644 --- a/python/robomp/src/proxy/server.py +++ b/python/robomp/src/proxy/server.py @@ -288,6 +288,8 @@ def _remote_auth_for_url(url: str, expected_repo: str, token: str) -> _RemoteAut raise HTTPException(400, "remote url must not be empty or padded") if _FORBIDDEN_URL_BYTES_RE.search(raw): raise HTTPException(400, "remote url contains forbidden control bytes") + if raw.startswith("-"): + raise HTTPException(400, "remote url must not start with '-'") if _REMOTE_HELPER_RE.match(raw): raise HTTPException(400, "git remote helper transports are disabled") scheme = (urlparse(raw).scheme or "").lower() diff --git a/python/robomp/tests/test_proxy_server.py b/python/robomp/tests/test_proxy_server.py index 369298303..26b8df665 100644 --- a/python/robomp/tests/test_proxy_server.py +++ b/python/robomp/tests/test_proxy_server.py @@ -1129,6 +1129,25 @@ async def test_git_fetch_rejects_ext_remote_helper(proxy_settings: Settings, ups assert resp.status_code == 400, resp.text +async def test_git_fetch_rejects_option_shaped_origin(proxy_settings: Settings, upstream_repo: Path) -> None: + pool_dir = _stage_pool(proxy_settings, upstream_repo) + config_path = pool_dir / ".git" / "config" + config_text = config_path.read_text(encoding="utf-8") + config_path.write_text(config_text.replace(f"\turl = {upstream_repo}\n", "\turl = --upload-pack=env\n"), encoding="utf-8") + + app = _build_app(proxy_settings) + body = b'{"repo":"octo/widget"}' + async with await _async_client(app) as client: + resp = await client.post( + "/gh/v1/git/fetch", + content=body, + headers={**_signed("POST", "/gh/v1/git/fetch", body), "Content-Type": "application/json"}, + ) + + assert resp.status_code == 400, resp.text + + + @pytest.mark.parametrize( "clone_url", @@ -1139,6 +1158,7 @@ async def test_git_fetch_rejects_ext_remote_helper(proxy_settings: Settings, ups "http://github.com/octo/widget.git", # plain http would ship the PAT in cleartext "https://github.com/octo/widget.git%0dhost=evil.example", # credential-protocol injection "ext::sh -c env", # remote helper transports can execute code + "--upload-pack=env", # leading dash would be parsed as a git option ], ) async def test_git_clone_rejects_unsafe_url(proxy_settings: Settings, clone_url: str) -> None: