Skip to content

Commit 489b5a3

Browse files
committed
fix: quote shell-special chars in Windows pip command to prevent CMD redirection
1 parent 336eea1 commit 489b5a3

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

python/rpdk/python/codegen.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,15 @@ def _pip_build(cls, base_path):
400400
LOG.warning("Starting pip build.")
401401
try:
402402
# On windows run pip command through the default shell (CMD)
403+
# Build a quoted string so CMD doesn't misinterpret '<' or '>'
404+
# in version specifiers (e.g. 'setuptools<82') as redirection.
403405
if os.name == "nt":
406+
cmd_str = " ".join(
407+
f'"{a}"' if any(c in a for c in ("<", ">", "&", "|")) else a
408+
for a in command
409+
)
404410
completed_proc = subprocess_run( # nosec
405-
command,
411+
cmd_str,
406412
stdout=PIPE,
407413
stderr=PIPE,
408414
cwd=base_path,

tests/plugin/codegen_test.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,25 @@ def test__pip_build_called_process_error(tmp_path):
540540
assert isinstance(excinfo.value.__cause__, (FileNotFoundError, CalledProcessError))
541541

542542

543+
def test__pip_build_windows_quotes_version_specifiers(tmp_path):
544+
"""On Windows, args with '<' must be quoted so CMD doesn't treat them as redirection."""
545+
command = ["pip", "install", "setuptools<82"]
546+
patch_cmd = patch.object(
547+
PythonLanguagePlugin, "_make_pip_command", return_value=command
548+
)
549+
patch_os = patch("rpdk.python.codegen.os.name", "nt")
550+
patch_run = patch("rpdk.python.codegen.subprocess_run", autospec=True)
551+
552+
with patch_cmd, patch_os, patch_run as mock_run:
553+
PythonLanguagePlugin._pip_build(tmp_path)
554+
555+
call_args = mock_run.call_args
556+
cmd_arg = call_args[0][0]
557+
assert isinstance(cmd_arg, str), "Windows branch must pass a string to subprocess_run"
558+
assert "setuptools<82" in cmd_arg
559+
assert call_args[1]["shell"] is True
560+
561+
543562
def test__build_pip(plugin):
544563
plugin._use_docker = False
545564
plugin._no_docker = True
@@ -592,7 +611,10 @@ def test__build_pip_windows(plugin):
592611
plugin._pip_build(temppath)
593612

594613
mock_subproc.assert_called_once_with(
595-
plugin._make_pip_command(temppath),
614+
" ".join(
615+
f'"{a}"' if any(c in a for c in ("<", ">", "&", "|")) else a
616+
for a in plugin._make_pip_command(temppath)
617+
),
596618
stdout=ANY,
597619
stderr=ANY,
598620
cwd=temppath,

0 commit comments

Comments
 (0)