https://github.com/python/cpython/commit/bf6623aaeb3516606f89851b0ba8b5756f18e9cf
commit: bf6623aaeb3516606f89851b0ba8b5756f18e9cf
branch: main
author: Neko Asakura <[email protected]>
committer: vsajip <[email protected]>
date: 2026-11-09T11:46:43Z
summary:

gh-160782: reject symlinked subdirectories in the venv directory (GH-160973)

files:
A Misc/NEWS.d/next/Library/2026-10-30-10-36-30.gh-issue-160782.n6JCUU.rst
M Lib/test/test_venv.py
M Lib/venv/__init__.py

diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 678615ea172aefa..43c2a552ee90b7f 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -663,6 +663,24 @@ def test_unoverwritable_fails(self):
             self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
             self.clear_directory(self.env_dir)
 
+    @unittest.skipUnless(can_symlink(), 'Needs symlinks')
+    def test_symlinked_directory_error(self):
+        """
+        Test that venv creation fails when a directory inside the target
+        directory is a symlink.
+        """
+        outside = tempfile.mkdtemp()
+        self.addCleanup(rmtree, outside)
+        for n in range(1, len(self.lib) + 1):
+            subdirs = self.lib[:n]
+            with self.subTest(subdirs=subdirs):
+                rmtree(self.env_dir)
+                os.makedirs(os.path.join(self.env_dir, *subdirs[:-1]))
+                os.symlink(outside, os.path.join(self.env_dir, *subdirs),
+                           target_is_directory=True)
+                self.assertRaises(ValueError, venv.create, self.env_dir)
+                self.assertEqual(os.listdir(outside), [])
+
     def test_upgrade(self):
         """
         Test upgrading an existing environment directory.
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index 620168be16862ec..60fe070b1f9f4b1 100644
--- a/Lib/venv/__init__.py
+++ b/Lib/venv/__init__.py
@@ -141,6 +141,12 @@ def ensure_directories(self, env_dir):
         """
 
         def create_if_needed(d):
+            path = env_dir
+            for part in os.path.relpath(d, env_dir).split(os.sep)[:-1]:
+                path = os.path.join(path, part)
+                if os.path.islink(path) or os.path.isjunction(path):
+                    raise ValueError(f'Refusing to create a venv in {env_dir} '
+                                     f'because {path} is a link.')
             if not os.path.exists(d):
                 os.makedirs(d)
             elif os.path.islink(d) or os.path.isfile(d):
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-30-10-36-30.gh-issue-160782.n6JCUU.rst 
b/Misc/NEWS.d/next/Library/2026-10-30-10-36-30.gh-issue-160782.n6JCUU.rst
new file mode 100644
index 000000000000000..d4da24bdca0b5e8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-30-10-36-30.gh-issue-160782.n6JCUU.rst
@@ -0,0 +1,2 @@
+:mod:`venv`: a symbolic link or junction in place of a directory inside the
+environment now raises :exc:`ValueError` instead of being followed.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to