diff --git a/SOURCES/CVE-2019-20916.patch b/SOURCES/CVE-2019-20916.patch new file mode 100644 index 0000000..8509beb --- /dev/null +++ b/SOURCES/CVE-2019-20916.patch @@ -0,0 +1,55 @@ +diff --git a/pip/download.py b/pip/download.py +index 54d3131..b55d694 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__) +@@ -823,6 +824,28 @@ def unpack_url(link, location, download_dir=None, + if only_download: + 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""" +@@ -864,10 +887,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) diff --git a/SOURCES/dummy-certifi.patch b/SOURCES/dummy-certifi.patch new file mode 100644 index 0000000..e990eb2 --- /dev/null +++ b/SOURCES/dummy-certifi.patch @@ -0,0 +1,20 @@ +diff --git a/pip/_vendor/requests/certs.py b/pip/_vendor/requests/certs.py +index f922b99..2c9d347 100644 +--- a/pip/_vendor/requests/certs.py ++++ b/pip/_vendor/requests/certs.py +@@ -14,12 +14,13 @@ packaged CA bundle. + import os.path + + try: ++ raise ImportError # force fallback + from certifi import where + except ImportError: + def where(): + """Return the preferred certificate bundle.""" +- # vendored bundle inside Requests +- return os.path.join(os.path.dirname(__file__), 'cacert.pem') ++ # system CA certs ++ return '/etc/pki/tls/certs/ca-bundle.crt' + + if __name__ == '__main__': + print(where()) diff --git a/SPECS/python-pip.spec b/SPECS/python-pip.spec index d6dbcb7..365f1aa 100644 --- a/SPECS/python-pip.spec +++ b/SPECS/python-pip.spec @@ -15,15 +15,48 @@ Name: %{?scl_prefix}python-%{srcname} Version: 9.0.1 -Release: 2%{?dist} +Release: 5%{?dist} Summary: A tool for installing and managing Python packages Group: Development/Libraries -License: MIT + +# 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 +# distlib: Python +# distro: ASL 2.0 +# html5lib: MIT +# six: MIT +# colorama: BSD +# CacheControl: ASL 2.0 +# lockfile: MIT +# progress: ISC +# ipaddress: Python +# packaging: ASL 2.0 or BSD +# pyparsing: MIT +# retrying: ASL 2.0 +# requests: ASL 2.0 +# chardet: LGPLv2 +# urllib3: MIT +# certifi: MPLv2.0 +# setuptools: MIT +# webencodings: BSD +# backports.ssl_match_hostname: Python + +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/pip/%{srcname}-%{version}.tar.gz Patch0: allow-stripping-given-prefix-from-wheel-RECORD-files.patch +# Use the system level root certificate instead of the one bundled in requests +# https://bugzilla.redhat.com/show_bug.cgi?id=1826520 +Patch4: dummy-certifi.patch + +# Fix CVE-2019-20916: directory traversal in _download_http_url() function +# Backported from upstream: https://github.com/pypa/pip/pull/6418 +Patch5: CVE-2019-20916.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch @@ -33,8 +66,37 @@ BuildRequires: %{?scl_prefix}python-setuptools BuildRequires: %{?scl_prefix}python-pip BuildRequires: %{?scl_prefix}python-wheel %endif +BuildRequires: ca-certificates +Requires: ca-certificates Requires: %{?scl_prefix}python-setuptools + +# Virtual provides for the packages bundled by pip. +# You can find the versions in pip/_vendor/vendor.txt file. +Provides: bundled(python-appdirs) = 1.4.0 +Provides: bundled(python-cachecontrol) = 0.11.7 +Provides: bundled(python-colorama) = 0.3.7 +Provides: bundled(python-distlib) = 0.2.4 +Provides: bundled(python-distro) = 1.0.1 +Provides: bundled(python-html5lib) = 1.0b10 +Provides: bundled(python-ipaddress) = 1.0.17 +Provides: bundled(python-lockfile) = 0.12.2 +Provides: bundled(python-packaging) = 16.8 +Provides: bundled(python-setuptools) = 28.8.0 +Provides: bundled(python-progress) = 1.2 +Provides: bundled(python-pyparsing) = 2.1.10 +Provides: bundled(python-requests) = 2.11.1 +Provides: bundled(python-retrying) = 1.3.3 +Provides: bundled(python-six) = 1.10.0 +Provides: bundled(python-webencodings) = 0.5 + +# Bundled within the requests bundle +Provides: bundled(python-chardet) = 2.3.0 +Provides: bundled(python-urllib3) = 1.16 + +# Bundled within the urllib3 bundle of the requests bundle +Provides: bundled(python-backports-ssl_match_hostname) = 3.4.0.2 + %description Pip is a replacement for `easy_install `_. It uses mostly the @@ -47,8 +109,19 @@ easy_installable should be pip-installable as well. %setup -q -n %{srcname}-%{version} %patch0 -p1 +%patch4 -p1 +%patch5 -p1 %{__sed} -i '1d' pip/__init__.py + +# this goes together with patch4 +rm pip/_vendor/requests/*.pem +sed -i '/\.pem$/d' pip.egg-info/SOURCES.txt + +# Remove ordereddict as it is only required for python <= 2.6 +rm pip/_vendor/ordereddict.py +rm pip/_vendor/requests/packages/urllib3/packages/ordered_dict.py + %{?scl:EOF} @@ -88,6 +161,19 @@ pip3 install -I dist/%{python3_wheelname} --root %{buildroot} --strip-file-prefi %{python3_sitelib}/pip* %changelog +* Mon Sep 21 2020 Charalampos Stratakis - 9.0.1-5 +- Security fix for CVE-2019-20916 +Resolves: rhbz#1877249 + +* Wed Sep 16 2020 Charalampos Stratakis - 9.0.1-4 +- Use the system level root certificate instead of the one bundled in requests +Resolves: rhbz#1826520 + +* Tue Sep 08 2020 Charalampos Stratakis - 9.0.1-3 +- Add virtual provides for the bundled libraries +- Correct the license information to reflect the bundled libraries +Resolves: rhbz#1774951 + * Wed Jun 14 2017 Charalampos Stratakis - 9.0.1-2 - Rebuild as wheel