diff --git a/test_testping1.py b/test_testping1.py index c1441cb..656e0eb 100644 --- a/test_testping1.py +++ b/test_testping1.py @@ -117,6 +117,19 @@ def test_is_reachable_prevents_log_injection(self, mock_call): self.assertIn(r"Invalid timeout value: '1\nERROR:root:System Compromised'", log.output[0]) self.assertNotIn("\nERROR:root:System Compromised", log.output[0]) + @patch('testping1.subprocess.call') + def test_is_reachable_ssrf_log_injection(self, mock_call): + """Test is_reachable escapes log injection in the SSRF block using an IPv6 scope ID.""" + # IPv6 permits a scope ID which can bypass validation if it contains a newline. + # This tests that the original `ip` string is escaped when logged by the SSRF filter. + malicious_ip = "fe80::1%eth0\nERROR:root:System Compromised" + + with self.assertLogs(level='ERROR') as log: + self.assertFalse(is_reachable(malicious_ip)) + self.assertIn(r"IP address not allowed for scanning: 'fe80::1%eth0\nERROR:root:System Compromised'", log.output[0]) + self.assertNotIn("\nERROR:root:System Compromised", log.output[0]) + mock_call.assert_not_called() + @patch('testping1.subprocess.call') def test_is_reachable_subprocess_timeout(self, mock_call): """Test is_reachable handles subprocess.TimeoutExpired securely.""" diff --git a/testping1.py b/testping1.py index 54205c7..4a412b6 100644 --- a/testping1.py +++ b/testping1.py @@ -57,7 +57,10 @@ def is_reachable(ip, timeout=1): # Block loopback, link-local, multicast, unspecified, and reserved addresses from being pinged. # reserved addresses include the broadcast address (255.255.255.255) if ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_multicast or ip_obj.is_unspecified or ip_obj.is_reserved: - logging.error(f"IP address not allowed for scanning: {ip}") + # 🛡️ Sentinel: Sanitize log input using repr() to prevent CRLF/Log Injection + # IPv6 addresses can contain an arbitrary scope ID (e.g., %eth0\r\n) which is + # not sanitized by ipaddress.ip_address() and could allow log spoofing. + logging.error(f"IP address not allowed for scanning: {repr(ip)}") return False # 🛡️ Sentinel: Validate timeout length to prevent CPU exhaustion (DoS)