""" Comprehensive tests for IP address blocking in SecurityWatchdog. Tests cover IPv4, IPv6, localhost, private networks, edge cases, and interactions with allowed_domains and prohibited_domains configurations. """ from bubus import EventBus from browser_use.browser import BrowserProfile, BrowserSession from browser_use.browser.watchdogs.security_watchdog import SecurityWatchdog class TestIPv4Blocking: """Test blocking of IPv4 addresses.""" def test_block_public_ipv4_addresses(self): """Test that public IPv4 addresses are blocked when block_ip_addresses=True.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Public IPv4 addresses should be blocked assert watchdog._is_url_allowed('http://180.1.1.1/supersafe.txt') is False assert watchdog._is_url_allowed('https://8.8.8.8/') is False assert watchdog._is_url_allowed('http://1.1.1.1:8080/api') is False assert watchdog._is_url_allowed('https://142.250.185.46/search') is False assert watchdog._is_url_allowed('http://93.184.216.34/') is False def test_block_private_ipv4_networks(self): """Test that private network IPv4 addresses are blocked.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Private network ranges (RFC 1918) assert watchdog._is_url_allowed('http://192.168.1.1/') is False assert watchdog._is_url_allowed('http://192.168.0.100/admin') is False assert watchdog._is_url_allowed('http://10.0.0.1/') is False assert watchdog._is_url_allowed('http://10.255.255.255/') is False assert watchdog._is_url_allowed('http://172.16.0.1/') is False assert watchdog._is_url_allowed('http://172.31.255.254/') is False def test_block_localhost_ipv4(self): """Test that localhost IPv4 addresses are blocked.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Localhost/loopback addresses assert watchdog._is_url_allowed('http://127.0.0.1/') is False assert watchdog._is_url_allowed('http://127.0.0.1:8080/') is False assert watchdog._is_url_allowed('https://127.0.0.1:3000/api/test') is False assert watchdog._is_url_allowed('http://127.1.2.3/') is False # Any 127.x.x.x def test_block_ipv4_with_ports_and_paths(self): """Test that IPv4 addresses with ports and paths are blocked.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # With various ports assert watchdog._is_url_allowed('http://8.8.8.8:80/') is False assert watchdog._is_url_allowed('https://8.8.8.8:443/') is False assert watchdog._is_url_allowed('http://192.168.1.1:8080/') is False assert watchdog._is_url_allowed('http://10.0.0.1:3000/api') is False # With paths and query strings assert watchdog._is_url_allowed('http://1.2.3.4/path/to/resource') is False assert watchdog._is_url_allowed('http://5.6.7.8/api?key=value') is False assert watchdog._is_url_allowed('https://9.10.11.12/path/to/file.html#anchor') is False def test_allow_ipv4_when_blocking_disabled(self): """Test that IPv4 addresses are allowed when block_ip_addresses=False (default).""" browser_profile = BrowserProfile(block_ip_addresses=False, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # All IP addresses should be allowed when blocking is disabled assert watchdog._is_url_allowed('http://180.1.1.1/supersafe.txt') is True assert watchdog._is_url_allowed('http://192.168.1.1/') is True assert watchdog._is_url_allowed('http://127.0.0.1:8080/') is True assert watchdog._is_url_allowed('http://8.8.8.8/') is True class TestIPv6Blocking: """Test blocking of IPv6 addresses.""" def test_block_ipv6_addresses(self): """Test that IPv6 addresses are blocked when block_ip_addresses=True.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Public IPv6 addresses (with brackets as per URL standard) assert watchdog._is_url_allowed('http://[2001:db8::1]/') is False assert watchdog._is_url_allowed('https://[2001:4860:4860::8888]/') is False assert watchdog._is_url_allowed('http://[2606:4700:4700::1111]/path') is False assert watchdog._is_url_allowed('https://[2001:db8:85a3::8a2e:370:7334]/api') is False def test_block_ipv6_localhost(self): """Test that IPv6 localhost addresses are blocked.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # IPv6 loopback assert watchdog._is_url_allowed('http://[::1]/') is False assert watchdog._is_url_allowed('http://[::1]:8080/') is False assert watchdog._is_url_allowed('https://[::1]:3000/api') is False assert watchdog._is_url_allowed('http://[0:0:0:0:0:0:0:1]/') is False # Expanded form def test_block_ipv6_with_ports_and_paths(self): """Test that IPv6 addresses with ports and paths are blocked.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # IPv6 with ports assert watchdog._is_url_allowed('http://[2001:db8::1]:80/') is False assert watchdog._is_url_allowed('https://[2001:db8::1]:443/') is False assert watchdog._is_url_allowed('http://[::1]:8080/api') is False # IPv6 with paths assert watchdog._is_url_allowed('http://[2001:db8::1]/path/to/resource') is False assert watchdog._is_url_allowed('https://[2001:db8::1]/api?key=value') is False def test_allow_ipv6_when_blocking_disabled(self): """Test that IPv6 addresses are allowed when block_ip_addresses=False.""" browser_profile = BrowserProfile(block_ip_addresses=False, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # All IPv6 addresses should be allowed assert watchdog._is_url_allowed('http://[2001:db8::1]/') is True assert watchdog._is_url_allowed('http://[::1]:8080/') is True assert watchdog._is_url_allowed('https://[2001:4860:4860::8888]/') is True class TestDomainNamesStillAllowed: """Test that regular domain names are not affected by IP blocking.""" def test_domain_names_allowed_with_ip_blocking(self): """Test that domain names continue to work when IP blocking is enabled.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Regular domain names should still be allowed assert watchdog._is_url_allowed('https://example.com') is True assert watchdog._is_url_allowed('https://www.google.com') is True assert watchdog._is_url_allowed('http://subdomain.example.org/path') is True assert watchdog._is_url_allowed('https://api.github.com/repos') is True assert watchdog._is_url_allowed('http://localhost/') is True # "localhost" is a domain name, not IP assert watchdog._is_url_allowed('http://localhost:8080/api') is True def test_domains_with_numbers_allowed(self): """Test that domain names containing numbers are still allowed.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Domains with numbers (but not valid IP addresses) assert watchdog._is_url_allowed('https://example123.com') is True assert watchdog._is_url_allowed('https://123example.com') is True assert watchdog._is_url_allowed('https://server1.example.com') is True assert watchdog._is_url_allowed('http://web2.site.org') is True class TestIPBlockingWithAllowedDomains: """Test interaction between IP blocking and allowed_domains.""" def test_ip_blocked_even_in_allowed_domains(self): """Test that IPs are blocked even if they're in allowed_domains list.""" # Note: It doesn't make sense to add IPs to allowed_domains, but if someone does, # IP blocking should take precedence browser_profile = BrowserProfile( block_ip_addresses=True, allowed_domains=['example.com', '192.168.1.1'], # IP in allowlist headless=True, user_data_dir=None, ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # IP should be blocked despite being in allowed_domains assert watchdog._is_url_allowed('http://192.168.1.1/') is False # Regular domain should work as expected assert watchdog._is_url_allowed('https://example.com') is True # Other domains not in allowed_domains should be blocked assert watchdog._is_url_allowed('https://other.com') is False def test_allowed_domains_with_ip_blocking_enabled(self): """Test that allowed_domains works normally with IP blocking enabled.""" browser_profile = BrowserProfile( block_ip_addresses=True, allowed_domains=['example.com', '*.google.com'], headless=True, user_data_dir=None ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Allowed domains should work assert watchdog._is_url_allowed('https://example.com') is True assert watchdog._is_url_allowed('https://www.google.com') is True # Not allowed domains should be blocked assert watchdog._is_url_allowed('https://other.com') is False # IPs should be blocked regardless assert watchdog._is_url_allowed('http://8.8.8.8/') is False assert watchdog._is_url_allowed('http://192.168.1.1/') is False class TestIPBlockingWithProhibitedDomains: """Test interaction between IP blocking and prohibited_domains.""" def test_ip_blocked_regardless_of_prohibited_domains(self): """Test that IPs are blocked when IP blocking is on, independent of prohibited_domains.""" browser_profile = BrowserProfile( block_ip_addresses=True, prohibited_domains=['example.com'], headless=True, user_data_dir=None ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # IPs should be blocked due to IP blocking assert watchdog._is_url_allowed('http://192.168.1.1/') is False assert watchdog._is_url_allowed('http://8.8.8.8/') is False # Prohibited domain should be blocked assert watchdog._is_url_allowed('https://example.com') is False # Other domains should be allowed assert watchdog._is_url_allowed('https://other.com') is True def test_prohibited_domains_without_ip_blocking(self): """Test that prohibited_domains works normally when IP blocking is disabled.""" browser_profile = BrowserProfile( block_ip_addresses=False, prohibited_domains=['example.com', '8.8.8.8'], headless=True, user_data_dir=None ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Prohibited domain should be blocked assert watchdog._is_url_allowed('https://example.com') is False # IP in prohibited list should be blocked (by prohibited_domains, not IP blocking) assert watchdog._is_url_allowed('http://8.8.8.8/') is False # Other IPs should be allowed (IP blocking is off) assert watchdog._is_url_allowed('http://192.168.1.1/') is True # Other domains should be allowed assert watchdog._is_url_allowed('https://other.com') is True class TestEdgeCases: """Test edge cases and invalid inputs.""" def test_invalid_urls_handled_gracefully(self): """Test that invalid URLs don't cause crashes.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Invalid URLs should return False assert watchdog._is_url_allowed('not-a-url') is False assert watchdog._is_url_allowed('') is False assert watchdog._is_url_allowed('http://') is False assert watchdog._is_url_allowed('://example.com') is False def test_internal_browser_urls_allowed(self): """Test that internal browser URLs are still allowed with IP blocking.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Internal URLs should always be allowed assert watchdog._is_url_allowed('about:blank') is True assert watchdog._is_url_allowed('chrome://new-tab-page/') is True assert watchdog._is_url_allowed('chrome://new-tab-page') is True assert watchdog._is_url_allowed('chrome://newtab/') is True def test_ipv4_lookalike_domains_allowed(self): """Test that domains that look like IPs but aren't are still allowed.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # These look like IPs but have too many/few octets or invalid ranges # The IP parser should reject them, so they're treated as domain names assert watchdog._is_url_allowed('http://999.999.999.999/') is True # Invalid IP range assert watchdog._is_url_allowed('http://1.2.3.4.5/') is True # Too many octets assert watchdog._is_url_allowed('http://1.2.3/') is True # Too few octets def test_different_schemes_with_ips(self): """Test that IP blocking works across different URL schemes.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # HTTP and HTTPS assert watchdog._is_url_allowed('http://192.168.1.1/') is False assert watchdog._is_url_allowed('https://192.168.1.1/') is False # FTP (if browser supports it) assert watchdog._is_url_allowed('ftp://192.168.1.1/') is False # WebSocket (parsed as regular URL) assert watchdog._is_url_allowed('ws://192.168.1.1:8080/') is False assert watchdog._is_url_allowed('wss://192.168.1.1:8080/') is False class TestIsIPAddressHelper: """Test the _is_ip_address helper method directly.""" def test_valid_ipv4_detection(self): """Test that valid IPv4 addresses are correctly detected.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Valid IPv4 addresses assert watchdog._is_ip_address('127.0.0.1') is True assert watchdog._is_ip_address('192.168.1.1') is True assert watchdog._is_ip_address('8.8.8.8') is True assert watchdog._is_ip_address('255.255.255.255') is True assert watchdog._is_ip_address('0.0.0.0') is True def test_valid_ipv6_detection(self): """Test that valid IPv6 addresses are correctly detected.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Valid IPv6 addresses (without brackets - those are URL-specific) assert watchdog._is_ip_address('::1') is True assert watchdog._is_ip_address('2001:db8::1') is True assert watchdog._is_ip_address('2001:4860:4860::8888') is True assert watchdog._is_ip_address('fe80::1') is True assert watchdog._is_ip_address('2001:db8:85a3::8a2e:370:7334') is True def test_invalid_ip_detection(self): """Test that non-IP strings are correctly identified as not IPs.""" browser_profile = BrowserProfile(block_ip_addresses=True, headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Domain names (not IPs) assert watchdog._is_ip_address('example.com') is False assert watchdog._is_ip_address('www.google.com') is False assert watchdog._is_ip_address('localhost') is False # Invalid IPs assert watchdog._is_ip_address('999.999.999.999') is False assert watchdog._is_ip_address('1.2.3') is False assert watchdog._is_ip_address('1.2.3.4.5') is False assert watchdog._is_ip_address('not-an-ip') is False assert watchdog._is_ip_address('') is False # IPs with ports or paths (not valid for the helper - it only checks hostnames) assert watchdog._is_ip_address('192.168.1.1:8080') is False assert watchdog._is_ip_address('192.168.1.1/path') is False class TestDefaultBehavior: """Test that default behavior (no IP blocking) is maintained.""" def test_default_block_ip_addresses_is_false(self): """Test that block_ip_addresses defaults to False.""" browser_profile = BrowserProfile(headless=True, user_data_dir=None) # Default should be False assert browser_profile.block_ip_addresses is False def test_no_blocking_by_default(self): """Test that IPs are not blocked by default.""" browser_profile = BrowserProfile(headless=True, user_data_dir=None) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # All IPs should be allowed by default assert watchdog._is_url_allowed('http://180.1.1.1/supersafe.txt') is True assert watchdog._is_url_allowed('http://192.168.1.1/') is True assert watchdog._is_url_allowed('http://127.0.0.1:8080/') is True assert watchdog._is_url_allowed('http://[::1]/') is True assert watchdog._is_url_allowed('https://8.8.8.8/') is True class TestComplexScenarios: """Test complex real-world scenarios.""" def test_mixed_configuration_comprehensive(self): """Test a complex configuration with multiple security settings.""" browser_profile = BrowserProfile( block_ip_addresses=True, allowed_domains=['example.com', '*.google.com'], prohibited_domains=['bad.example.com'], # Should be ignored when allowlist is set headless=True, user_data_dir=None, ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Allowed domains should work assert watchdog._is_url_allowed('https://example.com') is True assert watchdog._is_url_allowed('https://www.google.com') is True assert watchdog._is_url_allowed('https://mail.google.com') is True # IPs should be blocked assert watchdog._is_url_allowed('http://8.8.8.8/') is False assert watchdog._is_url_allowed('http://192.168.1.1/') is False # Domains not in allowlist should be blocked assert watchdog._is_url_allowed('https://other.com') is False def test_localhost_development_scenario(self): """Test typical local development scenario.""" # Developer wants to block external IPs but allow domain names browser_profile = BrowserProfile( block_ip_addresses=True, headless=True, user_data_dir=None, # No domain restrictions ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Domain names should work (including localhost as a name) assert watchdog._is_url_allowed('http://localhost:3000/') is True assert watchdog._is_url_allowed('http://localhost:8080/api') is True # But localhost IP should be blocked assert watchdog._is_url_allowed('http://127.0.0.1:3000/') is False # External domains should work assert watchdog._is_url_allowed('https://api.example.com') is True # External IPs should be blocked assert watchdog._is_url_allowed('http://8.8.8.8/') is False def test_security_hardening_scenario(self): """Test maximum security scenario with IP blocking and domain restrictions.""" browser_profile = BrowserProfile( block_ip_addresses=True, allowed_domains=['example.com', 'api.example.com'], headless=True, user_data_dir=None, ) browser_session = BrowserSession(browser_profile=browser_profile) event_bus = EventBus() watchdog = SecurityWatchdog(browser_session=browser_session, event_bus=event_bus) # Only specified domains allowed assert watchdog._is_url_allowed('https://example.com') is True assert watchdog._is_url_allowed('https://api.example.com') is True # IPs blocked assert watchdog._is_url_allowed('http://192.168.1.1/') is False # Other domains blocked assert watchdog._is_url_allowed('https://other.com') is False # Even localhost blocked assert watchdog._is_url_allowed('http://127.0.0.1/') is False