From ab6254b15870e3d9718188ba768a477cd770983e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:50:54 +0000 Subject: [PATCH 1/7] Initial plan From 9901bd9834c24f89f7d93fc8ccb6208a20f3438f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:55:07 +0000 Subject: [PATCH 2/7] Fix PEP 768 code injection failing with backslash in temp path When attaching using PEP 768 style code injection, the os.remove() call written to the temp file embedded the raw temp file path. On Windows, backslashes in the path (e.g. C:\Users\...\Temp\tmpXXX) were interpreted as Python escape sequences, causing a SyntaxError. Fix by replacing backslashes with forward slashes in the temp file path before embedding it in the Python code string. Windows accepts forward slashes as path separators, so this is safe on all platforms. Also add a regression test that confirms the unfixed code raises SyntaxError and the fixed code compiles correctly. Closes #2037 --- src/debugpy/server/cli.py | 2 +- tests/debugpy/server/test_cli.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/debugpy/server/cli.py b/src/debugpy/server/cli.py index 48bd90946..a13e5737f 100644 --- a/src/debugpy/server/cli.py +++ b/src/debugpy/server/cli.py @@ -466,7 +466,7 @@ def attach_to_pid(): tmp_file.write(python_code.encode()) tmp_file.write( """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=tmp_file_path + tmp_file_path=tmp_file_path.replace("\\", "/") ).encode() ) tmp_file.flush() diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index 45fed7527..65f0390c4 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -248,3 +248,29 @@ def test_script_parent_pid_with_listen_failure(cli): cli(["--listen", "8888", "--parent-session-pid", "1234", "spam.py"]) assert "--parent-session-pid requires --connect" in str(ex.value) + + +def test_pep768_code_injection_path_with_backslashes(): + """Test that PEP 768 code injection generates valid Python when the temp + path contains backslashes (e.g. on Windows: C:\\Users\\xxx\\AppData\\Local\\Temp\\).""" + # Simulate a Windows-style temp file path with backslashes. + windows_tmp_path = r"C:\Users\test\AppData\Local\Temp\tmp0_vuee4s" + + # This is the code template used in cli.py attach_to_pid(). + # Without the fix, this produces a SyntaxError when executed because + # backslashes in the string literal are interpreted as escape sequences. + code_without_fix = """import os;os.remove("{tmp_file_path}");""".format( + tmp_file_path=windows_tmp_path + ) + + # Verify that the unfixed code is indeed invalid Python syntax. + with pytest.raises(SyntaxError): + compile(code_without_fix, "", "exec") + + # The fix replaces backslashes with forward slashes (valid on Windows). + code_with_fix = """import os;os.remove("{tmp_file_path}");""".format( + tmp_file_path=windows_tmp_path.replace("\\", "/") + ) + + # Verify that the fixed code is valid Python syntax. + compile(code_with_fix, "", "exec") From 3746bdab729be3c77fcecd96886aa90359a91ac8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:55:57 +0000 Subject: [PATCH 3/7] Address code review: rename test function and variable for clarity --- tests/debugpy/server/test_cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index 65f0390c4..ee9f189ea 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -250,17 +250,17 @@ def test_script_parent_pid_with_listen_failure(cli): assert "--parent-session-pid requires --connect" in str(ex.value) -def test_pep768_code_injection_path_with_backslashes(): +def test_pep_768_code_injection_path_with_backslashes(): """Test that PEP 768 code injection generates valid Python when the temp path contains backslashes (e.g. on Windows: C:\\Users\\xxx\\AppData\\Local\\Temp\\).""" # Simulate a Windows-style temp file path with backslashes. - windows_tmp_path = r"C:\Users\test\AppData\Local\Temp\tmp0_vuee4s" + mock_windows_tmp_path = r"C:\Users\test\AppData\Local\Temp\tmp0_vuee4s" # This is the code template used in cli.py attach_to_pid(). # Without the fix, this produces a SyntaxError when executed because # backslashes in the string literal are interpreted as escape sequences. code_without_fix = """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=windows_tmp_path + tmp_file_path=mock_windows_tmp_path ) # Verify that the unfixed code is indeed invalid Python syntax. @@ -269,7 +269,7 @@ def test_pep768_code_injection_path_with_backslashes(): # The fix replaces backslashes with forward slashes (valid on Windows). code_with_fix = """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=windows_tmp_path.replace("\\", "/") + tmp_file_path=mock_windows_tmp_path.replace("\\", "/") ) # Verify that the fixed code is valid Python syntax. From a3b90e56a0e97fe1a5de9c32bde21462d0883d27 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:14:16 +0000 Subject: [PATCH 4/7] Add test verifying sys.remote_exec is called with valid injected code on Windows paths Add test_pep_768_remote_exec_called_with_backslash_path which mocks sys.remote_exec, tempfile.NamedTemporaryFile (using a Windows-style backslash path), and calls attach_to_pid() directly. It asserts that: - sys.remote_exec is called with the correct path - The bytes written to the temp file compile as valid Python - The os.remove() call uses forward slashes (no raw backslash escapes) --- tests/debugpy/server/test_cli.py | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index ee9f189ea..61cc9fc71 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -274,3 +274,67 @@ def test_pep_768_code_injection_path_with_backslashes(): # Verify that the fixed code is valid Python syntax. compile(code_with_fix, "", "exec") + + +def test_pep_768_remote_exec_called_with_backslash_path(): + """Test that attach_to_pid() calls sys.remote_exec and writes valid Python + to the temp file even when the temp path contains backslashes (Windows).""" + import contextlib + from debugpy.server import cli + + mock_windows_tmp_path = r"C:\Users\test\AppData\Local\Temp\tmp0_vuee4s" + pid = os.getpid() + + # A fake file object that captures writes via a side_effect list. + # Using MagicMock avoids the BytesIO.close() issue where getvalue() + # raises ValueError on a closed buffer. + written_chunks = [] + fake_file = mock.MagicMock() + fake_file.name = mock_windows_tmp_path + fake_file.write.side_effect = written_chunks.append + + fake_file_cm = mock.MagicMock() + fake_file_cm.__enter__ = mock.Mock(return_value=fake_file) + fake_file_cm.__exit__ = mock.Mock(return_value=False) + + # Configure cli.options with the minimum fields attach_to_pid() needs. + original_options = { + attr: getattr(cli.options, attr) + for attr in ("mode", "address", "wait_for_client", "log_to", + "adapter_access_token", "disable_sys_remote_exec", "target") + } + try: + cli.options.mode = "connect" + cli.options.address = ("127.0.0.1", 5678) + cli.options.wait_for_client = False + cli.options.log_to = None + cli.options.adapter_access_token = None + cli.options.disable_sys_remote_exec = False + cli.options.target = pid + + with contextlib.ExitStack() as stack: + stack.enter_context( + mock.patch("tempfile.NamedTemporaryFile", return_value=fake_file_cm) + ) + mock_remote_exec = stack.enter_context( + mock.patch.object(sys, "remote_exec", create=True) + ) + cli.attach_to_pid() + + # sys.remote_exec must have been called with the original (backslash) path + # because that is the actual path created on disk. + mock_remote_exec.assert_called_once_with(pid, mock_windows_tmp_path) + + # The Python code written to the temp file must be syntactically valid. + injected_code = b"".join(written_chunks).decode() + compile(injected_code, "", "exec") + + # The os.remove() call inside the injected code must use forward slashes + # so that it is a valid Python string literal (no backslash escape issues). + assert r"C:\Users" not in injected_code, ( + "Backslashes must be replaced with forward slashes in the injected os.remove() call" + ) + assert "C:/Users" in injected_code + finally: + for attr, value in original_options.items(): + setattr(cli.options, attr, value) From 843c0d169099b52326700c5ac283442347672db2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:49:06 +0000 Subject: [PATCH 5/7] Remove tautological test; use exact assertion in remote_exec test --- tests/debugpy/server/test_cli.py | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index 61cc9fc71..11cc68e08 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -250,32 +250,6 @@ def test_script_parent_pid_with_listen_failure(cli): assert "--parent-session-pid requires --connect" in str(ex.value) -def test_pep_768_code_injection_path_with_backslashes(): - """Test that PEP 768 code injection generates valid Python when the temp - path contains backslashes (e.g. on Windows: C:\\Users\\xxx\\AppData\\Local\\Temp\\).""" - # Simulate a Windows-style temp file path with backslashes. - mock_windows_tmp_path = r"C:\Users\test\AppData\Local\Temp\tmp0_vuee4s" - - # This is the code template used in cli.py attach_to_pid(). - # Without the fix, this produces a SyntaxError when executed because - # backslashes in the string literal are interpreted as escape sequences. - code_without_fix = """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=mock_windows_tmp_path - ) - - # Verify that the unfixed code is indeed invalid Python syntax. - with pytest.raises(SyntaxError): - compile(code_without_fix, "", "exec") - - # The fix replaces backslashes with forward slashes (valid on Windows). - code_with_fix = """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=mock_windows_tmp_path.replace("\\", "/") - ) - - # Verify that the fixed code is valid Python syntax. - compile(code_with_fix, "", "exec") - - def test_pep_768_remote_exec_called_with_backslash_path(): """Test that attach_to_pid() calls sys.remote_exec and writes valid Python to the temp file even when the temp path contains backslashes (Windows).""" @@ -331,10 +305,7 @@ def test_pep_768_remote_exec_called_with_backslash_path(): # The os.remove() call inside the injected code must use forward slashes # so that it is a valid Python string literal (no backslash escape issues). - assert r"C:\Users" not in injected_code, ( - "Backslashes must be replaced with forward slashes in the injected os.remove() call" - ) - assert "C:/Users" in injected_code + assert 'import os;os.remove("C:/Users/test/AppData/Local/Temp/tmp0_vuee4s");' in injected_code finally: for attr, value in original_options.items(): setattr(cli.options, attr, value) From 4fc7a19208ca92100a81031c16c975a25b28ef81 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:55:39 +0000 Subject: [PATCH 6/7] Fix POSIX regression: use repr() instead of replace for path embedding --- src/debugpy/server/cli.py | 4 +--- tests/debugpy/server/test_cli.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/debugpy/server/cli.py b/src/debugpy/server/cli.py index a13e5737f..e0f3a7c87 100644 --- a/src/debugpy/server/cli.py +++ b/src/debugpy/server/cli.py @@ -465,9 +465,7 @@ def attach_to_pid(): ) tmp_file.write(python_code.encode()) tmp_file.write( - """import os;os.remove("{tmp_file_path}");""".format( - tmp_file_path=tmp_file_path.replace("\\", "/") - ).encode() + "import os;os.remove({});".format(repr(tmp_file_path)).encode() ) tmp_file.flush() tmp_file.close() diff --git a/tests/debugpy/server/test_cli.py b/tests/debugpy/server/test_cli.py index 11cc68e08..da6117d7c 100644 --- a/tests/debugpy/server/test_cli.py +++ b/tests/debugpy/server/test_cli.py @@ -303,9 +303,9 @@ def test_pep_768_remote_exec_called_with_backslash_path(): injected_code = b"".join(written_chunks).decode() compile(injected_code, "", "exec") - # The os.remove() call inside the injected code must use forward slashes - # so that it is a valid Python string literal (no backslash escape issues). - assert 'import os;os.remove("C:/Users/test/AppData/Local/Temp/tmp0_vuee4s");' in injected_code + # The os.remove() call inside the injected code must use the repr of the path + # so that it is a valid Python string literal on all platforms. + assert "import os;os.remove({});".format(repr(mock_windows_tmp_path)) in injected_code finally: for attr, value in original_options.items(): setattr(cli.options, attr, value) From 3e28555330f8ddecd499329f6b10a13b5fdf7be8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Jun 2026 18:00:32 +0000 Subject: [PATCH 7/7] Use {tmp_file_path!r} format per PR #2039 style --- src/debugpy/server/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/debugpy/server/cli.py b/src/debugpy/server/cli.py index e0f3a7c87..aa506e0ce 100644 --- a/src/debugpy/server/cli.py +++ b/src/debugpy/server/cli.py @@ -465,7 +465,9 @@ def attach_to_pid(): ) tmp_file.write(python_code.encode()) tmp_file.write( - "import os;os.remove({});".format(repr(tmp_file_path)).encode() + """import os;os.remove({tmp_file_path!r});""".format( + tmp_file_path=tmp_file_path + ).encode() ) tmp_file.flush() tmp_file.close()