From 03a167c0b7a30ce819768264db426fd2e8e8ed06 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 10 May 2022 03:14:41 -0400 Subject: [PATCH] import python-pip-9.0.3-22.el8 --- .gitignore | 1 + .python-pip.metadata | 1 + SOURCES/CVE-2018-18074.patch | 90 +++ SOURCES/CVE-2018-20060.patch | 94 +++ SOURCES/CVE-2019-11236.patch | 43 ++ SOURCES/CVE-2019-11324.patch | 26 + SOURCES/CVE-2021-3572.patch | 53 ++ ...given-prefix-from-wheel-RECORD-files.patch | 95 +++ SOURCES/dummy-certifi.patch | 15 + ...ng-when-running-with-root-privileges.patch | 44 ++ ...ctory-traversal-security-issue-tests.patch | 122 ++++ ...p-directory-traversal-security-issue.patch | 79 +++ SOURCES/pip-nowarn-upgrade.patch | 36 + ...existing-dist-only-if-path-conflicts.patch | 97 +++ SOURCES/skip_yanked_releases.patch | 91 +++ SPECS/python-pip.spec | 663 ++++++++++++++++++ 16 files changed, 1550 insertions(+) create mode 100644 .gitignore create mode 100644 .python-pip.metadata create mode 100644 SOURCES/CVE-2018-18074.patch create mode 100644 SOURCES/CVE-2018-20060.patch create mode 100644 SOURCES/CVE-2019-11236.patch create mode 100644 SOURCES/CVE-2019-11324.patch create mode 100644 SOURCES/CVE-2021-3572.patch create mode 100644 SOURCES/allow-stripping-given-prefix-from-wheel-RECORD-files.patch create mode 100644 SOURCES/dummy-certifi.patch create mode 100644 SOURCES/emit-a-warning-when-running-with-root-privileges.patch create mode 100644 SOURCES/pip-directory-traversal-security-issue-tests.patch create mode 100644 SOURCES/pip-directory-traversal-security-issue.patch create mode 100644 SOURCES/pip-nowarn-upgrade.patch create mode 100644 SOURCES/remove-existing-dist-only-if-path-conflicts.patch create mode 100644 SOURCES/skip_yanked_releases.patch create mode 100644 SPECS/python-pip.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a3caa7a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/pip-9.0.3.tar.gz diff --git a/.python-pip.metadata b/.python-pip.metadata new file mode 100644 index 0000000..3bc8f51 --- /dev/null +++ b/.python-pip.metadata @@ -0,0 +1 @@ +1f5f44d433ca599d40f8e46f440479b2c73802d8 SOURCES/pip-9.0.3.tar.gz diff --git a/SOURCES/CVE-2018-18074.patch b/SOURCES/CVE-2018-18074.patch new file mode 100644 index 0000000..21a9e7f --- /dev/null +++ b/SOURCES/CVE-2018-18074.patch @@ -0,0 +1,90 @@ +From ffbfdb53681207b23bcf67dd76368ad6185ade24 Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Thu, 16 Jan 2020 07:06:09 +0100 +Subject: [PATCH] Fix for CVE-2018-18074 + +This patch contains the fix for CVE-2018-18074 and +a subsequent regression fix combined in one. +--- + sessions.py | 36 +++++++++++++++++++++++++++++------- + utils.py | 1 + + 2 files changed, 30 insertions(+), 7 deletions(-) + +diff --git a/sessions.py b/sessions.py +index 6570e73..4038047 100644 +--- a/sessions.py ++++ b/sessions.py +@@ -29,7 +29,7 @@ from .adapters import HTTPAdapter + + from .utils import ( + requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, +- get_auth_from_url, rewind_body ++ get_auth_from_url, rewind_body, DEFAULT_PORTS + ) + + from .status_codes import codes +@@ -116,6 +116,32 @@ class SessionRedirectMixin(object): + return to_native_string(location, 'utf8') + return None + ++ ++ def should_strip_auth(self, old_url, new_url): ++ """Decide whether Authorization header should be removed when redirecting""" ++ old_parsed = urlparse(old_url) ++ new_parsed = urlparse(new_url) ++ if old_parsed.hostname != new_parsed.hostname: ++ return True ++ # Special case: allow http -> https redirect when using the standard ++ # ports. This isn't specified by RFC 7235, but is kept to avoid ++ # breaking backwards compatibility with older versions of requests ++ # that allowed any redirects on the same host. ++ if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) ++ and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): ++ return False ++ ++ # Handle default port usage corresponding to scheme. ++ changed_port = old_parsed.port != new_parsed.port ++ changed_scheme = old_parsed.scheme != new_parsed.scheme ++ default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) ++ if (not changed_scheme and old_parsed.port in default_port ++ and new_parsed.port in default_port): ++ return False ++ ++ # Standard case: root URI must match ++ return changed_port or changed_scheme ++ + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): + """Receives a Response. Returns a generator of Responses or Requests.""" +@@ -232,14 +258,10 @@ class SessionRedirectMixin(object): + headers = prepared_request.headers + url = prepared_request.url + +- if 'Authorization' in headers: ++ if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): + # If we get redirected to a new host, we should strip out any + # authentication headers. +- original_parsed = urlparse(response.request.url) +- redirect_parsed = urlparse(url) +- +- if (original_parsed.hostname != redirect_parsed.hostname): +- del headers['Authorization'] ++ del headers['Authorization'] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None +diff --git a/utils.py b/utils.py +index 5c47de9..5695ab0 100644 +--- a/utils.py ++++ b/utils.py +@@ -38,6 +38,7 @@ NETRC_FILES = ('.netrc', '_netrc') + + DEFAULT_CA_BUNDLE_PATH = certs.where() + ++DEFAULT_PORTS = {'http': 80, 'https': 443} + + if platform.system() == 'Windows': + # provide a proxy_bypass version on Windows without DNS lookups +-- +2.24.1 + diff --git a/SOURCES/CVE-2018-20060.patch b/SOURCES/CVE-2018-20060.patch new file mode 100644 index 0000000..cc3b741 --- /dev/null +++ b/SOURCES/CVE-2018-20060.patch @@ -0,0 +1,94 @@ +From c734f873270cf9ca414832423f7aad98443c379f Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Thu, 9 Jan 2020 11:26:24 +0100 +Subject: [PATCH] CVE-2018-20060 + +--- + poolmanager.py | 11 ++++++++++- + util/retry.py | 12 +++++++++++- + 2 files changed, 21 insertions(+), 2 deletions(-) + +diff --git a/poolmanager.py b/poolmanager.py +index 4ae9174..bfa5115 100644 +--- a/poolmanager.py ++++ b/poolmanager.py +@@ -312,8 +312,9 @@ class PoolManager(RequestMethods): + + kw['assert_same_host'] = False + kw['redirect'] = False ++ + if 'headers' not in kw: +- kw['headers'] = self.headers ++ kw['headers'] = self.headers.copy() + + if self.proxy is not None and u.scheme == "http": + response = conn.urlopen(method, url, **kw) +@@ -335,6 +336,14 @@ class PoolManager(RequestMethods): + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + ++ # Strip headers marked as unsafe to forward to the redirected location. ++ # Check remove_headers_on_redirect to avoid a potential network call within ++ # conn.is_same_host() which may use socket.gethostbyname() in the future. ++ if (retries.remove_headers_on_redirect ++ and not conn.is_same_host(redirect_location)): ++ for header in retries.remove_headers_on_redirect: ++ kw['headers'].pop(header, None) ++ + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: +diff --git a/util/retry.py b/util/retry.py +index c603cb4..0b83963 100644 +--- a/util/retry.py ++++ b/util/retry.py +@@ -126,6 +126,11 @@ class Retry(object): + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + ++ :param iterable remove_headers_on_redirect: ++ Sequence of headers to remove from the request when a response ++ indicating a redirect is returned before firing off the redirected ++ request ++ + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have +@@ -144,6 +149,8 @@ class Retry(object): + DEFAULT_METHOD_WHITELIST = frozenset([ + 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + ++ DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) ++ + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + #: Maximum backoff time. +@@ -152,7 +159,8 @@ class Retry(object): + def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, + backoff_factor=0, raise_on_redirect=True, raise_on_status=True, +- history=None, respect_retry_after_header=True): ++ history=None, respect_retry_after_header=True, ++ remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): + + self.total = total + self.connect = connect +@@ -171,6 +179,7 @@ class Retry(object): + self.raise_on_status = raise_on_status + self.history = history or tuple() + self.respect_retry_after_header = respect_retry_after_header ++ self.remove_headers_on_redirect = remove_headers_on_redirect + + def new(self, **kw): + params = dict( +@@ -182,6 +191,7 @@ class Retry(object): + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, ++ remove_headers_on_redirect=self.remove_headers_on_redirect, + ) + params.update(kw) + return type(self)(**params) +-- +2.24.1 + diff --git a/SOURCES/CVE-2019-11236.patch b/SOURCES/CVE-2019-11236.patch new file mode 100644 index 0000000..f8729d5 --- /dev/null +++ b/SOURCES/CVE-2019-11236.patch @@ -0,0 +1,43 @@ +From b40eb0f43daecc6e2e3ce47b0be49cf570d02adc Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Thu, 9 Jan 2020 11:14:58 +0100 +Subject: [PATCH] CVE-2019-9740 + +--- + util/url.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/util/url.py b/util/url.py +index 6b6f996..2784c85 100644 +--- a/util/url.py ++++ b/util/url.py +@@ -1,5 +1,6 @@ + from __future__ import absolute_import + from collections import namedtuple ++import re + + from ..exceptions import LocationParseError + +@@ -10,6 +11,8 @@ url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] + # urllib3 infers URLs without a scheme (None) to be http. + NORMALIZABLE_SCHEMES = ('http', 'https', None) + ++_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f]') ++from ..packages.six.moves.urllib.parse import quote + + class Url(namedtuple('Url', url_attrs)): + """ +@@ -155,6 +158,10 @@ def parse_url(url): + # Empty + return Url() + ++ # Prevent CVE-2019-9740. ++ # adapted from https://github.com/python/cpython/pull/12755 ++ url = _contains_disallowed_url_pchar_re.sub(lambda match: quote(match.group()), url) ++ + scheme = None + auth = None + host = None +-- +2.24.1 + diff --git a/SOURCES/CVE-2019-11324.patch b/SOURCES/CVE-2019-11324.patch new file mode 100644 index 0000000..b1016ab --- /dev/null +++ b/SOURCES/CVE-2019-11324.patch @@ -0,0 +1,26 @@ +From 54e768a6dbe3cadeb456dea37bbeaf6e1e17e87c Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Thu, 9 Jan 2020 10:47:27 +0100 +Subject: [PATCH] CVE-2019-11324 Certification mishandle when error should be + thrown + +--- + util/ssl_.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/util/ssl_.py b/util/ssl_.py +index 32fd9ed..f9f12ff 100644 +--- a/util/ssl_.py ++++ b/util/ssl_.py +@@ -319,7 +319,7 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + if e.errno == errno.ENOENT: + raise SSLError(e) + raise +- elif getattr(context, 'load_default_certs', None) is not None: ++ elif ssl_context is None and hasattr(context, 'load_default_certs'): + # try to load OS default certs; works well on Windows (require Python3.4+) + context.load_default_certs() + +-- +2.24.1 + diff --git a/SOURCES/CVE-2021-3572.patch b/SOURCES/CVE-2021-3572.patch new file mode 100644 index 0000000..b1d0a62 --- /dev/null +++ b/SOURCES/CVE-2021-3572.patch @@ -0,0 +1,53 @@ +Backport of https://github.com/pypa/pip/pull/9827 with parts of +https://github.com/pypa/pip/pull/4690 to make it work with pip v9.0.1 +diff --git a/pip/vcs/git.py b/pip/vcs/git.py +index 2187dd8..d1502f8 100644 +--- a/pip/vcs/git.py ++++ b/pip/vcs/git.py +@@ -81,7 +81,7 @@ class Git(VersionControl): + and branches may need origin/ as a prefix. + Returns the SHA1 of the branch or tag if found. + """ +- revisions = self.get_short_refs(dest) ++ revisions = self.get_short_refs(dest, rev) + + origin_rev = 'origin/%s' % rev + if origin_rev in revisions: +@@ -171,12 +171,20 @@ class Git(VersionControl): + ['rev-parse', 'HEAD'], show_stdout=False, cwd=location) + return current_rev.strip() + +- def get_full_refs(self, location): ++ def get_full_refs(self, location, pattern=''): + """Yields tuples of (commit, ref) for branches and tags""" +- output = self.run_command(['show-ref'], ++ output = self.run_command(['show-ref', pattern], + show_stdout=False, cwd=location) +- for line in output.strip().splitlines(): +- commit, ref = line.split(' ', 1) ++ for line in output.split("\n"): ++ line = line.rstrip("\r") ++ if not line: ++ continue ++ try: ++ commit, ref = line.split(' ', 1) ++ except ValueError: ++ # Include the offending line to simplify troubleshooting if ++ # this error ever occurs. ++ raise ValueError(f'unexpected show-ref line: {line!r}') + yield commit.strip(), ref.strip() + + def is_ref_remote(self, ref): +@@ -200,10 +208,10 @@ class Git(VersionControl): + def get_refs(self, location): + return self.get_short_refs(location) + +- def get_short_refs(self, location): ++ def get_short_refs(self, location, pattern=''): + """Return map of named refs (branches or tags) to commit hashes.""" + rv = {} +- for commit, ref in self.get_full_refs(location): ++ for commit, ref in self.get_full_refs(location, pattern): + ref_name = None + if self.is_ref_remote(ref): + ref_name = ref[len('refs/remotes/'):] diff --git a/SOURCES/allow-stripping-given-prefix-from-wheel-RECORD-files.patch b/SOURCES/allow-stripping-given-prefix-from-wheel-RECORD-files.patch new file mode 100644 index 0000000..d66b250 --- /dev/null +++ b/SOURCES/allow-stripping-given-prefix-from-wheel-RECORD-files.patch @@ -0,0 +1,95 @@ +diff -up pip-9.0.1/pip/commands/install.py.orig pip-9.0.1/pip/commands/install.py +--- pip-9.0.1/pip/commands/install.py.orig 2016-11-06 11:49:45.000000000 -0700 ++++ pip-9.0.1/pip/commands/install.py 2016-11-16 16:20:48.638906543 -0700 +@@ -151,6 +151,14 @@ class InstallCommand(RequirementCommand) + "directory.") + + cmd_opts.add_option( ++ '--strip-file-prefix', ++ dest='strip_file_prefix', ++ metavar='prefix', ++ default=None, ++ help="Strip given prefix from script paths in wheel RECORD." ++ ) ++ ++ cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', +@@ -340,6 +348,7 @@ class InstallCommand(RequirementCommand) + global_options, + root=options.root_path, + prefix=options.prefix_path, ++ strip_file_prefix=options.strip_file_prefix, + ) + + possible_lib_locations = get_lib_location_guesses( +diff -up pip-9.0.1/pip/req/req_install.py.orig pip-9.0.1/pip/req/req_install.py +--- pip-9.0.1/pip/req/req_install.py.orig 2016-11-06 11:49:45.000000000 -0700 ++++ pip-9.0.1/pip/req/req_install.py 2016-11-16 16:19:24.848336960 -0700 +@@ -838,8 +838,7 @@ class InstallRequirement(object): + else: + return True + +- def install(self, install_options, global_options=[], root=None, +- prefix=None): ++ def install(self, install_options, global_options=[], root=None, prefix=None, strip_file_prefix=None): + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix) +@@ -848,7 +847,12 @@ class InstallRequirement(object): + version = pip.wheel.wheel_version(self.source_dir) + pip.wheel.check_compatibility(version, self.name) + +- self.move_wheel_files(self.source_dir, root=root, prefix=prefix) ++ self.move_wheel_files( ++ self.source_dir, ++ root=root, ++ prefix=prefix, ++ strip_file_prefix=strip_file_prefix ++ ) + self.install_succeeded = True + return + +@@ -1053,7 +1057,7 @@ class InstallRequirement(object): + def is_wheel(self): + return self.link and self.link.is_wheel + +- def move_wheel_files(self, wheeldir, root=None, prefix=None): ++ def move_wheel_files(self, wheeldir, root=None, prefix=None, strip_file_prefix=None): + move_wheel_files( + self.name, self.req, wheeldir, + user=self.use_user_site, +@@ -1062,6 +1066,7 @@ class InstallRequirement(object): + prefix=prefix, + pycompile=self.pycompile, + isolated=self.isolated, ++ strip_file_prefix=strip_file_prefix, + ) + + def get_dist(self): +diff -up pip-9.0.1/pip/wheel.py.orig pip-9.0.1/pip/wheel.py +--- pip-9.0.1/pip/wheel.py.orig 2016-11-06 11:49:45.000000000 -0700 ++++ pip-9.0.1/pip/wheel.py 2016-11-16 16:19:24.848336960 -0700 +@@ -238,7 +238,7 @@ def get_entrypoints(filename): + + + def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, +- pycompile=True, scheme=None, isolated=False, prefix=None): ++ pycompile=True, scheme=None, isolated=False, prefix=None, strip_file_prefix=None): + """Install a wheel""" + + if not scheme: +@@ -521,7 +521,11 @@ if __name__ == '__main__': + writer.writerow(row) + for f in generated: + h, l = rehash(f) +- writer.writerow((normpath(f, lib_dir), h, l)) ++ final_path = normpath(f, lib_dir) ++ if strip_file_prefix and final_path.startswith(strip_file_prefix): ++ final_path = os.path.join(os.sep, ++ os.path.relpath(final_path, strip_file_prefix)) ++ writer.writerow((final_path, h, l)) + for f in installed: + writer.writerow((installed[f], '', '')) + shutil.move(temp_record, record) diff --git a/SOURCES/dummy-certifi.patch b/SOURCES/dummy-certifi.patch new file mode 100644 index 0000000..f6a9f35 --- /dev/null +++ b/SOURCES/dummy-certifi.patch @@ -0,0 +1,15 @@ +diff --git a/pip/_vendor/certifi/core.py b/pip/_vendor/certifi/core.py +index eab9d1d..9614a88 100644 +--- a/pip/_vendor/certifi/core.py ++++ b/pip/_vendor/certifi/core.py +@@ -19,9 +19,7 @@ class DeprecatedBundleWarning(DeprecationWarning): + + + def where(): +- f = os.path.dirname(__file__) +- +- return os.path.join(f, 'cacert.pem') ++ return '/etc/pki/tls/certs/ca-bundle.crt' + + + def old_where(): diff --git a/SOURCES/emit-a-warning-when-running-with-root-privileges.patch b/SOURCES/emit-a-warning-when-running-with-root-privileges.patch new file mode 100644 index 0000000..644fce9 --- /dev/null +++ b/SOURCES/emit-a-warning-when-running-with-root-privileges.patch @@ -0,0 +1,44 @@ +From 18a617e9e0f64b727938422d4f941dfddfbf5d00 Mon Sep 17 00:00:00 2001 +From: Tomas Orsava +Date: Tue, 14 Feb 2017 17:10:09 +0100 +Subject: [PATCH] Emit a warning when running with root privileges. + +--- + pip/commands/install.py | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/pip/commands/install.py b/pip/commands/install.py +index 227c526..277a3d1 100644 +--- a/pip/commands/install.py ++++ b/pip/commands/install.py +@@ -6,6 +6,8 @@ import os + import tempfile + import shutil + import warnings ++import sys ++from os import path + try: + import wheel + except ImportError: +@@ -193,6 +195,18 @@ class InstallCommand(RequirementCommand): + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + ++ def is_venv(): ++ return hasattr(sys, 'real_prefix') or \ ++ (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix) ++ ++ # Check whether we have root privileges and aren't in venv/virtualenv ++ if os.getuid() == 0 and not is_venv(): ++ logger.warning( ++ "WARNING: Running pip install with root privileges is " ++ "generally not a good idea. Try `%s install --user` instead." ++ % path.basename(sys.argv[0]) ++ ) ++ + if options.as_egg: + warnings.warn( + "--egg has been deprecated and will be removed in the future. " +-- +2.11.0 + diff --git a/SOURCES/pip-directory-traversal-security-issue-tests.patch b/SOURCES/pip-directory-traversal-security-issue-tests.patch new file mode 100644 index 0000000..3f63467 --- /dev/null +++ b/SOURCES/pip-directory-traversal-security-issue-tests.patch @@ -0,0 +1,122 @@ +From 7917dbda14ef64a5e7fdea48383a266577484ac8 Mon Sep 17 00:00:00 2001 +From: Tomas Orsava +Date: Wed, 19 Aug 2020 12:51:16 +0200 +Subject: [PATCH 2/2] FIX #6413 pip install allow directory traversal + (tests) + +--- + tests/unit/test_download.py | 85 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 85 insertions(+) + +diff --git a/tests/unit/test_download.py b/tests/unit/test_download.py +index ee4b11c..15f99ec 100644 +--- a/tests/unit/test_download.py ++++ b/tests/unit/test_download.py +@@ -1,5 +1,6 @@ + import hashlib + import os ++import sys + from io import BytesIO + from shutil import rmtree, copy + from tempfile import mkdtemp +@@ -13,6 +14,7 @@ import pip + from pip.exceptions import HashMismatch + from pip.download import ( + PipSession, SafeFileCache, path_to_url, unpack_http_url, url_to_path, ++ _download_http_url, parse_content_disposition, sanitize_content_filename, + unpack_file_url, + ) + from pip.index import Link +@@ -123,6 +125,89 @@ def test_unpack_http_url_bad_downloaded_checksum(mock_unpack_file): + rmtree(download_dir) + + ++@pytest.mark.parametrize("filename, expected", [ ++ ('dir/file', 'file'), ++ ('../file', 'file'), ++ ('../../file', 'file'), ++ ('../', ''), ++ ('../..', '..'), ++ ('/', ''), ++]) ++def test_sanitize_content_filename(filename, expected): ++ """ ++ Test inputs where the result is the same for Windows and non-Windows. ++ """ ++ assert sanitize_content_filename(filename) == expected ++ ++ ++@pytest.mark.parametrize("filename, win_expected, non_win_expected", [ ++ ('dir\\file', 'file', 'dir\\file'), ++ ('..\\file', 'file', '..\\file'), ++ ('..\\..\\file', 'file', '..\\..\\file'), ++ ('..\\', '', '..\\'), ++ ('..\\..', '..', '..\\..'), ++ ('\\', '', '\\'), ++]) ++def test_sanitize_content_filename__platform_dependent( ++ filename, ++ win_expected, ++ non_win_expected ++): ++ """ ++ Test inputs where the result is different for Windows and non-Windows. ++ """ ++ if sys.platform == 'win32': ++ expected = win_expected ++ else: ++ expected = non_win_expected ++ assert sanitize_content_filename(filename) == expected ++ ++ ++@pytest.mark.parametrize("content_disposition, default_filename, expected", [ ++ ('attachment;filename="../file"', 'df', 'file'), ++]) ++def test_parse_content_disposition( ++ content_disposition, ++ default_filename, ++ expected ++): ++ actual = parse_content_disposition(content_disposition, default_filename) ++ assert actual == expected ++ ++ ++def test_download_http_url__no_directory_traversal(tmpdir): ++ """ ++ Test that directory traversal doesn't happen on download when the ++ Content-Disposition header contains a filename with a ".." path part. ++ """ ++ mock_url = 'http://www.example.com/whatever.tgz' ++ contents = b'downloaded' ++ link = Link(mock_url) ++ ++ session = Mock() ++ resp = MockResponse(contents) ++ resp.url = mock_url ++ resp.headers = { ++ # Set the content-type to a random value to prevent ++ # mimetypes.guess_extension from guessing the extension. ++ 'content-type': 'random', ++ 'content-disposition': 'attachment;filename="../out_dir_file"' ++ } ++ session.get.return_value = resp ++ ++ download_dir = tmpdir.join('download') ++ os.mkdir(download_dir) ++ file_path, content_type = _download_http_url( ++ link, ++ session, ++ download_dir, ++ hashes=None, ++ ) ++ # The file should be downloaded to download_dir. ++ actual = os.listdir(download_dir) ++ assert actual == ['out_dir_file'] ++ ++ + @pytest.mark.skipif("sys.platform == 'win32'") + def test_path_to_url_unix(): + assert path_to_url('/tmp/file') == 'file:///tmp/file' +-- +2.25.4 + diff --git a/SOURCES/pip-directory-traversal-security-issue.patch b/SOURCES/pip-directory-traversal-security-issue.patch new file mode 100644 index 0000000..c6ef12d --- /dev/null +++ b/SOURCES/pip-directory-traversal-security-issue.patch @@ -0,0 +1,79 @@ +From 8044d9f2fbcb09f09a62b26ac1d8a134976bb2ac Mon Sep 17 00:00:00 2001 +From: gzpan123 +Date: Wed, 17 Apr 2019 21:25:45 +0800 +Subject: [PATCH 1/2] FIX #6413 pip install allow directory traversal + +--- + news/6413.bugfix | 3 +++ + pip/download.py | 31 ++++++++++++++++++++++++++----- + 2 files changed, 29 insertions(+), 5 deletions(-) + create mode 100644 news/6413.bugfix + +diff --git a/news/6413.bugfix b/news/6413.bugfix +new file mode 100644 +index 0000000..68d0a72 +--- /dev/null ++++ b/news/6413.bugfix +@@ -0,0 +1,3 @@ ++Prevent ``pip install `` from permitting directory traversal if e.g. ++a malicious server sends a ``Content-Disposition`` header with a filename ++containing ``../`` or ``..\\``. +diff --git a/pip/download.py b/pip/download.py +index 039e55a..b3d169b 100644 +--- a/pip/download.py ++++ b/pip/download.py +@@ -54,7 +54,8 @@ __all__ = ['get_file_content', + 'is_url', 'url_to_path', 'path_to_url', + 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', +- 'unpack_http_url', 'unpack_url'] ++ 'unpack_http_url', 'unpack_url', ++ 'parse_content_disposition', 'sanitize_content_filename'] + + + logger = logging.getLogger(__name__) +@@ -824,6 +825,29 @@ def unpack_url(link, location, download_dir=None, + write_delete_marker_file(location) + + ++def sanitize_content_filename(filename): ++ # type: (str) -> str ++ """ ++ Sanitize the "filename" value from a Content-Disposition header. ++ """ ++ return os.path.basename(filename) ++ ++ ++def parse_content_disposition(content_disposition, default_filename): ++ # type: (str, str) -> str ++ """ ++ Parse the "filename" value from a Content-Disposition header, and ++ return the default filename if the result is empty. ++ """ ++ _type, params = cgi.parse_header(content_disposition) ++ filename = params.get('filename') ++ if filename: ++ # We need to sanitize the filename to prevent directory traversal ++ # in case the filename contains ".." path parts. ++ filename = sanitize_content_filename(filename) ++ return filename or default_filename ++ ++ + def _download_http_url(link, session, temp_dir, hashes): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] +@@ -864,10 +888,7 @@ def _download_http_url(link, session, temp_dir, hashes): + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: +- type, params = cgi.parse_header(content_disposition) +- # We use ``or`` here because we don't want to use an "empty" value +- # from the filename param. +- filename = params.get('filename') or filename ++ filename = parse_content_disposition(content_disposition, filename) + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) +-- +2.25.4 + diff --git a/SOURCES/pip-nowarn-upgrade.patch b/SOURCES/pip-nowarn-upgrade.patch new file mode 100644 index 0000000..9e314a5 --- /dev/null +++ b/SOURCES/pip-nowarn-upgrade.patch @@ -0,0 +1,36 @@ +diff --git a/pip/utils/outdated.py b/pip/utils/outdated.py +index 2164cc3..c71539f 100644 +--- a/pip/utils/outdated.py ++++ b/pip/utils/outdated.py +@@ -92,6 +92,21 @@ def load_selfcheck_statefile(): + return GlobalSelfCheckState() + + ++def pip_installed_by_pip(): ++ """Checks whether pip was installed by pip ++ ++ This is used not to display the upgrade message when pip is in fact ++ installed by system package manager, such as dnf on Fedora. ++ """ ++ import pkg_resources ++ try: ++ dist = pkg_resources.get_distribution('pip') ++ return (dist.has_metadata('INSTALLER') and ++ 'pip' in dist.get_metadata_lines('INSTALLER')) ++ except pkg_resources.DistributionNotFound: ++ return False ++ ++ + def pip_version_check(session): + """Check for an update for pip. + +@@ -141,7 +156,8 @@ def pip_version_check(session): + + # Determine if our pypi_version is older + if (pip_version < remote_version and +- pip_version.base_version != remote_version.base_version): ++ pip_version.base_version != remote_version.base_version and ++ pip_installed_by_pip()): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: diff --git a/SOURCES/remove-existing-dist-only-if-path-conflicts.patch b/SOURCES/remove-existing-dist-only-if-path-conflicts.patch new file mode 100644 index 0000000..25ff598 --- /dev/null +++ b/SOURCES/remove-existing-dist-only-if-path-conflicts.patch @@ -0,0 +1,97 @@ +diff --git a/pip/req/req_install.py b/pip/req/req_install.py +index 6911fba..8524932 100644 +--- a/pip/req/req_install.py ++++ b/pip/req/req_install.py +@@ -34,7 +34,7 @@ from pip.locations import ( + ) + from pip.utils import ( + display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir, +- dist_in_usersite, dist_in_site_packages, egg_link_path, ++ dist_in_usersite, dist_in_site_packages, dist_in_install_path, egg_link_path, + call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir, + get_installed_version, normalize_path, dist_is_local, + ) +@@ -1049,7 +1049,7 @@ class InstallRequirement(object): + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) +- else: ++ elif dist_in_install_path(existing_dist): + self.conflicts_with = existing_dist + return True + +diff --git a/pip/req/req_set.py b/pip/req/req_set.py +index 76aec06..b93304a 100644 +--- a/pip/req/req_set.py ++++ b/pip/req/req_set.py +@@ -18,7 +18,8 @@ from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled, + UnsupportedPythonVersion) + from pip.req.req_install import InstallRequirement + from pip.utils import ( +- display_path, dist_in_usersite, ensure_dir, normalize_path) ++ display_path, dist_in_usersite, dist_in_install_path, ensure_dir, ++ normalize_path) + from pip.utils.hashes import MissingHashes + from pip.utils.logging import indent_log + from pip.utils.packaging import check_dist_requires_python +@@ -437,10 +438,12 @@ class RequirementSet(object): + + if not best_installed: + # don't uninstall conflict if user install and +- # conflict is not user install ++ # conflict is not user install or conflict lives ++ # in a different path (/usr/lib vs /usr/local/lib/) + if not (self.use_user_site and not +- dist_in_usersite(req_to_install.satisfied_by)): +- req_to_install.conflicts_with = \ ++ dist_in_usersite(req_to_install.satisfied_by) or not ++ dist_in_install_path(req_to_install.satisfied_by)): ++ req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + +@@ -644,10 +647,12 @@ class RequirementSet(object): + if req_to_install.satisfied_by: + if self.upgrade or self.ignore_installed: + # don't uninstall conflict if user install and +- # conflict is not user install ++ # conflict is not user install or conflict lives ++ # in a different path (/usr/lib vs /usr/local/lib/) + if not (self.use_user_site and not + dist_in_usersite( +- req_to_install.satisfied_by)): ++ req_to_install.satisfied_by) or not ++ dist_in_install_path(req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None +diff --git a/pip/utils/__init__.py b/pip/utils/__init__.py +index 815bd33..0ed59f7 100644 +--- a/pip/utils/__init__.py ++++ b/pip/utils/__init__.py +@@ -22,7 +22,7 @@ from pip.exceptions import InstallationError + from pip.compat import console_to_str, expanduser, stdlib_pkgs + from pip.locations import ( + site_packages, user_site, running_under_virtualenv, virtualenv_no_global, +- write_delete_marker_file, ++ write_delete_marker_file, distutils_scheme, + ) + from pip._vendor import pkg_resources + from pip._vendor.six.moves import input +@@ -315,6 +315,16 @@ def dist_in_site_packages(dist): + ).startswith(normalize_path(site_packages)) + + ++def dist_in_install_path(dist): ++ """ ++ Return True if given Distribution is installed in ++ path matching distutils_scheme layout. ++ """ ++ norm_path = normalize_path(dist_location(dist)) ++ return norm_path.startswith(normalize_path( ++ distutils_scheme("")['purelib'].split('python')[0])) ++ ++ + def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: diff --git a/SOURCES/skip_yanked_releases.patch b/SOURCES/skip_yanked_releases.patch new file mode 100644 index 0000000..ef3f8e5 --- /dev/null +++ b/SOURCES/skip_yanked_releases.patch @@ -0,0 +1,91 @@ +From b97ef609100fbdd5895dab48cdab578dfeba396c Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Fri, 10 Sep 2021 13:38:40 +0200 +Subject: [PATCH 1/2] Implement handling of yanked_reason from the HTML anchor + +--- + pip/index.py | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/pip/index.py b/pip/index.py +index f653f6e6a..ced52ce5a 100644 +--- a/pip/index.py ++++ b/pip/index.py +@@ -865,7 +865,11 @@ class HTMLPage(object): + ) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None +- yield Link(url, self, requires_python=pyrequire) ++ yanked_reason = anchor.get('data-yanked', default=None) ++ # Empty or valueless attribute are both parsed as empty string ++ if yanked_reason is not None: ++ yanked_reason = unescape(yanked_reason) ++ yield Link(url, self, requires_python=pyrequire, yanked_reason=yanked_reason) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + +@@ -879,7 +883,7 @@ class HTMLPage(object): + + class Link(object): + +- def __init__(self, url, comes_from=None, requires_python=None): ++ def __init__(self, url, comes_from=None, requires_python=None, yanked_reason=None): + """ + Object representing a parsed link from https://pypi.python.org/simple/* + +@@ -900,6 +904,8 @@ class Link(object): + self.url = url + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None ++ self.yanked_reason = yanked_reason ++ self.yanked = yanked_reason is not None + + def __str__(self): + if self.requires_python: +-- +2.31.1 + +From d8dc6ee5d6809736dce43dc1e57d497f9ff91f26 Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Fri, 10 Sep 2021 13:43:22 +0200 +Subject: [PATCH 2/2] Skip all yanked candidates if possible + +--- + pip/index.py | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/pip/index.py b/pip/index.py +index ced52ce5a..823bbaf7d 100644 +--- a/pip/index.py ++++ b/pip/index.py +@@ -489,6 +489,27 @@ class PackageFinder(object): + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) ++ # If we cannot find a non-yanked candidate, ++ # use the best one and print a warning about it. ++ # Otherwise, try to find another best candidate, ignoring ++ # all the yanked releases. ++ if getattr(best_candidate.location, "yanked", False): ++ nonyanked_candidates = [ ++ c for c in applicable_candidates ++ if not getattr(c.location, "yanked", False) ++ ] ++ ++ if set(nonyanked_candidates): ++ best_candidate = max(nonyanked_candidates, ++ key=self._candidate_sort_key) ++ else: ++ warning_message = ( ++ "WARNING: The candidate selected for download or install " ++ "is a yanked version: '{}' candidate (version {} at {})" ++ ).format(best_candidate.project, best_candidate.version, best_candidate.location) ++ if best_candidate.location.yanked_reason: ++ warning_message += "\nReason for being yanked: {}".format(best_candidate.location.yanked_reason) ++ logger.warning(warning_message) + else: + best_candidate = None + +-- +2.31.1 + diff --git a/SPECS/python-pip.spec b/SPECS/python-pip.spec new file mode 100644 index 0000000..9ad42c0 --- /dev/null +++ b/SPECS/python-pip.spec @@ -0,0 +1,663 @@ +%bcond_with bootstrap +%bcond_with tests + +%bcond_without doc + +%global srcname pip +%global python3_wheelname %{srcname}-%{version}-py2.py3-none-any.whl +%if %{without bootstrap} +%global python3_wheeldir %{_datadir}/python3-wheels +%endif + +%global bashcompdir %(b=$(pkg-config --variable=completionsdir bash-completion 2>/dev/null); echo ${b:-%{_sysconfdir}/bash_completion.d}) + +Name: python-%{srcname} +# When updating, update the bundled libraries versions bellow! +Version: 9.0.3 +Release: 22%{?dist} +Summary: A tool for installing and managing Python packages + +Group: Development/Libraries + +# We bundle a lot of libraries with pip, which itself is under MIT license. +# Here is the list of the libraries with corresponding licenses: + +# appdirs: MIT +# CacheControl: ASL 2.0 +# certifi: MPLv2.0 +# chardet: LGPLv2 +# colorama: BSD +# distlib: Python +# distro: ASL 2.0 +# html5lib: MIT +# idna: BSD +# ipaddress: Python +# lockfile: MIT +# packaging: ASL 2.0 or BSD +# progress: ISC +# pyparsing: MIT +# requests: ASL 2.0 +# retrying: ASL 2.0 +# urllib3: MIT +# six: MIT +# urllib3: MIT +# webencodings: BSD + +License: MIT and Python and ASL 2.0 and BSD and ISC and LGPLv2 and MPLv2.0 and (ASL 2.0 or BSD) +URL: http://www.pip-installer.org +Source0: https://files.pythonhosted.org/packages/source/p/%{srcname}/%{srcname}-%{version}.tar.gz + +BuildArch: noarch + +%if %{with tests} +BuildRequires: git +BuildRequires: bzr +%endif + +# to get tests: +# git clone https://github.com/pypa/pip && cd pip +# git checkout 9.0.1 && tar -czvf ../pip-9.0.1-tests.tar.gz tests/ +%if %{with tests} +Source1: pip-%{version}-tests.tar.gz +%endif + +# Patch until the following issue gets implemented upstream: +# https://github.com/pypa/pip/issues/1351 +Patch0: allow-stripping-given-prefix-from-wheel-RECORD-files.patch + +# Downstream only patch +# Emit a warning to the user if pip install is run with root privileges +# Issue upstream: https://github.com/pypa/pip/issues/4288 +Patch1: emit-a-warning-when-running-with-root-privileges.patch + +# Prevent removing of the system packages installed under /usr/lib +# when pip install -U is executed. +# https://bugzilla.redhat.com/show_bug.cgi?id=1626408 +# Author: Michal Cyprian +Patch2: remove-existing-dist-only-if-path-conflicts.patch + +# Do not show the "new version of pip" warning outside of venv +# Upstream issue: https://github.com/pypa/pip/issues/5346 +# Fedora bug: https://bugzilla.redhat.com/show_bug.cgi?id=1573755 +Patch3: pip-nowarn-upgrade.patch + +# Use the system level root certificate instead of the one bundled in certifi +# https://bugzilla.redhat.com/show_bug.cgi?id=1655255 +Patch4: dummy-certifi.patch + +# Patch for CVE in the bundled urllib3 +# CVE-2018-20060 Cross-host redirect does not remove Authorization header allow for credential exposure +# https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2018-20060 +Patch5: CVE-2018-20060.patch + +# Patch for CVE in the bundled urllib3 +# CVE-2019-11236 CRLF injection due to not encoding the '\r\n' sequence leading to possible attack on internal service +# https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-11236 +Patch6: CVE-2019-11236.patch + +# Patch for CVE in the bundled urllib3 +# CVE-2019-11324 Certification mishandle when error should be thrown +# https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-11324 +Patch7: CVE-2019-11324.patch + +# Patch for CVE in the bundled requests +# CVE-2018-18074 Redirect from HTTPS to HTTP does not remove Authorization header +# This patch fixes both the CVE +# https://bugzilla.redhat.com/show_bug.cgi?id=1643829 +# and the subsequent regression +# https://github.com/psf/requests/pull/4851 +Patch8: CVE-2018-18074.patch + +# Patch for pip install allow directory traversal, leading to arbitrary file write +# - Upstream PR: https://github.com/pypa/pip/pull/6418/files +# - Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1868016 +# Patch9 fixes the issue +# Patch10 adds unit tests for the issue +Patch9: pip-directory-traversal-security-issue.patch +Patch10: pip-directory-traversal-security-issue-tests.patch + +# Patch for CVE-2021-3572 - pip incorrectly handled unicode separators in git references +# The patch is adjusted for older pip where it's necessary to also switch +# the way pip gets revisions from git +# Upstream PR: https://github.com/pypa/pip/pull/9827 +# Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1962856 +Patch11: CVE-2021-3572.patch + +# Downstream-only implementation of support of yanked releases +# PEP 592 - Adding "Yank" Support to the Simple API: +# https://www.python.org/dev/peps/pep-0592/ +# Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2000135 +Patch12: skip_yanked_releases.patch + +%global _description \ +pip is a package management system used to install and manage software packages \ +written in Python. Many packages can be found in the Python Package Index \ +(PyPI). pip is a recursive acronym that can stand for either "Pip Installs \ +Packages" or "Pip Installs Python". + +%description %_description + + +%package -n platform-python-%{srcname} +Summary: A tool for installing and managing Python3 packages +Group: Development/Libraries +Conflicts: python%{python3_pkgversion}-pip < 9.0.3-5%{?dist} +Obsoletes: python%{python3_pkgversion}-pip < 9.0.3-6%{?dist} + +BuildRequires: python%{python3_pkgversion}-devel +BuildRequires: python%{python3_pkgversion}-setuptools +BuildRequires: bash-completion +%if %{with tests} +BuildRequires: python%{python3_pkgversion}-mock +BuildRequires: python%{python3_pkgversion}-pytest +BuildRequires: python%{python3_pkgversion}-pretend +BuildRequires: python%{python3_pkgversion}-freezegun +BuildRequires: python%{python3_pkgversion}-pytest-capturelog +BuildRequires: python%{python3_pkgversion}-scripttest +BuildRequires: python%{python3_pkgversion}-virtualenv +%endif +%if %{without bootstrap} +BuildRequires: python%{python3_pkgversion}-pip +BuildRequires: python%{python3_pkgversion}-wheel +%endif +Requires: platform-python-setuptools + +BuildRequires: ca-certificates +Requires: ca-certificates + +# Virtual provides for the packages bundled by pip. +# See the python2 list above for instructions. +Provides: bundled(python3dist(appdirs)) = 1.4.0 +Provides: bundled(python3dist(cachecontrol)) = 0.11.7 +Provides: bundled(python3dist(colorama)) = 0.3.7 +Provides: bundled(python3dist(distlib)) = 0.2.4 +Provides: bundled(python3dist(distro)) = 1.0.1 +Provides: bundled(python3dist(html5lib)) = 1.0b10 +Provides: bundled(python3dist(ipaddress) = 1.0.17 +Provides: bundled(python3dist(lockfile)) = 0.12.2 +Provides: bundled(python3dist(packaging)) = 16.8 +Provides: bundled(python3dist(setuptools)) = 28.8.0 +Provides: bundled(python3dist(progress)) = 1.2 +Provides: bundled(python3dist(pyparsing)) = 2.1.10 +Provides: bundled(python3dist(requests)) = 2.11.1 +Provides: bundled(python3dist(retrying)) = 1.3.3 +Provides: bundled(python3dist(six)) = 1.10.0 +Provides: bundled(python3dist(webencodings)) = 0.5 + +# Bundled within the requests bundle +Provides: bundled(python3dist(chardet)) = 2.3.0 +Provides: bundled(python3dist(urllib3)) = 1.16 + +%description -n platform-python-%{srcname} %_description + + +%package -n python%{python3_pkgversion}-%{srcname} +Summary: A tool for installing and managing Python3 packages +Group: Development/Libraries + +Requires: platform-python-pip = %{version}-%{release} +Requires: python36 +%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}} + +%description -n python%{python3_pkgversion}-%{srcname} %_description + + +%if %{with doc} +%package doc +Summary: A documentation for a tool for installing and managing Python packages + +BuildRequires: python%{python3_pkgversion}-sphinx + +%description doc +A documentation for a tool for installing and managing Python packages + +%endif + +%if %{without bootstrap} +%package -n python3-%{srcname}-wheel +Summary: The pip wheel + +# Virtual provides for the packages bundled by pip. +# You can find the versions in pip/_vendor/vendor.txt file. +Provides: bundled(python3dist(appdirs)) = 1.4.0 +Provides: bundled(python3dist(cachecontrol)) = 0.11.7 +Provides: bundled(python3dist(colorama)) = 0.3.7 +Provides: bundled(python3dist(distlib)) = 0.2.4 +Provides: bundled(python3dist(distro)) = 1.0.1 +Provides: bundled(python3dist(html5lib)) = 1.0b10 +Provides: bundled(python3dist(ipaddress) = 1.0.17 +Provides: bundled(python3dist(lockfile)) = 0.12.2 +Provides: bundled(python3dist(packaging)) = 16.8 +Provides: bundled(python3dist(setuptools)) = 28.8.0 +Provides: bundled(python3dist(progress)) = 1.2 +Provides: bundled(python3dist(pyparsing)) = 2.1.10 +Provides: bundled(python3dist(requests)) = 2.11.1 +Provides: bundled(python3dist(retrying)) = 1.3.3 +Provides: bundled(python3dist(six)) = 1.10.0 +Provides: bundled(python3dist(webencodings)) = 0.5 + +# Bundled within the requests bundle +Provides: bundled(python3dist(chardet)) = 2.3.0 +Provides: bundled(python3dist(urllib3)) = 1.16 + +%description -n python3-%{srcname}-wheel +A Python wheel of pip to use with venv. +%endif + +%prep +%setup -q -n %{srcname}-%{version} + +%if %{with tests} +tar -xf %{SOURCE1} +%endif + +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 + +# Patching of bundled libraries +pushd pip/_vendor/urllib3 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +popd +pushd pip/_vendor/requests +%patch8 -p1 +popd +%patch9 -p1 +%if %{with tests} +%patch10 -p1 +%endif +%patch11 -p1 +%patch12 -p1 + +# this goes together with patch4 +rm pip/_vendor/certifi/*.pem +rm pip/_vendor/requests/*.pem +sed -i '/\.pem$/d' pip.egg-info/SOURCES.txt + +sed -i '1d' pip/__init__.py + +# Remove ordereddict as it is only required for python <= 2.6 +rm pip/_vendor/ordereddict.py + +# Remove windows executable binaries +rm -v pip/_vendor/distlib/*.exe +sed -i '/\.exe/d' setup.py + +%build +%if %{without bootstrap} +%py3_build_wheel +%else +%py3_build +%endif + +%if %{with doc} +pushd docs +make html +make man +rm _build/html/.buildinfo +popd +%endif + + +%install +%if %{without bootstrap} +%py3_install_wheel %{python3_wheelname} +%else +%py3_install +%endif + +rm %{buildroot}%{_bindir}/pip + +%if %{with doc} +install -d %{buildroot}%{_mandir}/man1 +install -pm0644 docs/_build/man/*.1 %{buildroot}%{_mandir}/man1/pip3.1 +%endif # with doc + +mkdir -p %{buildroot}%{bashcompdir} +PYTHONPATH=%{buildroot}%{python3_sitelib} \ + %{buildroot}%{_bindir}/pip3 completion --bash \ + > %{buildroot}%{bashcompdir}/pip3 + +sed -i -e "s/^\\(complete.*\\) pip\$/\\1 pip3 pip-3 pip3.6 pip-3.6/" \ + -e s/_pip_completion/_pip3_completion/ \ + %{buildroot}%{bashcompdir}/pip3 + +# Provide symlinks to executables to comply with Fedora guidelines for Python +mv %{buildroot}%{_bindir}/pip3 %{buildroot}%{_bindir}/pip%{python3_version} +ln -s ./pip%{python3_version} %{buildroot}%{_bindir}/pip-%{python3_version} + +# Change shebang in /usr/bin/pip3.6 to /usr/bin/python3.6 +pathfix.py -i /usr/bin/python%{python3_version} -np %{buildroot}%{_bindir}/pip%{python3_version} + +# Make sure the INSTALLER is not pip, otherwise pip-nowarn-upgrade.patch +# (Patch3) won't work +echo rpm > %{buildroot}%{python3_sitelib}/pip-%{version}.dist-info/INSTALLER + +%if %{without bootstrap} +mkdir -p %{buildroot}%{python3_wheeldir} +install -p dist/%{python3_wheelname} -t %{buildroot}%{python3_wheeldir} +%endif + +%if %{with tests} +%check +py.test-%{python3_version} -m 'not network' +%endif + + +%files -n platform-python-%{srcname} +%license LICENSE.txt +%doc README.rst +%if %{with doc} +%{_mandir}/man1/pip3.* +%endif +%{python3_sitelib}/pip* + +%files -n python%{python3_pkgversion}-%{srcname} +%license LICENSE.txt +%doc README.rst +# The pip3 binary is created using alternatives +# defined in the python36 package +%{_bindir}/pip%{python3_version} +%{_bindir}/pip-%{python3_version} +%dir %{bashcompdir} +%{bashcompdir}/pip* + +%if %{with doc} +%files doc +%license LICENSE.txt +%doc README.rst +%doc docs/_build/html +%endif # with doc + +%if %{without bootstrap} +%files -n python3-%{srcname}-wheel +%license LICENSE.txt +# we own the dir for simplicity +%dir %{python3_wheeldir}/ +%{python3_wheeldir}/%{python3_wheelname} +%endif + +%changelog +* Wed Oct 06 2021 Charalampos Stratakis - 9.0.3-22 +- Remove bundled windows executables +- Resolves: rhbz#2006788 + +* Tue Oct 05 2021 Lumír Balhar - 9.0.3-21 +- Support of yanked releases +Resolves: rhbz#2000135 + +* Mon Jun 07 2021 Lumír Balhar - 9.0.3-20 +- Fix for CVE-2021-3572 - pip incorrectly handled unicode separators in git references +Resolves: rhbz#1962856 + +* Fri Jan 08 2021 Lumír Balhar - 9.0.3-19 +- Fix bash completion files and simplify spec +Resolves: rhbz#1904478 + +* Wed Aug 19 2020 Tomas Orsava - 9.0.3-18 +- Patch for pip install allow directory traversal, leading to arbitrary file write +Resolves: rhbz#1868016 + +* Wed Mar 04 2020 Charalampos Stratakis - 9.0.3-17 +- Remove unused CA bundle from the bundled requests library +Resolves: rhbz#1775200 + +* Mon Jan 13 2020 Lumír Balhar - 9.0.3-16 +- Add four new patches for CVEs in bundled urllib3 and requests +CVE-2018-20060, CVE-2019-11236, CVE-2019-11324, CVE-2018-18074 +Resolves: rhbz#1649153 +Resolves: rhbz#1700824 +Resolves: rhbz#1702473 +Resolves: rhbz#1643829 + +* Thu Jun 06 2019 Charalampos Stratakis - 9.0.3-15 +- Create python-pip-wheel package with the wheel +Resolves: rhbz#1718031 + +* Wed Mar 13 2019 Lumír Balhar - 9.0.3-14 +- Move bash completion files from platform-python- to python3- subpackage +- resolves: rhbz#1664749 + +* Mon Dec 03 2018 Miro Hrončok - 9.0.3-13 +- Use the system level root certificate instead of the one bundled in certifi +- Resolves: rhbz#1655255 + +* Wed Nov 28 2018 Tomas Orsava - 9.0.3-12 +- Do not show the "new version of pip" warning outside of venv +- Resolves: rhbz#1656171 + +* Mon Nov 19 2018 Victor Stinner - 9.0.3-11 +- Prevent removing of the system packages installed under /usr/lib + when pip install -U is executed. Patch by Michal Cyprian. + Resolves: rhbz#1626408. + +* Fri Nov 16 2018 Tomas Orsava - 9.0.3-10 +- Bump the NVR so it's higher than previous builds of python3-pip that have + mistakenly gotten into the python27 module build when we were dealing with an + MBS filtering problem. See BZ#1650568. +- Resolves: rhbz#1638836 + +* Mon Nov 12 2018 Lumír Balhar - 9.0.3-6 +- python3-pip requires python36 and obsoletes previous version + where python3- and platform-python- were in one package +- Resolves: rhbz#1638836 + +* Mon Oct 22 2018 Tomas Orsava - 9.0.3-5 +- Split part of the python3-pip package into platform-python-pip +- python3-pip will only contain binaries in /usr/bin +- Resolves: rhbz#1638836 + +* Mon Aug 06 2018 Petr Viktorin - 9.0.3-4 +- Remove the python2 subpackage +- Remove unversioned executables (only *-3.6 should be provided) + +* Mon Aug 06 2018 Charalampos Stratakis - 9.0.3-3 +- Correct license information + +* Mon Jun 25 2018 Petr Viktorin - 9.0.3-2 +- Don't build the python2 subpackage + https://bugzilla.redhat.com/show_bug.cgi?id=1594335 + +* Thu Mar 29 2018 Charalampos Stratakis - 9.0.3-1 +- Update to 9.0.3 + +* Wed Feb 21 2018 Lumír Balhar - 9.0.1-16 +- Include built HTML documentation (in the new -doc subpackage) and man page + +* Fri Feb 09 2018 Fedora Release Engineering - 9.0.1-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Dec 04 2017 Charalampos Stratakis - 9.0.1-14 +- Reintroduce the ipaddress module in the python3 subpackage. + +* Mon Nov 20 2017 Charalampos Stratakis - 9.0.1-13 +- Add virtual provides for the bundled libraries. (rhbz#1096912) + +* Tue Aug 29 2017 Tomas Orsava - 9.0.1-12 +- Switch macros to bcond's and make Python 2 optional to facilitate building + the Python 2 and Python 3 modules + +* Thu Jul 27 2017 Fedora Release Engineering - 9.0.1-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Tue May 23 2017 Tomas Orsava - 9.0.1-10 +- Modernized package descriptions +Resolves: rhbz#1452568 + +* Tue Mar 21 2017 Tomas Orsava - 9.0.1-9 +- Fix typo in the sudo pip warning + +* Fri Mar 03 2017 Tomas Orsava - 9.0.1-8 +- Patch 1 update: No sudo pip warning in venv or virtualenv + +* Thu Feb 23 2017 Tomas Orsava - 9.0.1-7 +- Patch 1 update: Customize the warning with the proper version of the pip + command + +* Tue Feb 14 2017 Tomas Orsava - 9.0.1-6 +- Added patch 1: Emit a warning when running with root privileges + +* Sat Feb 11 2017 Fedora Release Engineering - 9.0.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Jan 02 2017 Tomas Orsava - 9.0.1-4 +- Provide symlinks to executables to comply with Fedora guidelines for Python +Resolves: rhbz#1406922 + +* Fri Dec 09 2016 Charalampos Stratakis - 9.0.1-3 +- Rebuild for Python 3.6 with wheel + +* Fri Dec 09 2016 Charalampos Stratakis - 9.0.1-2 +- Rebuild for Python 3.6 without wheel + +* Fri Nov 18 2016 Orion Poplawski - 9.0.1-1 +- Update to 9.0.1 + +* Fri Nov 18 2016 Orion Poplawski - 8.1.2-5 +- Enable EPEL Python 3 builds +- Use new python macros +- Cleanup spec + +* Fri Aug 05 2016 Tomas Orsava - 8.1.2-4 +- Updated the test sources + +* Fri Aug 05 2016 Tomas Orsava - 8.1.2-3 +- Moved python-pip into the python2-pip subpackage +- Added the python_provide macro + +* Tue Jul 19 2016 Fedora Release Engineering - 8.1.2-2 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Tue May 17 2016 Tomas Orsava - 8.1.2-1 +- Update to 8.1.2 +- Moved to a new PyPI URL format +- Updated the prefix-stripping patch because of upstream changes in pip/wheel.py + +* Mon Feb 22 2016 Slavek Kabrda - 8.0.2-1 +- Update to 8.0.2 + +* Thu Feb 04 2016 Fedora Release Engineering - 7.1.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Oct 14 2015 Robert Kuska - 7.1.0-3 +- Rebuilt for Python3.5 rebuild +- With wheel set to 1 + +* Tue Oct 13 2015 Robert Kuska - 7.1.0-2 +- Rebuilt for Python3.5 rebuild + +* Wed Jul 01 2015 Slavek Kabrda - 7.1.0-1 +- Update to 7.1.0 + +* Tue Jun 30 2015 Ville Skyttä - 7.0.3-3 +- Install bash completion +- Ship LICENSE.txt as %%license where available + +* Thu Jun 18 2015 Fedora Release Engineering - 7.0.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Jun 04 2015 Matej Stuchlik - 7.0.3-1 +- Update to 7.0.3 + +* Fri Mar 06 2015 Matej Stuchlik - 6.0.8-1 +- Update to 6.0.8 + +* Thu Dec 18 2014 Slavek Kabrda - 1.5.6-5 +- Only enable tests on Fedora. + +* Mon Dec 01 2014 Matej Stuchlik - 1.5.6-4 +- Add tests +- Add patch skipping tests requiring Internet access + +* Tue Nov 18 2014 Matej Stuchlik - 1.5.6-3 +- Added patch for local dos with predictable temp dictionary names + (http://seclists.org/oss-sec/2014/q4/655) + +* Sat Jun 07 2014 Fedora Release Engineering - 1.5.6-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Sun May 25 2014 Matej Stuchlik - 1.5.6-1 +- Update to 1.5.6 + +* Fri Apr 25 2014 Matej Stuchlik - 1.5.4-4 +- Rebuild as wheel for Python 3.4 + +* Thu Apr 24 2014 Matej Stuchlik - 1.5.4-3 +- Disable build_wheel + +* Thu Apr 24 2014 Matej Stuchlik - 1.5.4-2 +- Rebuild as wheel for Python 3.4 + +* Mon Apr 07 2014 Matej Stuchlik - 1.5.4-1 +- Updated to 1.5.4 + +* Mon Oct 14 2013 Tim Flink - 1.4.1-1 +- Removed patch for CVE 2013-2099 as it has been included in the upstream 1.4.1 release +- Updated version to 1.4.1 + +* Sun Aug 04 2013 Fedora Release Engineering - 1.3.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 16 2013 Toshio Kuratomi - 1.3.1-4 +- Fix for CVE 2013-2099 + +* Thu May 23 2013 Tim Flink - 1.3.1-3 +- undo python2 executable rename to python-pip. fixes #958377 +- fix summary to match upstream + +* Mon May 06 2013 Kevin Kofler - 1.3.1-2 +- Fix main package Summary, it's for Python 2, not 3 (#877401) + +* Fri Apr 26 2013 Jon Ciesla - 1.3.1-1 +- Update to 1.3.1, fix for CVE-2013-1888. + +* Thu Feb 14 2013 Fedora Release Engineering - 1.2.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Tue Oct 09 2012 Tim Flink - 1.2.1-2 +- Fixing files for python3-pip + +* Thu Oct 04 2012 Tim Flink - 1.2.1-1 +- Update to upstream 1.2.1 +- Change binary from pip-python to python-pip (RHBZ#855495) +- Add alias from python-pip to pip-python, to be removed at a later date + +* Tue May 15 2012 Tim Flink - 1.1.0-1 +- Update to upstream 1.1.0 + +* Sat Jan 14 2012 Fedora Release Engineering - 1.0.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Sat Oct 22 2011 Tim Flink - 1.0.2-1 +- update to 1.0.2 and added python3 subpackage + +* Wed Jun 22 2011 Tim Flink - 0.8.3-1 +- update to 0.8.3 and project home page + +* Tue Feb 08 2011 Fedora Release Engineering - 0.8.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Mon Dec 20 2010 Luke Macken - 0.8.2-1 +- update to 0.8.2 of pip +* Mon Aug 30 2010 Peter Halliday - 0.8-1 +- update to 0.8 of pip +* Thu Jul 22 2010 David Malcolm - 0.7.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild + +* Wed Jul 7 2010 Peter Halliday - 0.7.2-1 +- update to 0.7.2 of pip +* Sun May 23 2010 Peter Halliday - 0.7.1-1 +- update to 0.7.1 of pip +* Fri Jan 1 2010 Peter Halliday - 0.6.1.4 +- fix dependency issue +* Fri Dec 18 2009 Peter Halliday - 0.6.1-2 +- fix spec file +* Thu Dec 17 2009 Peter Halliday - 0.6.1-1 +- upgrade to 0.6.1 of pip +* Mon Aug 31 2009 Peter Halliday - 0.4-1 +- Initial package +