From b77d10eee5805aea3055e434e08ad1f105bd330c Mon Sep 17 00:00:00 2001 From: Damian 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: - Omit tests/unit/test_wheel.py because the pip 24.0 PyPI sdist used by this recipe does not ship the upstream tests directory. (cherry picked from commit fa7854f6b37113a2c4698cdde902e1fcc9bebdd5) Signed-off-by: Hetvi Thakar --- 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 8a36a66ae..ce3e8efe6 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -409,17 +409,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: @@ -428,7 +417,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 188f27e67..14b7e846a 100644 --- a/src/pip/_internal/utils/unpacking.py +++ b/src/pip/_internal/utils/unpacking.py @@ -77,6 +77,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