From cc5fc0caae825ac62e199a090fade162a2ba4470 Mon Sep 17 00:00:00 2001 From: "Jokob @NetAlertX" <96159884+jokob-sk@users.noreply.github.com> Date: Sun, 24 May 2026 22:25:17 +0000 Subject: [PATCH] Enhance node name extraction logic to robustly handle dots in identifiers for PUSH and PULL modes --- front/plugins/sync/sync.py | 15 ++++++++----- test/plugins/test_sync_protocol.py | 35 ++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/front/plugins/sync/sync.py b/front/plugins/sync/sync.py index c778fc72..9ed0c386 100755 --- a/front/plugins/sync/sync.py +++ b/front/plugins/sync/sync.py @@ -182,13 +182,16 @@ def main(): # are pipe-delimited — catch and skip them via the JSONDecodeError guard below. parts = file_name.split('.') if len(parts) > 2: - # Extract node name: - # decoded/encoded: last_result.PLUGIN.decoded.NodeName.N.log → parts[3] - # pull mode: last_result.NodeName.log → parts[1] - if 'decoded' in file_name or 'encoded' in file_name: - syncHubNodeName = parts[3] + # Extract node name robustly, handling dots in plugin/node identifiers. + # PUSH (decoded/encoded): split on the '.decoded.'/.encoded.' marker; + # strip the trailing .N.log counter with rsplit from the right. + # PULL: strip the known 'last_result.' prefix and '.log' suffix. + if '.decoded.' in file_name or '.encoded.' in file_name: + _marker = '.decoded.' if '.decoded.' in file_name else '.encoded.' + _, _after = file_name.split(_marker, 1) + syncHubNodeName = _after.rsplit('.', 2)[0] else: - syncHubNodeName = parts[1] + syncHubNodeName = file_name[len('last_result.'):-len('.log')] file_path = f"{LOG_PATH}/{file_name}" diff --git a/test/plugins/test_sync_protocol.py b/test/plugins/test_sync_protocol.py index fc0a7bdb..c765394f 100644 --- a/test/plugins/test_sync_protocol.py +++ b/test/plugins/test_sync_protocol.py @@ -71,11 +71,18 @@ def _node_name_from_filename(file_name: str) -> str: """Mirror of the node-name extraction in sync.main() (Mode 3). Real file formats produced by the system: - PUSH (post-decode): last_result.PLUGIN.decoded.NodeName.N.log → parts[3] - PULL: last_result.NodeName.log → parts[1] + PUSH (post-decode): last_result.PLUGIN.decoded.NodeName.N.log + — split on '.decoded.' marker, strip .N.log with rsplit from the right + PULL: last_result.NodeName.log + — strip 'last_result.' prefix and '.log' suffix + + Both forms handle dots anywhere in PLUGIN or NodeName. """ - parts = file_name.split(".") - return parts[3] if ("decoded" in file_name or "encoded" in file_name) else parts[1] + if '.decoded.' in file_name or '.encoded.' in file_name: + marker = '.decoded.' if '.decoded.' in file_name else '.encoded.' + _, after = file_name.split(marker, 1) + return after.rsplit('.', 2)[0] + return file_name[len('last_result.'):-len('.log')] def _should_delete_after_process(filename: str) -> bool: @@ -339,6 +346,26 @@ class TestNodeNameExtraction: assert _node_name_from_filename(fname) == "HubNode", \ f"Expected 'HubNode' from {fname}" + # --- dot-in-identifier regression (fragile parts[3] fix) --- + + def test_pull_node_name_with_dots(self): + # PULL mode: node name set to e.g. "node.home" or an IP like "192.168.1.82" + assert _node_name_from_filename("last_result.node.home.log") == "node.home" + assert _node_name_from_filename("last_result.192.168.1.82.log") == "192.168.1.82" + + def test_push_decoded_node_name_with_dots(self): + # Node name "Node.Vlan01" must survive the filename round-trip intact + assert _node_name_from_filename("last_result.ARPSCAN.decoded.Node.Vlan01.1.log") == "Node.Vlan01" + + def test_push_decoded_plugin_name_with_dots(self): + # Hypothetical plugin with a dot in its name must not shift the node index + assert _node_name_from_filename("last_result.MY.PLUGIN.decoded.NodeA.1.log") == "NodeA" + + def test_push_both_identifiers_with_dots(self): + assert _node_name_from_filename( + "last_result.A.B.decoded.x.y.z.1.log" + ) == "x.y.z" + # =========================================================================== # CurrentScan candidates filter (Mode 3 – RECEIVE)