From fa7854f6b37113a2c4698cdde902e1fcc9bebdd5 Mon Sep 17 00:00:00 2001
From: Damian <damian.peter.shaw@gmail.com>
Date: Sun, 24 May 2026 14:54:47 -0400
Subject: [PATCH] Use is_within_directory for entry point check

CVE: CVE-2026-8643
Upstream-Status: Backport [https://github.com/pypa/pip/commit/fa7854f6b37113a2c4698cdde902e1fcc9bebdd5]

Backport Changes:
- Omitted tests/unit/test_wheel.py because the pip 26.0.1 PyPI sdist
  used by this recipe does not ship the upstream tests directory.

(cherry picked from commit fa7854f6b37113a2c4698cdde902e1fcc9bebdd5)
Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
---
 src/pip/_internal/operations/install/wheel.py | 18 ++++++------------
 src/pip/_internal/utils/unpacking.py          |  1 +
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py
index 231e400658..6f9a983364 100644
--- a/src/pip/_internal/operations/install/wheel.py
+++ b/src/pip/_internal/operations/install/wheel.py
@@ -397,17 +397,6 @@ class MissingCallableSuffix(InstallationError):
         )
 
 
-def _script_within_dir(name: str, scripts_dir: str) -> bool:
-    """Return whether script ``name`` resolves to a path inside the ``scripts_dir``.
-
-    distlib joins the entry point name onto the scripts directory, so a name
-    with path separators or ``..`` components can resolve elsewhere.
-    """
-    root = os.path.normpath(scripts_dir)
-    dest = os.path.normpath(os.path.join(scripts_dir, name))
-    return dest.startswith(root + os.sep)
-
-
 def _raise_for_invalid_entrypoint(specification: str, scripts_dir: str) -> None:
     entry = get_export_entry(specification)
     if entry is None:
@@ -416,7 +405,12 @@ def _raise_for_invalid_entrypoint(specification: str, scripts_dir: str) -> None:
     if entry.suffix is None:
         raise MissingCallableSuffix(str(entry))
 
-    if not _script_within_dir(entry.name, scripts_dir):
+    # distlib joins the entry point name onto the scripts directory, so a name
+    # with path separators or ``..`` components can resolve elsewhere. The script
+    # must resolve to a path strictly inside the scripts directory.
+    dest = os.path.join(scripts_dir, entry.name)
+    resolves_to_scripts_dir = os.path.abspath(dest) == os.path.abspath(scripts_dir)
+    if resolves_to_scripts_dir or not is_within_directory(scripts_dir, dest):
         raise InstallationError(
             f"Invalid script entry point name {entry.name!r}: the script "
             f"would be installed outside the scripts directory ({scripts_dir})."
diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py
index ba7cb52579..8a9b2059ca 100644
--- a/src/pip/_internal/utils/unpacking.py
+++ b/src/pip/_internal/utils/unpacking.py
@@ -79,6 +79,7 @@ def has_leading_dir(paths: Iterable[str]) -> bool:
 def is_within_directory(directory: str, target: str) -> bool:
     """
     Return true if the absolute path of target is within the directory
+    (including when target is equal to the directory).
     """
     abs_directory = os.path.abspath(directory)
     abs_target = os.path.abspath(target)
-- 
2.35.6
