From 2441d72b4185f301cd799de01afd85a3327281c9 Mon Sep 17 00:00:00 2001 From: Pouzor Date: Sat, 7 Mar 2026 23:31:33 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20banner=5Fregex=20sigs=20require=20actual?= =?UTF-8?q?=20banner=20match=20=E2=80=94=20prevents=20wrong=20type=20infer?= =?UTF-8?q?ence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Banner-specific signatures (e.g. AdGuard on port 3000) were incorrectly matching when nmap returned no banner, causing wrong suggested_node_type (e.g. 'router' for a media server). Now a signature with banner_regex is only matched when a banner is present AND matches the regex. Also make _PORT_TYPE_HINTS always contribute to suggest_node_type found set (not just as elif fallback) so well-known ports like 8006 still resolve to proxmox even when a generic sig also matched. --- backend/app/services/fingerprint.py | 4 ++-- backend/tests/test_fingerprint.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/app/services/fingerprint.py b/backend/app/services/fingerprint.py index 51cdefb..4470776 100644 --- a/backend/app/services/fingerprint.py +++ b/backend/app/services/fingerprint.py @@ -21,7 +21,7 @@ def match_port(port: int, protocol: str, banner: str | None = None) -> dict[str, for sig in _load(): if sig["port"] != port or sig["protocol"] != protocol: continue - if sig.get("banner_regex") and banner and not re.search(sig["banner_regex"], banner, re.IGNORECASE): + if sig.get("banner_regex") and (not banner or not re.search(sig["banner_regex"], banner, re.IGNORECASE)): continue return sig return None @@ -95,7 +95,7 @@ def suggest_node_type(open_ports: list[dict[str, Any]]) -> str: sig = match_port(port, proto) if sig and sig.get("suggested_node_type"): found.add(sig["suggested_node_type"]) - elif port in _PORT_TYPE_HINTS: + if port in _PORT_TYPE_HINTS: found.add(_PORT_TYPE_HINTS[port]) for t in priority: if t in found: diff --git a/backend/tests/test_fingerprint.py b/backend/tests/test_fingerprint.py index 8aa9630..159e595 100644 --- a/backend/tests/test_fingerprint.py +++ b/backend/tests/test_fingerprint.py @@ -50,11 +50,11 @@ def test_match_port_with_banner_no_match_falls_through_to_next(): assert result["service_name"] == "Web UI" -def test_match_port_no_banner_matches_first_sig(): - # no banner → banner check is skipped entirely, first matching sig wins +def test_match_port_no_banner_skips_banner_regex_sigs(): + # no banner → banner_regex sigs are skipped, falls through to generic fallback result = match_port(8006, "tcp", banner=None) assert result is not None - assert result["service_name"] == "Proxmox API" + assert result["service_name"] == "Web UI" # ── fingerprint_ports ─────────────────────────────────────────────────────────