diff --git a/news/7847.feature b/news/7847.feature new file mode 100644 index 00000000000..8f1a69b6fd2 --- /dev/null +++ b/news/7847.feature @@ -0,0 +1 @@ +Change default behaviour to always cache responses from trusted-host source. diff --git a/src/pip/_internal/network/session.py b/src/pip/_internal/network/session.py index f5eb15ef2f6..39a4a546edc 100644 --- a/src/pip/_internal/network/session.py +++ b/src/pip/_internal/network/session.py @@ -217,6 +217,14 @@ def cert_verify(self, conn, url, verify, cert): ) +class InsecureCacheControlAdapter(CacheControlAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureCacheControlAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + class PipSession(requests.Session): timeout = None # type: Optional[int] @@ -263,8 +271,16 @@ def __init__(self, *args, **kwargs): backoff_factor=0.25, ) - # We want to _only_ cache responses on securely fetched origins. We do - # this because we can't validate the response of an insecurely fetched + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching so we'll use it for all http:// URLs. + # If caching is disabled, we will also use it for + # https:// hosts that we've marked as ignoring + # TLS errors for (trusted-hosts). + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + # We want to _only_ cache responses on securely fetched origins or when + # the host is specified as trusted. We do this because + # we can't validate the response of an insecurely/untrusted fetched # origin, and we don't want someone to be able to poison the cache and # require manual eviction from the cache to fix it. if cache: @@ -272,16 +288,13 @@ def __init__(self, *args, **kwargs): cache=SafeFileCache(cache), max_retries=retries, ) + self._trusted_host_adapter = InsecureCacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) else: secure_adapter = HTTPAdapter(max_retries=retries) - - # Our Insecure HTTPAdapter disables HTTPS validation. It does not - # support caching (see above) so we'll use it for all http:// URLs as - # well as any https:// host that we've marked as ignoring TLS errors - # for. - insecure_adapter = InsecureHTTPAdapter(max_retries=retries) - # Save this for later use in add_insecure_host(). - self._insecure_adapter = insecure_adapter + self._trusted_host_adapter = insecure_adapter self.mount("https://", secure_adapter) self.mount("http://", insecure_adapter) @@ -310,12 +323,15 @@ def add_trusted_host(self, host, source=None, suppress_logging=False): if host_port not in self.pip_trusted_origins: self.pip_trusted_origins.append(host_port) - self.mount(build_url_from_netloc(host) + '/', self._insecure_adapter) + self.mount( + build_url_from_netloc(host) + '/', + self._trusted_host_adapter + ) if not host_port[1]: # Mount wildcard ports for the same host. self.mount( build_url_from_netloc(host) + ':', - self._insecure_adapter + self._trusted_host_adapter ) def iter_secure_origins(self): diff --git a/tests/unit/test_network_session.py b/tests/unit/test_network_session.py index 159a4d4dea1..a0d1463b2cf 100644 --- a/tests/unit/test_network_session.py +++ b/tests/unit/test_network_session.py @@ -72,7 +72,7 @@ def test_http_cache_is_not_enabled(self, tmpdir): assert not hasattr(session.adapters["http://"], "cache") - def test_insecure_host_adapter(self, tmpdir): + def test_trusted_hosts_adapter(self, tmpdir): session = PipSession( cache=tmpdir.joinpath("test-cache"), trusted_hosts=["example.com"], @@ -81,14 +81,14 @@ def test_insecure_host_adapter(self, tmpdir): assert "https://example.com/" in session.adapters # Check that the "port wildcard" is present. assert "https://example.com:" in session.adapters - # Check that the cache isn't enabled. - assert not hasattr(session.adapters["https://example.com/"], "cache") + # Check that the cache is enabled. + assert hasattr(session.adapters["https://example.com/"], "cache") def test_add_trusted_host(self): # Leave a gap to test how the ordering is affected. trusted_hosts = ['host1', 'host3'] session = PipSession(trusted_hosts=trusted_hosts) - insecure_adapter = session._insecure_adapter + trusted_host_adapter = session._trusted_host_adapter prefix2 = 'https://host2/' prefix3 = 'https://host3/' prefix3_wildcard = 'https://host3:' @@ -97,8 +97,8 @@ def test_add_trusted_host(self): assert session.pip_trusted_origins == [ ('host1', None), ('host3', None) ] - assert session.adapters[prefix3] is insecure_adapter - assert session.adapters[prefix3_wildcard] is insecure_adapter + assert session.adapters[prefix3] is trusted_host_adapter + assert session.adapters[prefix3_wildcard] is trusted_host_adapter assert prefix2 not in session.adapters @@ -108,8 +108,8 @@ def test_add_trusted_host(self): ('host1', None), ('host3', None), ('host2', None) ] # Check that prefix3 is still present. - assert session.adapters[prefix3] is insecure_adapter - assert session.adapters[prefix2] is insecure_adapter + assert session.adapters[prefix3] is trusted_host_adapter + assert session.adapters[prefix2] is trusted_host_adapter # Test that adding the same host doesn't create a duplicate. session.add_trusted_host('host3') @@ -123,7 +123,7 @@ def test_add_trusted_host(self): ('host1', None), ('host3', None), ('host2', None), ('host4', 8080) ] - assert session.adapters[prefix4] is insecure_adapter + assert session.adapters[prefix4] is trusted_host_adapter def test_add_trusted_host__logging(self, caplog): """ diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py index c12cdb64bf6..57b50017a7f 100644 --- a/tests/unit/test_req_file.py +++ b/tests/unit/test_req_file.py @@ -401,10 +401,13 @@ def test_set_finder_trusted_host( ) assert list(finder.trusted_hosts) == ['host1', 'host2:8080'] session = finder._link_collector.session - assert session.adapters['https://host1/'] is session._insecure_adapter + assert ( + session.adapters['https://host1/'] + is session._trusted_host_adapter + ) assert ( session.adapters['https://host2:8080/'] - is session._insecure_adapter + is session._trusted_host_adapter ) # Test the log message.