From e81fee6b0baa9d7e93dd5fd7fb68b0062b154dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 16 Jul 2026 14:14:46 +0200 Subject: [PATCH 1/2] Fix reporting of port as string for outgoing domains --- aikido_zen/storage/hostnames.py | 21 ++++++++++++++++++++ aikido_zen/storage/hostnames_test.py | 29 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/aikido_zen/storage/hostnames.py b/aikido_zen/storage/hostnames.py index 6b3d86a02..c05d7689d 100644 --- a/aikido_zen/storage/hostnames.py +++ b/aikido_zen/storage/hostnames.py @@ -1,5 +1,7 @@ """Export Hostnames class""" +import socket + class Hostnames: """Stores hostnames""" @@ -10,6 +12,7 @@ def __init__(self, max_entries=200): def add(self, hostname, port, hits=1): """Add a hostname and port to the map""" + port = normalize_port(port) key = get_key(hostname, port) if not self.map.get(key): self.map[key] = {"hostname": hostname, "port": port, "hits": 0} @@ -31,3 +34,21 @@ def clear(self): def get_key(hostname, port): """Returns a string key""" return f"{hostname}:{port}" + + +def normalize_port(port): + """ + Ensures port is an int (or None), never a str. + `socket.getaddrinfo` accepts a service name (e.g. "http") or a numeric + string as the port, so it needs to be resolved/parsed to a number here. + """ + if port is None or isinstance(port, int): + return port + try: + return int(port) + except (TypeError, ValueError): + pass + try: + return socket.getservbyname(port) + except OSError: + return None diff --git a/aikido_zen/storage/hostnames_test.py b/aikido_zen/storage/hostnames_test.py index b7a6bea35..92218e01e 100644 --- a/aikido_zen/storage/hostnames_test.py +++ b/aikido_zen/storage/hostnames_test.py @@ -99,6 +99,35 @@ def test_add_none_port(): assert hostnames.map[key]["hits"] == 1 +def test_add_numeric_string_port(): + """Test that a numeric string port is stored as an int.""" + hostnames = Hostnames(max_entries=3) + hostnames.add("example.com", "80") + key = "example.com:80" + assert key in hostnames.map + assert hostnames.map[key]["port"] == 80 + assert isinstance(hostnames.map[key]["port"], int) + + +def test_add_service_name_port(): + """Test that a service name port (e.g. 'https') is resolved to its number.""" + hostnames = Hostnames(max_entries=3) + hostnames.add("example.com", "https") + key = "example.com:443" + assert key in hostnames.map + assert hostnames.map[key]["port"] == 443 + assert isinstance(hostnames.map[key]["port"], int) + + +def test_add_unknown_service_name_port(): + """Test that an unresolvable service name port falls back to None.""" + hostnames = Hostnames(max_entries=3) + hostnames.add("example.com", "not-a-real-service") + key = "example.com:None" + assert key in hostnames.map + assert hostnames.map[key]["port"] is None + + def test_exceed_max_entries_with_multiple_ports(): """Test exceeding max entries with multiple ports.""" hostnames = Hostnames(max_entries=3) From 88a424671f3b7b822b7bbafbd72b7d47f9b483cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 16 Jul 2026 14:18:17 +0200 Subject: [PATCH 2/2] Fix codequality comment --- aikido_zen/storage/hostnames.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aikido_zen/storage/hostnames.py b/aikido_zen/storage/hostnames.py index c05d7689d..22b14dac1 100644 --- a/aikido_zen/storage/hostnames.py +++ b/aikido_zen/storage/hostnames.py @@ -12,10 +12,10 @@ def __init__(self, max_entries=200): def add(self, hostname, port, hits=1): """Add a hostname and port to the map""" - port = normalize_port(port) - key = get_key(hostname, port) + normalized_port = normalize_port(port) + key = get_key(hostname, normalized_port) if not self.map.get(key): - self.map[key] = {"hostname": hostname, "port": port, "hits": 0} + self.map[key] = {"hostname": hostname, "port": normalized_port, "hits": 0} if len(self.map) > self.max_entries: # Remove the first added hostname first_added = next(iter(self.map))