From 98aa8b5298340dd57b1e02d24943410491a261b9 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 22 Nov 2022 15:00:04 -0500 Subject: [PATCH] Restrict `#egg=` fragments to valid PEP 508 names This should help reduce user confusion about what can go in a URI's egg fragment. Fixes #11567. Signed-off-by: William Woodruff --- src/pip/_internal/exceptions.py | 8 ++++++++ src/pip/_internal/models/link.py | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 2ab1f591f12..ac4057733e1 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -658,3 +658,11 @@ def __str__(self) -> str: assert self.error is not None message_part = f".\n{self.error}\n" return f"Configuration file {self.reason}{message_part}" + + +class InvalidEggFragment(InstallationError): + """A link's `#egg=` fragment doesn't look like a valid PEP 508 project + name.""" + + def __init__(self, fragment: str) -> None: + super().__init__(f"egg fragment is not a bare project name: {fragment}") diff --git a/src/pip/_internal/models/link.py b/src/pip/_internal/models/link.py index c792d128bcf..27001b2bbc6 100644 --- a/src/pip/_internal/models/link.py +++ b/src/pip/_internal/models/link.py @@ -18,6 +18,7 @@ Union, ) +from pip._internal.exceptions import InvalidEggFragment from pip._internal.utils.filetypes import WHEEL_EXTENSION from pip._internal.utils.hashes import Hashes from pip._internal.utils.misc import ( @@ -358,12 +359,25 @@ def url_without_fragment(self) -> str: _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") + # Per PEP 508. + _project_name_re = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE + ) + @property def egg_fragment(self) -> Optional[str]: match = self._egg_fragment_re.search(self._url) if not match: return None - return match.group(1) + + # The egg fragment must look like a project name, and only + # a project name. In particular, it can't contain version constraints + # or anything else like that. + project_name = match.group(1) + if not self._project_name_re.match(project_name): + raise InvalidEggFragment(project_name) + + return project_name _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)")