From 4e5bd4ebb18e46c351ecfe8f6f57a3a677edfce2 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Wed, 13 May 2026 18:35:45 +0200 Subject: [PATCH 1/2] fix: _destinsrc to use realpath instead of abspath --- Lib/shutil.py | 4 ++-- Lib/test/test_shutil.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/shutil.py b/Lib/shutil.py index 45cbe4c855b462..d3ac5dc5c50a73 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -940,8 +940,8 @@ def move(src, dst, copy_function=copy2): return real_dst def _destinsrc(src, dst): - src = os.path.abspath(src) - dst = os.path.abspath(dst) + src = os.path.realpath(src) + dst = os.path.realpath(dst) if not src.endswith(os.path.sep): src += os.path.sep if not dst.endswith(os.path.sep): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 13a3487382dfcf..c2485e20a19903 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2914,6 +2914,23 @@ def test_destinsrc_false_positive(self): finally: os_helper.rmtree(TESTFN) + @os_helper.skip_unless_symlink + def test_destinsrc_symlink_bypass(self): + tmp = self.mkdtemp() + src = os.path.join(tmp, 'src') + os.makedirs(src) + # tmp/link -> tmp (one level up) + link = os.path.join(tmp, 'link') + os.symlink(tmp, link) + # raw path: tmp/link/src/sub - no src prefix in string space + # real path: tmp/src/sub - physically inside src + dst = os.path.join(link, 'src', 'sub') + self.assertTrue( + shutil._destinsrc(src, dst), + msg='_destinsrc failed to detect dst inside src via symlink ' + '(dst=%s, src=%s)' % (dst, src), + ) + @os_helper.skip_unless_symlink @mock_rename def test_move_file_symlink(self): From 3147bc54e1526ad435b87026f651e579a9b9a096 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Mon, 18 May 2026 17:49:00 +0200 Subject: [PATCH 2/2] misc: add news entry --- .../Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst diff --git a/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst new file mode 100644 index 00000000000000..20cab736552486 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst @@ -0,0 +1,3 @@ +:func:`shutil.move` now resolves symlinks via :func:`os.path.realpath` +when checking whether the destination is inside the source directory, +preventing a symlink-based bypass of that guard.