new file mode 100644
@@ -0,0 +1,34 @@
+From 7cac095948e86d8a0e0e17de6b763727e9b051ac Mon Sep 17 00:00:00 2001
+From: Damian Shaw <damian.peter.shaw@gmail.com>
+Date: Mon, 18 May 2026 23:22:51 -0400
+Subject: [PATCH] Fix is_within_directory for doubled-slash roots
+
+CVE: CVE-2026-8643
+Upstream-Status: Backport [https://github.com/pypa/pip/commit/7ff8bdd81ec5edca2bebf78ad8506dda710d6af5]
+
+Backport Changes:
+- Omit tests/unit/test_utils_unpacking.py because the pip 24.0 PyPI sdist used
+ by this recipe does not ship the upstream tests directory.
+
+(cherry picked from commit 7ff8bdd81ec5edca2bebf78ad8506dda710d6af5)
+Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
+---
+ src/pip/_internal/utils/unpacking.py | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/src/pip/_internal/utils/unpacking.py b/src/pip/_internal/utils/unpacking.py
+index 0b26525fb..188f27e67 100644
+--- a/src/pip/_internal/utils/unpacking.py
++++ b/src/pip/_internal/utils/unpacking.py
+@@ -81,8 +81,7 @@ def is_within_directory(directory: str, target: str) -> bool:
+ abs_directory = os.path.abspath(directory)
+ abs_target = os.path.abspath(target)
+
+- prefix = os.path.commonpath([abs_directory, abs_target])
+- return prefix == abs_directory
++ return abs_target == abs_directory or abs_target.startswith(abs_directory + os.sep)
+
+
+ def set_extracted_file_to_default_mode_plus_executable(path: str) -> None:
+--
+2.35.6
new file mode 100644
@@ -0,0 +1,69 @@
+From b77d10eee5805aea3055e434e08ad1f105bd330c 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:
+- 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 <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 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
new file mode 100644
@@ -0,0 +1,79 @@
+From fc0f7c683c372d66f2e2d6edc00909cc5f225f67 Mon Sep 17 00:00:00 2001
+From: Damian Shaw <damian.peter.shaw@gmail.com>
+Date: Wed, 20 May 2026 15:20:25 -0400
+Subject: [PATCH] Reject entry point names that escape scripts dir (#14000)
+
+* Reject entry point names that escape scripts dir
+
+* NEWS ENTRY
+
+CVE: CVE-2026-8643
+Upstream-Status: Backport [https://github.com/pypa/pip/commit/8eb178480bd1a2b223f509fc430796b265158dfb]
+
+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 8eb178480bd1a2b223f509fc430796b265158dfb)
+Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
+---
+ news/14000.bugfix.rst | 2 +
+ src/pip/_internal/operations/install/wheel.py | 26 +++++++-
+ 2 files changed, 25 insertions(+), 3 deletions(-)
+ create mode 100644 news/14000.bugfix.rst
+
+diff --git a/news/14000.bugfix.rst b/news/14000.bugfix.rst
+new file mode 100644
+index 000000000..3b86f1b3b
+--- /dev/null
++++ b/news/14000.bugfix.rst
+@@ -0,0 +1,2 @@
++Reject ``console_scripts`` and ``gui_scripts`` entry points whose name would
++install a script outside the scripts directory.
+diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py
+index f67180c9e..8a36a66ae 100644
+--- a/src/pip/_internal/operations/install/wheel.py
++++ b/src/pip/_internal/operations/install/wheel.py
+@@ -409,17 +409,37 @@ class MissingCallableSuffix(InstallationError):
+ )
+
+
+-def _raise_for_invalid_entrypoint(specification: str) -> None:
++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 not None and entry.suffix is None:
++ if entry is None:
++ return
++
++ if entry.suffix is None:
+ raise MissingCallableSuffix(str(entry))
+
++ if not _script_within_dir(entry.name, scripts_dir):
++ raise InstallationError(
++ f"Invalid script entry point name {entry.name!r}: the script "
++ f"would be installed outside the scripts directory ({scripts_dir})."
++ )
++
+
+ class PipScriptMaker(ScriptMaker):
+ def make(
+ self, specification: str, options: Optional[Dict[str, Any]] = None
+ ) -> List[str]:
+- _raise_for_invalid_entrypoint(specification)
++ _raise_for_invalid_entrypoint(specification, self.target_dir)
+ return super().make(specification, options)
+
+
+--
+2.35.6
@@ -33,6 +33,9 @@ inherit pypi python_setuptools_build_meta
SRC_URI += "file://no_shebang_mangling.patch \
file://CVE-2026-1703.patch \
+ file://CVE-2026-8643.patch \
+ file://CVE-2026-8643-regression_p1.patch \
+ file://CVE-2026-8643-regression_p2.patch \
"
SRC_URI[sha256sum] = "ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2"