Files
bigbrother/tests/test_all_modules.py
T

415 lines
15 KiB
Python

"""Tests for ALL module categories — import, BaseModule subclass, and attribute validation.
Covers: passive (17), active (9), intel (9), connectivity (8).
Stealth modules are already covered by test_stealth_modules.py.
"""
import importlib
import pytest
from modules.base import BaseModule
# ---------------------------------------------------------------------------
# Passive modules (17)
# ---------------------------------------------------------------------------
PASSIVE_MODULE_DOTTED = [
"modules.passive.packet_capture",
"modules.passive.dns_logger",
"modules.passive.tls_sni_extractor",
"modules.passive.credential_sniffer",
"modules.passive.kerberos_harvester",
"modules.passive.host_discovery",
"modules.passive.os_fingerprint",
"modules.passive.traffic_analyzer",
"modules.passive.vlan_discovery",
"modules.passive.network_mapper",
"modules.passive.auth_flow_tracker",
"modules.passive.smb_monitor",
"modules.passive.cloud_token_harvester",
"modules.passive.ldap_harvester",
"modules.passive.rdp_monitor",
"modules.passive.quic_analyzer",
"modules.passive.db_interceptor",
]
PASSIVE_CLASS_NAMES = [
"PacketCapture",
"DNSLogger",
"TLSSNIExtractor",
"CredentialSniffer",
"KerberosHarvester",
"HostDiscovery",
"OSFingerprint",
"TrafficAnalyzer",
"VLANDiscovery",
"NetworkMapper",
"AuthFlowTracker",
"SMBMonitor",
"CloudTokenHarvester",
"LDAPHarvester",
"RDPMonitor",
"QUICAnalyzer",
"DBInterceptor",
]
class TestPassiveModulesImportable:
@pytest.mark.parametrize("dotted_path", PASSIVE_MODULE_DOTTED)
def test_passive_module_importable(self, dotted_path):
"""Each passive module file can be imported without errors."""
mod = importlib.import_module(dotted_path)
assert mod is not None
class TestPassiveModulesAreBaseModule:
@pytest.mark.parametrize("class_name", PASSIVE_CLASS_NAMES)
def test_passive_modules_are_base_module(self, class_name):
"""Each passive module class is a subclass of BaseModule."""
import modules.passive as passive_pkg
cls = getattr(passive_pkg, class_name)
assert issubclass(cls, BaseModule), f"{class_name} is not a BaseModule subclass"
class TestPassiveModuleAttributes:
@pytest.mark.parametrize("class_name", PASSIVE_CLASS_NAMES)
def test_passive_module_attributes(self, class_name):
"""Each passive module has required class attributes."""
import modules.passive as passive_pkg
cls = getattr(passive_pkg, class_name)
assert hasattr(cls, "name") and cls.name != "unnamed"
assert hasattr(cls, "module_type") and cls.module_type == "passive"
assert hasattr(cls, "priority") and isinstance(cls.priority, int)
assert hasattr(cls, "dependencies") and isinstance(cls.dependencies, list)
assert hasattr(cls, "requires_root")
for method_name in ("start", "stop", "status", "configure"):
assert callable(getattr(cls, method_name, None))
class TestPassiveModuleInstantiate:
@pytest.mark.parametrize("class_name", PASSIVE_CLASS_NAMES)
def test_passive_module_instantiate(self, class_name, mock_bus, mock_state, mock_config):
"""Each passive module can be instantiated with mock bus/state/config."""
import modules.passive as passive_pkg
cls = getattr(passive_pkg, class_name)
instance = cls(bus=mock_bus, state=mock_state, config=mock_config)
assert instance is not None
assert instance._running is False
# ---------------------------------------------------------------------------
# Active modules (9)
# ---------------------------------------------------------------------------
ACTIVE_MODULE_DOTTED = [
"modules.active.bettercap_mgr",
"modules.active.arp_spoof",
"modules.active.dns_poison",
"modules.active.dhcp_spoof",
"modules.active.evil_twin",
"modules.active.ipv6_slaac",
"modules.active.responder_mgr",
"modules.active.mitmproxy_mgr",
"modules.active.ntlm_relay",
]
ACTIVE_CLASS_NAMES = [
"BettercapManager",
"ARPSpoof",
"DNSPoison",
"DHCPSpoof",
"EvilTwin",
"IPv6SLAAC",
"ResponderManager",
"MitmproxyManager",
"NTLMRelay",
]
class TestActiveModulesImportable:
@pytest.mark.parametrize("dotted_path", ACTIVE_MODULE_DOTTED)
def test_active_module_importable(self, dotted_path):
"""Each active module file can be imported without errors."""
mod = importlib.import_module(dotted_path)
assert mod is not None
class TestActiveModulesAreBaseModule:
@pytest.mark.parametrize("class_name", ACTIVE_CLASS_NAMES)
def test_active_modules_are_base_module(self, class_name):
"""Each active module class is a subclass of BaseModule."""
import modules.active as active_pkg
cls = getattr(active_pkg, class_name)
assert issubclass(cls, BaseModule), f"{class_name} is not a BaseModule subclass"
class TestActiveModuleAttributes:
@pytest.mark.parametrize("class_name", ACTIVE_CLASS_NAMES)
def test_active_module_attributes(self, class_name):
"""Each active module has required class attributes."""
import modules.active as active_pkg
cls = getattr(active_pkg, class_name)
assert hasattr(cls, "name") and cls.name != "unnamed"
assert hasattr(cls, "module_type") and cls.module_type == "active"
assert hasattr(cls, "priority") and isinstance(cls.priority, int)
assert hasattr(cls, "dependencies") and isinstance(cls.dependencies, list)
assert hasattr(cls, "requires_root")
for method_name in ("start", "stop", "status", "configure"):
assert callable(getattr(cls, method_name, None))
class TestActiveModuleInstantiate:
@pytest.mark.parametrize("class_name", ACTIVE_CLASS_NAMES)
def test_active_module_instantiate(self, class_name, mock_bus, mock_state, mock_config):
"""Each active module can be instantiated with mock bus/state/config."""
import modules.active as active_pkg
cls = getattr(active_pkg, class_name)
instance = cls(bus=mock_bus, state=mock_state, config=mock_config)
assert instance is not None
assert instance._running is False
# ---------------------------------------------------------------------------
# Intel modules (9)
# ---------------------------------------------------------------------------
INTEL_MODULE_DOTTED = [
"modules.intel.credential_db",
"modules.intel.topology_mapper",
"modules.intel.net_intel",
"modules.intel.user_timeline",
"modules.intel.supply_chain_detect",
"modules.intel.change_detector",
"modules.intel.security_posture",
"modules.intel.operator_audit",
"modules.intel.tool_output_parser",
]
INTEL_CLASS_NAMES = [
"CredentialDB",
"TopologyMapper",
"NetIntel",
"UserTimeline",
"SupplyChainDetect",
"ChangeDetector",
"SecurityPosture",
"OperatorAudit",
"ToolOutputParser",
]
class TestIntelModulesImportable:
@pytest.mark.parametrize("dotted_path", INTEL_MODULE_DOTTED)
def test_intel_module_importable(self, dotted_path):
"""Each intel module file can be imported without errors."""
mod = importlib.import_module(dotted_path)
assert mod is not None
class TestIntelModulesAreBaseModule:
@pytest.mark.parametrize("class_name", INTEL_CLASS_NAMES)
def test_intel_modules_are_base_module(self, class_name):
"""Each intel module class is a subclass of BaseModule."""
import modules.intel as intel_pkg
cls = getattr(intel_pkg, class_name)
assert issubclass(cls, BaseModule), f"{class_name} is not a BaseModule subclass"
class TestIntelModuleAttributes:
@pytest.mark.parametrize("class_name", INTEL_CLASS_NAMES)
def test_intel_module_attributes(self, class_name):
"""Each intel module has required class attributes."""
import modules.intel as intel_pkg
cls = getattr(intel_pkg, class_name)
assert hasattr(cls, "name") and cls.name != "unnamed"
assert hasattr(cls, "module_type") and cls.module_type == "intel"
assert hasattr(cls, "priority") and isinstance(cls.priority, int)
assert hasattr(cls, "dependencies") and isinstance(cls.dependencies, list)
assert hasattr(cls, "requires_root")
for method_name in ("start", "stop", "status", "configure"):
assert callable(getattr(cls, method_name, None))
class TestIntelModuleInstantiate:
@pytest.mark.parametrize("class_name", INTEL_CLASS_NAMES)
def test_intel_module_instantiate(self, class_name, mock_bus, mock_state, mock_config):
"""Each intel module can be instantiated with mock bus/state/config."""
import modules.intel as intel_pkg
cls = getattr(intel_pkg, class_name)
instance = cls(bus=mock_bus, state=mock_state, config=mock_config)
assert instance is not None
assert instance._running is False
# ---------------------------------------------------------------------------
# Connectivity modules (8)
# ---------------------------------------------------------------------------
CONNECTIVITY_MODULE_DOTTED = [
"modules.connectivity.wireguard",
"modules.connectivity.tailscale",
"modules.connectivity.bridge",
"modules.connectivity.wifi_client",
"modules.connectivity.cellular_backup",
"modules.connectivity.ble_emergency",
"modules.connectivity.data_exfil",
]
CONNECTIVITY_CLASS_NAMES = [
"WireGuard",
"Tailscale",
"Bridge",
"WiFiClient",
"CellularBackup",
"BLEEmergency",
"DataExfil",
]
class TestConnectivityModulesImportable:
@pytest.mark.parametrize("dotted_path", CONNECTIVITY_MODULE_DOTTED)
def test_connectivity_module_importable(self, dotted_path):
"""Each connectivity module file can be imported without errors."""
mod = importlib.import_module(dotted_path)
assert mod is not None
class TestConnectivityModulesAreBaseModule:
@pytest.mark.parametrize("class_name", CONNECTIVITY_CLASS_NAMES)
def test_connectivity_modules_are_base_module(self, class_name):
"""Each connectivity module class is a subclass of BaseModule."""
import modules.connectivity as conn_pkg
cls = getattr(conn_pkg, class_name)
assert issubclass(cls, BaseModule), f"{class_name} is not a BaseModule subclass"
class TestConnectivityModuleAttributes:
@pytest.mark.parametrize("class_name", CONNECTIVITY_CLASS_NAMES)
def test_connectivity_module_attributes(self, class_name):
"""Each connectivity module has required class attributes."""
import modules.connectivity as conn_pkg
cls = getattr(conn_pkg, class_name)
assert hasattr(cls, "name") and cls.name != "unnamed"
assert hasattr(cls, "module_type") and cls.module_type == "connectivity"
assert hasattr(cls, "priority") and isinstance(cls.priority, int)
assert hasattr(cls, "dependencies") and isinstance(cls.dependencies, list)
assert hasattr(cls, "requires_root")
for method_name in ("start", "stop", "status", "configure"):
assert callable(getattr(cls, method_name, None))
class TestConnectivityModuleInstantiate:
@pytest.mark.parametrize("class_name", CONNECTIVITY_CLASS_NAMES)
def test_connectivity_module_instantiate(self, class_name, mock_bus, mock_state, mock_config):
"""Each connectivity module can be instantiated with mock bus/state/config."""
import modules.connectivity as conn_pkg
cls = getattr(conn_pkg, class_name)
instance = cls(bus=mock_bus, state=mock_state, config=mock_config)
assert instance is not None
assert instance._running is False
# ---------------------------------------------------------------------------
# Cross-category: package __init__.py exports
# ---------------------------------------------------------------------------
class TestPackageExports:
def test_passive_init_exports_all(self):
"""modules.passive.__init__.py exports all 17 classes."""
import modules.passive as pkg
assert len(pkg.__all__) == 17
for name in pkg.__all__:
cls = getattr(pkg, name)
assert issubclass(cls, BaseModule)
def test_active_init_exports_all(self):
"""modules.active.__init__.py exports all 9 classes."""
import modules.active as pkg
assert len(pkg.__all__) == 9
for name in pkg.__all__:
cls = getattr(pkg, name)
assert issubclass(cls, BaseModule)
def test_intel_init_exports_all(self):
"""modules.intel.__init__.py exports all 9 classes."""
import modules.intel as pkg
assert len(pkg.__all__) == 9
for name in pkg.__all__:
cls = getattr(pkg, name)
assert issubclass(cls, BaseModule)
def test_connectivity_init_exports_all(self):
"""modules.connectivity.__init__.py exports all 7 classes."""
import modules.connectivity as pkg
assert len(pkg.__all__) == 7
for name in pkg.__all__:
cls = getattr(pkg, name)
assert issubclass(cls, BaseModule)
def test_stealth_init_exports_all(self):
"""modules.stealth.__init__.py exports all 12 classes."""
import modules.stealth as pkg
assert len(pkg.__all__) == 12
for name in pkg.__all__:
cls = getattr(pkg, name)
assert issubclass(cls, BaseModule)
# ---------------------------------------------------------------------------
# Discovery function test
# ---------------------------------------------------------------------------
class TestModuleDiscovery:
def test_discover_all_modules(self):
"""discover_all_modules() finds all 55 modules across 5 categories."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from bigbrother import discover_all_modules, discover_modules_in_category
all_mods = discover_all_modules()
# Stealth: 12, Passive: 17, Active: 9, Intel: 9, Connectivity: 7 = 54
assert len(all_mods) == 54, f"Expected 54 modules, found {len(all_mods)}"
# Verify per-category counts
assert len(discover_modules_in_category("stealth")) == 12
assert len(discover_modules_in_category("passive")) == 17
assert len(discover_modules_in_category("active")) == 9
assert len(discover_modules_in_category("intel")) == 9
assert len(discover_modules_in_category("connectivity")) == 7
def test_all_discovered_are_base_module(self):
"""Every discovered module is a BaseModule subclass."""
from bigbrother import discover_all_modules
for name, cls in discover_all_modules().items():
assert issubclass(cls, BaseModule), f"{name} ({cls}) is not a BaseModule subclass"