python-tuf/tests/test_proxy_environment.py
Jussi Kukkonen 265e772dba ProxyEnvironment: Handle no_proxy="*"
Add support for leading dots in no_proxy and "*" as a no_proxy value.

Both are supported in requests and based on
https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/
both are somewhat common.

Signed-off-by: Jussi Kukkonen <jkukkonen@google.com>
2025-02-20 10:56:23 +02:00

217 lines
8.1 KiB
Python

# Copyright 2025, the TUF contributors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""Test ngclient ProxyEnvironment"""
from __future__ import annotations
import sys
import unittest
from unittest.mock import Mock, patch
from urllib3 import PoolManager, ProxyManager
from tests import utils
from tuf.ngclient._internal.proxy import ProxyEnvironment
class TestProxyEnvironment(unittest.TestCase):
"""Test ngclient ProxyEnvironment implementation
These tests use the ProxyEnvironment.get_pool_manager() endpoint and then
look at the ProxyEnvironment._poolmanagers dict keys to decide if the result
is correct.
The test environment is changed via mocking getproxies(): this is a urllib
method that returns a dict with the proxy environment variable contents.
Testing ProxyEnvironment.request() would possibly be better but far more
difficult: the current test implementation does not require actually setting up
all of the different proxies.
"""
def assert_pool_managers(
self, env: ProxyEnvironment, expected: list[str | None]
) -> None:
# Pool managers have the expected proxy urls
self.assertEqual(list(env._pool_managers.keys()), expected)
# Pool manager types are as expected
for proxy_url, pool_manager in env._pool_managers.items():
self.assertIsInstance(pool_manager, PoolManager)
if proxy_url is not None:
self.assertIsInstance(pool_manager, ProxyManager)
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_no_variables(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "subdomain.example.com")
env.get_pool_manager("https", "differentsite.com")
# There is a single pool manager (no proxies)
self.assert_pool_managers(env, [None])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_proxy_set(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"https": "http://localhost:8888",
}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "differentsite.com")
# There are two pool managers: A plain poolmanager and https proxymanager
self.assert_pool_managers(env, [None, "http://localhost:8888"])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_proxies_set(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"http": "http://localhost:8888",
"https": "http://localhost:9999",
}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "subdomain.example.com")
env.get_pool_manager("https", "differentsite.com")
# There are two pool managers: A http proxymanager and https proxymanager
self.assert_pool_managers(
env, ["http://localhost:8888", "http://localhost:9999"]
)
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_no_proxy_set(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"http": "http://localhost:8888",
"https": "http://localhost:9999",
"no": "somesite.com, example.com, another.site.com",
}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
# There is a single pool manager (no proxies)
self.assert_pool_managers(env, [None])
env.get_pool_manager("http", "differentsite.com")
env.get_pool_manager("https", "differentsite.com")
# There are three pool managers: plain poolmanager for no_proxy domains,
# http proxymanager and https proxymanager
self.assert_pool_managers(
env, [None, "http://localhost:8888", "http://localhost:9999"]
)
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_no_proxy_subdomain_match(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"https": "http://localhost:9999",
"no": "somesite.com, example.com, another.site.com",
}
env = ProxyEnvironment()
# this should match example.com in no_proxy
env.get_pool_manager("https", "subdomain.example.com")
# There is a single pool manager (no proxies)
self.assert_pool_managers(env, [None])
# this should not match example.com in no_proxy
env.get_pool_manager("https", "xexample.com")
# There are two pool managers: plain poolmanager for no_proxy domains,
# and a https proxymanager
self.assert_pool_managers(env, [None, "http://localhost:9999"])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_no_proxy_wildcard(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"https": "http://localhost:8888",
"no": "*",
}
env = ProxyEnvironment()
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "differentsite.com")
env.get_pool_manager("https", "subdomain.example.com")
# There is a single pool manager, no proxies
self.assert_pool_managers(env, [None])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_no_proxy_leading_dot(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"https": "http://localhost:8888",
"no": ".example.com",
}
env = ProxyEnvironment()
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "subdomain.example.com")
# There is a single pool manager, no proxies
self.assert_pool_managers(env, [None])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_all_proxy_set(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"all": "http://localhost:8888",
}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "subdomain.example.com")
env.get_pool_manager("https", "differentsite.com")
# There is a single proxy manager
self.assert_pool_managers(env, ["http://localhost:8888"])
# This urllib3 currently only handles http and https but let's test anyway
env.get_pool_manager("file", None)
# proxy manager and a plain pool manager
self.assert_pool_managers(env, ["http://localhost:8888", None])
@patch("tuf.ngclient._internal.proxy.getproxies")
def test_all_proxy_and_no_proxy_set(self, mock_getproxies: Mock) -> None:
mock_getproxies.return_value = {
"all": "http://localhost:8888",
"no": "somesite.com, example.com, another.site.com",
}
env = ProxyEnvironment()
env.get_pool_manager("http", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "example.com")
env.get_pool_manager("https", "subdomain.example.com")
# There is a single pool manager (no proxies)
self.assert_pool_managers(env, [None])
env.get_pool_manager("http", "differentsite.com")
env.get_pool_manager("https", "differentsite.com")
# There are two pool managers: plain poolmanager for no_proxy domains and
# one proxymanager
self.assert_pool_managers(env, [None, "http://localhost:8888"])
if __name__ == "__main__":
utils.configure_test_logging(sys.argv)
unittest.main()