def run_tests(self): # import here, cause outside the eggs aren't loaded import pytest import advocate import advocate.packages.ipaddress as ipaddress from test.monkeypatching import AdvocateEnforcer, CheckedSocket # We need to change to the checkout dir, requests' test suite expects certain files # to be in the CWD. with pushd(self.requests_location): validator = advocate.AddrValidator( ip_whitelist={ # requests needs to be able to hit these for its tests! ipaddress.ip_network("127.0.0.1"), ipaddress.ip_network("127.0.1.1"), ipaddress.ip_network("10.255.255.1"), }, # the `httpbin` fixture uses a random fixture, we need to allow all ports port_whitelist=set(range(0, 65535)), ) enforcer = AdvocateEnforcer(validator) with enforcer.monkeypatch_requests_module(): socket.socket = CheckedSocket errno = pytest.main(self.pytest_args, plugins=["requests_pytest_plugin"]) sys.exit(errno)
def test_ip_whitelist_blacklist_conflict(self): """Manual whitelist should take precedence over manual blacklist""" validator = AddrValidator( ip_whitelist=(ipaddress.ip_network("127.0.0.1"), ), ip_blacklist=(ipaddress.ip_network("127.0.0.1"), ), ) self.assertTrue(validator.is_ip_allowed("127.0.0.1"))
def test_manual_ip_blacklist(self): """Test manually blacklisting based on IP""" validator = AddrValidator( allow_ipv6=True, ip_blacklist=( ipaddress.ip_network("132.0.5.0/24"), ipaddress.ip_network("152.0.0.0/8"), ipaddress.ip_network("::1"), ), ) self.assertFalse(validator.is_ip_allowed("132.0.5.1")) self.assertFalse(validator.is_ip_allowed("152.254.90.1")) self.assertTrue(validator.is_ip_allowed("178.254.90.1")) self.assertFalse(validator.is_ip_allowed("::1")) # Google, found via `dig google.com AAAA` self.assertTrue(validator.is_ip_allowed("2607:f8b0:400a:807::200e"))
def test_advocate_wrapper_futures(self): wrapper = RequestsAPIWrapper(validator=AddrValidator()) local_validator = AddrValidator(ip_whitelist={ ipaddress.ip_network("127.0.0.1"), }) local_wrapper = RequestsAPIWrapper(validator=local_validator) with self.assertRaises(UnacceptableAddressException): sess = wrapper.FuturesSession() sess.get("http://127.0.0.1/").result() with self.assertRaises(Exception) as cm: sess = local_wrapper.FuturesSession() sess.get("http://127.0.0.1:1/").result() # Check that we got a connection exception instead of a validation one # This might be either exception depending on the requests version self.assertRegexpMatches( cm.exception.__class__.__name__, r"\A(Connection|Protocol)Error", ) with self.assertRaises(UnacceptableAddressException): sess = wrapper.FuturesSession() sess.get("http://localhost:1/").result() with self.assertRaises(UnacceptableAddressException): sess = wrapper.FuturesSession() sess.get("https://localhost:1/").result()
def pytest_runtestloop(): validator = advocate.AddrValidator( ip_whitelist={ # requests needs to be able to hit these for its tests! ipaddress.ip_network("127.0.0.1"), ipaddress.ip_network("127.0.1.1"), ipaddress.ip_network("10.255.255.1"), }, # the `httpbin` fixture uses a random port, we need to allow all ports port_whitelist=set(range(0, 65535)), ) # this will yell at us if we failed to patch something socket.socket = CheckedSocket # requests' tests rely on being able to pickle a `Session` advocate.api.RequestsAPIWrapper.SUPPORT_WRAPPER_PICKLING = True wrapper = advocate.api.RequestsAPIWrapper(validator) for attr in advocate.api.__all__: setattr(requests, attr, getattr(wrapper, attr))
def test_connect_without_local_addresses(self, mock_determine_local_addresses): fake_addresses = [ipaddress.ip_network("200.1.1.1")] mock_determine_local_addresses.return_value = fake_addresses validator = permissive_validator(autodetect_local_addresses=True) advocate.get("http://example.com/", validator=validator) # Check that `is_ip_allowed` didn't make its own call to determine # local addresses mock_determine_local_addresses.assert_called_once_with() mock_determine_local_addresses.reset_mock() validator = permissive_validator(autodetect_local_addresses=False) advocate.get("http://example.com", validator=validator) mock_determine_local_addresses.assert_not_called()
def test_wrapper_session_pickle(self): # Make sure the validator still works after a pickle round-trip wrapper = RequestsAPIWrapper(validator=AddrValidator(ip_whitelist={ ipaddress.ip_network("127.0.0.1"), })) sess_instance = pickle.loads(pickle.dumps(wrapper.Session())) with self.assertRaises(Exception) as cm: sess_instance.get("http://127.0.0.1:1/") self.assertRegexpMatches( cm.exception.__class__.__name__, r"\A(Connection|Protocol)Error", ) self.assertRaises(UnacceptableAddressException, sess_instance.get, "http://127.0.1.1:1/")
def test_local_address_handling(self, mock_determine_local_addresses): fake_addresses = [ipaddress.ip_network("200.1.1.1")] mock_determine_local_addresses.return_value = fake_addresses self.assertFalse( self._is_addrinfo_allowed("200.1.1.1", 80, autodetect_local_addresses=True)) # Check that `is_ip_allowed` didn't make its own call to determine # local addresses mock_determine_local_addresses.assert_called_once_with() mock_determine_local_addresses.reset_mock() self.assertTrue( self._is_addrinfo_allowed( "200.1.1.1", 80, autodetect_local_addresses=False, )) mock_determine_local_addresses.assert_not_called()
def test_wrapper_session_subclass(self): # Make sure pickle doesn't explode if we try to pickle a subclass # of `wrapper.Session` wrapper = RequestsAPIWrapper(validator=AddrValidator(ip_whitelist={ ipaddress.ip_network("127.0.0.1"), })) class _SessionThing(wrapper.Session): pass sess_instance = pickle.loads(pickle.dumps(_SessionThing())) with self.assertRaises(Exception) as cm: sess_instance.get("http://127.0.0.1:1/") self.assertRegexpMatches( cm.exception.__class__.__name__, r"\A(Connection|Protocol)Error", ) self.assertRaises(UnacceptableAddressException, sess_instance.get, "http://127.0.1.1:1/")
def test_safecurl_blacklist(self): """Test that we at least disallow everything SafeCurl does""" # All IPs that SafeCurl would disallow bad_netblocks = (ipaddress.ip_network(x) for x in ('0.0.0.0/8', '10.0.0.0/8', '100.64.0.0/10', '127.0.0.0/8', '169.254.0.0/16', '172.16.0.0/12', '192.0.0.0/29', '192.0.2.0/24', '192.88.99.0/24', '192.168.0.0/16', '198.18.0.0/15', '198.51.100.0/24', '203.0.113.0/24', '224.0.0.0/4', '240.0.0.0/4')) i = 0 validator = AddrValidator() for bad_netblock in bad_netblocks: num_ips = bad_netblock.num_addresses # Don't test *every* IP in large netblocks step_size = int(min(max(num_ips / 255, 1), 128)) for ip_idx in six.moves.range(0, num_ips, step_size): i += 1 bad_ip = bad_netblock[ip_idx] bad_ip_allowed = validator.is_ip_allowed(bad_ip) if bad_ip_allowed: print(i, bad_ip) self.assertFalse(bad_ip_allowed)
def test_ip_whitelist(self): """Test manually whitelisting based on IP""" validator = AddrValidator( ip_whitelist=(ipaddress.ip_network("127.0.0.1"), ), ) self.assertTrue(validator.is_ip_allowed("127.0.0.1"))
from advocate.connection import advocate_getaddrinfo from advocate.exceptions import ( MountDisabledException, NameserverException, UnacceptableAddressException, ) from advocate.packages import ipaddress from advocate.futures import FuturesSession # We use port 1 for testing because nothing is likely to legitimately listen # on it. AddrValidator.DEFAULT_PORT_WHITELIST.add(1) RequestsAPIWrapper.SUPPORT_WRAPPER_PICKLING = True global_wrapper = RequestsAPIWrapper(validator=AddrValidator(ip_whitelist={ ipaddress.ip_network("127.0.0.1"), })) RequestsAPIWrapper.SUPPORT_WRAPPER_PICKLING = False class _WrapperSubclass(global_wrapper.Session): def good_method(self): return "foo" def canonname_supported(): """Check if the nameserver supports the AI_CANONNAME flag travis-ci.org's Python 3 env doesn't seem to support it, so don't try any of the test that rely on it. """