Skip to content

Commit

Permalink
Merge pull request from GHSA-qc9x-gjcv-465w
Browse files Browse the repository at this point in the history
fix TLS validation for requirements.txt
  • Loading branch information
frostming authored Jan 8, 2022
2 parents 9cb42e1 + 1679098 commit 439782a
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
13 changes: 9 additions & 4 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from pipenv.patched import crayons
from pipenv.utils import (
cmd_list_to_shell, convert_deps_to_pip, create_spinner, download_file,
find_python, get_canonical_names, get_source_list, is_pinned,
find_python, get_canonical_names, get_host_and_port, get_source_list, is_pinned,
is_python_command, is_required_version, is_star, is_valid_url,
parse_indexes, pep423_name, prepare_pip_source_args, proper_case,
python_version, run_command, subprocess_run, venv_resolve_deps
Expand Down Expand Up @@ -169,7 +169,7 @@ def import_requirements(project, r=None, dev=False):
if extra_index:
indexes.append(extra_index)
if trusted_host:
trusted_hosts.append(trusted_host)
trusted_hosts.append(get_host_and_port(trusted_host))
indexes = sorted(set(indexes))
trusted_hosts = sorted(set(trusted_hosts))
reqs = [install_req_from_parsed_requirement(f) for f in parse_requirements(r, session=pip_requests)]
Expand All @@ -185,8 +185,13 @@ def import_requirements(project, r=None, dev=False):
else:
project.add_package_to_pipfile(str(package.req), dev=dev)
for index in indexes:
trusted = index in trusted_hosts
project.add_index_to_pipfile(index, verify_ssl=trusted)
# don't require HTTPS for trusted hosts (see: https://pip.pypa.io/en/stable/cli/pip/#cmdoption-trusted-host)
host_and_port = get_host_and_port(index)
require_valid_https = not any((v in trusted_hosts for v in (
host_and_port,
host_and_port.partition(':')[0], # also check if hostname without port is in trusted_hosts
)))
project.add_index_to_pipfile(index, verify_ssl=require_valid_https)
project.recase_pipfile()


Expand Down
24 changes: 24 additions & 0 deletions pipenv/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,30 @@ def get_url_name(url):
return urllib3_util.parse_url(url).host


def get_host_and_port(url):
"""Get the host, or the host:port pair if port is explicitly included, for the given URL.
Examples:
>>> get_host_and_port('example.com')
'example.com'
>>> get_host_and_port('example.com:443')
'example.com:443'
>>> get_host_and_port('http://example.com')
'example.com'
>>> get_host_and_port('https://example.com/')
'example.com'
>>> get_host_and_port('https://example.com:8081')
'example.com:8081'
>>> get_host_and_port('ssh://example.com')
'example.com'
:param url: the URL string to parse
:return: a string with the host:port pair if the URL includes port number explicitly; otherwise, returns host only
"""
url = urllib3_util.parse_url(url)
return '{}:{}'.format(url.host, url.port) if url.port else url.host


def get_canonical_names(packages):
"""Canonicalize a list of packages and return a set of canonical names"""
from .vendor.packaging.utils import canonicalize_name
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def test_convert_deps_to_pip_unicode():
("--extra-index-url=https://example.com/simple/", (None, "https://example.com/simple/", None, [])),
("--trusted-host=example.com", (None, None, "example.com", [])),
("# -i https://example.com/simple/", (None, None, None, [])),
("requests", (None, None, None, ["requests"]))
("requests # -i https://example.com/simple/", (None, None, None, ["requests"])),
])
@pytest.mark.utils
def test_parse_indexes(line, result):
Expand Down

0 comments on commit 439782a

Please # to comment.