File CVE-2026-22702.patch of Package python-virtualenv.42221

From b338d8cee138c2fd98f788ec295f8f2f719f53c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= <bgabor8@bloomberg.net>
Date: Fri, 9 Jan 2026 09:49:49 -0800
Subject: [PATCH] fix: resolve TOCTOU vulnerabilities in app_data and lock
 directory creation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Use atomic os.makedirs(..., exist_ok=True) operations instead of
check-then-act pattern to prevent symlink-based TOCTOU attacks.

Reported by: tsigouris007

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
---
 docs/changelog/3013.bugfix.rst      |  1 +
 src/virtualenv/app_data/__init__.py | 11 +++++------
 src/virtualenv/util/lock.py         |  7 +++----
 3 files changed, 9 insertions(+), 10 deletions(-)

Index: virtualenv-20.22.0/src/virtualenv/app_data/__init__.py
===================================================================
--- virtualenv-20.22.0.orig/src/virtualenv/app_data/__init__.py
+++ virtualenv-20.22.0/src/virtualenv/app_data/__init__.py
@@ -36,12 +36,11 @@ def make_app_data(folder, **kwargs):
     if is_read_only:
         return ReadOnlyAppData(folder)
 
-    if not os.path.isdir(folder):
-        try:
-            os.makedirs(folder)
-            logging.debug("created app data folder %s", folder)
-        except OSError as exception:
-            logging.info("could not create app data folder %s due to %r", folder, exception)
+    try:
+        os.makedirs(folder, exist_ok=True)
+        logging.debug("created app data folder %s", folder)
+    except OSError as exception:
+        logging.info("could not create app data folder %s due to %r", folder, exception)
 
     if os.access(folder, os.W_OK):
         return AppDataDiskFolder(folder)
Index: virtualenv-20.22.0/src/virtualenv/util/lock.py
===================================================================
--- virtualenv-20.22.0.orig/src/virtualenv/util/lock.py
+++ virtualenv-20.22.0/src/virtualenv/util/lock.py
@@ -5,7 +5,7 @@ from __future__ import annotations
 import logging
 import os
 from abc import ABCMeta, abstractmethod
-from contextlib import contextmanager
+from contextlib import contextmanager, suppress
 from pathlib import Path
 from threading import Lock, RLock
 
@@ -15,11 +15,9 @@ from filelock import FileLock, Timeout
 class _CountedFileLock(FileLock):
     def __init__(self, lock_file):
         parent = os.path.dirname(lock_file)
-        if not os.path.isdir(parent):
-            try:
-                os.makedirs(parent)
-            except OSError:
-                pass
+        with suppress(OSError):
+            os.makedirs(parent, exist_ok=True)
+
         super().__init__(lock_file)
         self.count = 0
         self.thread_safe = RLock()
@@ -113,10 +111,9 @@ class ReentrantFileLock(PathLockBase):
         # multiple processes might be trying to get a first lock... so we cannot check if this directory exist without
         # a lock, but that lock might then become expensive, and it's not clear where that lock should live.
         # Instead here we just ignore if we fail to create the directory.
-        try:
-            os.makedirs(str(self.path))
-        except OSError:
-            pass
+        with suppress(OSError):
+            os.makedirs(str(self.path), exist_ok=True)
+
         try:
             lock.acquire(0.0001)
         except Timeout:
Index: virtualenv-20.22.0/tests/conftest.py
===================================================================
--- virtualenv-20.22.0.orig/tests/conftest.py
+++ virtualenv-20.22.0/tests/conftest.py
@@ -23,8 +23,8 @@ def pytest_addoption(parser):
 def pytest_configure(config):
     """Ensure randomly is called before we re-order"""
     manager = config.pluginmanager
-    # noinspection PyProtectedMember
-    order = manager.hook.pytest_collection_modifyitems._nonwrappers
+    
+    order = manager.hook.pytest_collection_modifyitems.get_hookimpls()
     dest = next((i for i, p in enumerate(order) if p.plugin is manager.getplugin("randomly")), None)
     if dest is not None:
         from_pos = next(i for i, p in enumerate(order) if p.plugin is manager.getplugin(__file__))
openSUSE Build Service is sponsored by