diff --git a/utils/bluetooth/dbus_scanner.py b/utils/bluetooth/dbus_scanner.py index 95793af..309ce98 100644 --- a/utils/bluetooth/dbus_scanner.py +++ b/utils/bluetooth/dbus_scanner.py @@ -305,8 +305,18 @@ class DBusScanner: if mfr_data: for mid, mdata in mfr_data.items(): manufacturer_id = int(mid) - if isinstance(mdata, dbus.Array): - manufacturer_data = bytes(mdata) + # Handle various DBus data types safely + try: + if isinstance(mdata, (bytes, bytearray)): + manufacturer_data = bytes(mdata) + elif isinstance(mdata, dbus.Array): + manufacturer_data = bytes(mdata) + elif isinstance(mdata, (list, tuple)): + manufacturer_data = bytes(mdata) + elif isinstance(mdata, str): + manufacturer_data = bytes.fromhex(mdata) + except (TypeError, ValueError) as e: + logger.debug(f"Could not convert manufacturer data: {e}") break # Extract service UUIDs @@ -319,8 +329,17 @@ class DBusScanner: service_data = {} if 'ServiceData' in props: for uuid, data in props['ServiceData'].items(): - if isinstance(data, dbus.Array): - service_data[str(uuid)] = bytes(data) + try: + if isinstance(data, (bytes, bytearray)): + service_data[str(uuid)] = bytes(data) + elif isinstance(data, dbus.Array): + service_data[str(uuid)] = bytes(data) + elif isinstance(data, (list, tuple)): + service_data[str(uuid)] = bytes(data) + elif isinstance(data, str): + service_data[str(uuid)] = bytes.fromhex(data) + except (TypeError, ValueError) as e: + logger.debug(f"Could not convert service data for {uuid}: {e}") # Extract Class of Device (Classic BT) class_of_device = None diff --git a/utils/tscm/device_identity.py b/utils/tscm/device_identity.py index 9b0fe66..97b64ee 100644 --- a/utils/tscm/device_identity.py +++ b/utils/tscm/device_identity.py @@ -1157,6 +1157,30 @@ def reset_identity_engine() -> None: _identity_engine = DeviceIdentityEngine() +def _convert_to_bytes(value) -> Optional[bytes]: + """Convert various data types to bytes safely.""" + if value is None: + return None + if isinstance(value, bytes): + return value + if isinstance(value, bytearray): + return bytes(value) + if isinstance(value, str): + # Assume hex string + try: + return bytes.fromhex(value) + except ValueError: + # Not a valid hex string, encode as UTF-8 + return value.encode('utf-8') + if isinstance(value, (list, tuple)): + # Array of integers (like dbus.Array) + try: + return bytes(value) + except (TypeError, ValueError): + return None + return None + + def ingest_ble_dict(data: dict) -> DeviceSession: """ Ingest BLE observation from dictionary. @@ -1173,9 +1197,9 @@ def ingest_ble_dict(data: dict) -> DeviceSession: adv_type=data.get('adv_type', 'unknown'), adv_flags=data.get('adv_flags'), manufacturer_id=data.get('manufacturer_id'), - manufacturer_data=bytes.fromhex(data['manufacturer_data']) if data.get('manufacturer_data') else None, + manufacturer_data=_convert_to_bytes(data.get('manufacturer_data')), service_uuids=data.get('service_uuids', []), - service_data=bytes.fromhex(data['service_data']) if data.get('service_data') else None, + service_data=_convert_to_bytes(data.get('service_data')), local_name=data.get('local_name', data.get('name')), appearance=data.get('appearance'), packet_length=data.get('packet_length'), diff --git a/utils/wifi/scanner.py b/utils/wifi/scanner.py index dc5de47..a84339f 100644 --- a/utils/wifi/scanner.py +++ b/utils/wifi/scanner.py @@ -265,6 +265,36 @@ class UnifiedWiFiScanner: pass return False + def _is_monitor_mode_interface(self, interface: str) -> bool: + """ + Check if interface is currently in monitor mode. + + Returns True if: + - Interface name ends with 'mon' (common convention) + - iw reports type as 'monitor' + """ + # Quick check by name convention + if interface.endswith('mon'): + return True + + # Check actual mode via iw + if shutil.which('iw'): + try: + result = subprocess.run( + ['iw', interface, 'info'], + capture_output=True, + text=True, + timeout=TOOL_TIMEOUT_DETECT, + ) + if result.returncode == 0: + # Look for "type monitor" in output + if re.search(r'type\s+monitor', result.stdout, re.IGNORECASE): + return True + except Exception: + pass + + return False + # ========================================================================= # Quick Scan # ========================================================================= @@ -299,6 +329,17 @@ class UnifiedWiFiScanner: result.interface = iface + # Check if interface is in monitor mode (can't use quick scan tools on monitor interfaces) + if self._is_monitor_mode_interface(iface): + result.error = ( + f"Interface '{iface}' appears to be in monitor mode. " + "Quick scan requires a managed mode interface. " + "Either use a different interface, disable monitor mode, or use deep_scan() with airodump-ng." + ) + result.is_complete = True + result.warnings.append("Monitor mode interfaces don't support standard WiFi scanning") + return result + # Select and run parser based on platform/tools # Try multiple tools with fallback on Linux observations = []