mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-09-13 14:49:58 -04:00
100 lines
4.2 KiB
Python
100 lines
4.2 KiB
Python
"""
|
|
Tests for centralized MAC-case normalization in plugin_object_class.
|
|
|
|
Regression coverage for https://github.com/netalertx/NetAlertX/issues/1775:
|
|
plugin authors are responsible for calling normalize_mac() themselves before
|
|
writing a MAC as primaryId, and it's easy for one parsing branch in one
|
|
plugin to forget (see server/plugins/snmp_discovery) - producing a device
|
|
reported under two different MAC cases and spurious connect/disconnect
|
|
events, since plugin_object_class.idsHash (used to detect new/missing
|
|
objects across scan cycles) is a case-sensitive hash of primaryId.
|
|
|
|
plugin_object_class now normalizes primaryId itself as a generic safety
|
|
net, via utils.plugin_utils.primary_id_is_mac(), whenever the owning
|
|
plugin's config.json marks objectPrimaryId as a MAC (true for every
|
|
device-scanning plugin - arp_scan, snmp_discovery, sync, etc. - and false
|
|
for publishers/exporters/other non-scanning plugins, whose primaryId is not
|
|
a MAC and must not be silently rewritten).
|
|
|
|
Run from inside the NetAlertX container - server/plugin.py isn't importable
|
|
standalone outside it (real conf/database/api imports).
|
|
|
|
pytest "test/server/test_plugin_object_mac_normalization.py" -v
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Path setup
|
|
# ---------------------------------------------------------------------------
|
|
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
|
sys.path.extend([f"{INSTALL_PATH}/server/plugins", f"{INSTALL_PATH}/server"])
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
from db_test_helpers import make_plugin_event_row # noqa: E402
|
|
|
|
from plugin import plugin_object_class # noqa: E402
|
|
from utils.plugin_utils import primary_id_is_mac # noqa: E402
|
|
|
|
PREFIX = "TESTPLG"
|
|
|
|
|
|
def _scanner_plugin(prefix=PREFIX):
|
|
"""Shaped like a real device-scanning plugin's config.json
|
|
(e.g. snmp_discovery, arp_scan): objectPrimaryId marked as a MAC."""
|
|
return {
|
|
"unique_prefix": prefix,
|
|
"settings": [
|
|
{"function": "WATCH", "value": ["watchedValue1", "watchedValue2"]},
|
|
],
|
|
"mapped_to_table": "CurrentScan",
|
|
"database_column_definitions": [
|
|
{"column": "objectPrimaryId", "mapped_to_column": "scanMac", "type": "device_mac"},
|
|
{"column": "objectSecondaryId", "mapped_to_column": "scanLastIP", "type": "device_ip"},
|
|
],
|
|
}
|
|
|
|
|
|
def _non_scanner_plugin(prefix=PREFIX):
|
|
"""Shaped like a non-scanning plugin (publisher/exporter): no
|
|
CurrentScan mapping, primaryId is not a MAC."""
|
|
return {
|
|
"unique_prefix": prefix,
|
|
"settings": [],
|
|
}
|
|
|
|
|
|
class TestScannerPluginNormalizesMacCase:
|
|
def test_uppercase_primary_id_is_normalized(self):
|
|
row = make_plugin_event_row(PREFIX, "AA:BB:CC:DD:EE:FF")
|
|
obj = plugin_object_class(_scanner_plugin(), row)
|
|
assert obj.primaryId == "aa:bb:cc:dd:ee:ff"
|
|
|
|
def test_hyphenated_primary_id_is_normalized_to_colon_form(self):
|
|
row = make_plugin_event_row(PREFIX, "AA-BB-CC-DD-EE-FF")
|
|
obj = plugin_object_class(_scanner_plugin(), row)
|
|
assert obj.primaryId == "aa:bb:cc:dd:ee:ff"
|
|
|
|
def test_idshash_agrees_across_case_variants(self):
|
|
"""The actual bug: two readings of the same device that differ only
|
|
in MAC case must produce the same idsHash, or the scan-cycle diff
|
|
engine treats them as different objects (spurious connect/disconnect)."""
|
|
upper = plugin_object_class(_scanner_plugin(), make_plugin_event_row(PREFIX, "AA:BB:CC:DD:EE:FF"))
|
|
lower = plugin_object_class(_scanner_plugin(), make_plugin_event_row(PREFIX, "aa:bb:cc:dd:ee:ff"))
|
|
assert upper.idsHash == lower.idsHash
|
|
|
|
|
|
class TestNonScannerPluginLeavesPrimaryIdAlone:
|
|
def test_primary_id_untouched(self):
|
|
"""A publisher/exporter's primaryId isn't a MAC - must not be run
|
|
through normalize_mac(), which would silently mangle it."""
|
|
row = make_plugin_event_row(PREFIX, "Some-Mixed-Case-ID")
|
|
obj = plugin_object_class(_non_scanner_plugin(), row)
|
|
assert obj.primaryId == "Some-Mixed-Case-ID"
|
|
|
|
|
|
def test_helper_detects_scanner_vs_non_scanner_plugins():
|
|
assert primary_id_is_mac(_scanner_plugin()) is True
|
|
assert primary_id_is_mac(_non_scanner_plugin()) is False
|