Fix TSCM WiFi detection, SDR capabilities, layout, and correlation/cluster emission

- Use networksetup instead of deprecated airport utility for macOS WiFi detection
- Fix SDRDevice attribute access (use getattr instead of dict .get())
- Move Detected Threats panel next to RF Signals in 2-column grid
- Always run correlation/identity analysis at sweep end, even if stopped by user

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-05 16:41:10 +00:00
parent 5d4b19aef2
commit eabb6b2951
4 changed files with 22 additions and 29 deletions

View File

@@ -368,25 +368,21 @@ def _check_available_devices(wifi: bool, bt: bool, rf: bool) -> dict:
# Check WiFi
if wifi:
if platform.system() == 'Darwin':
# macOS: Check for airport utility
airport_path = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport'
if os.path.exists(airport_path):
try:
result = subprocess.run(
[airport_path, '-I'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
available['wifi'] = True
available['wifi_reason'] = 'macOS WiFi available'
else:
available['wifi_reason'] = 'WiFi interface not active'
except (subprocess.TimeoutExpired, subprocess.SubprocessError):
available['wifi_reason'] = 'Cannot access WiFi interface'
else:
available['wifi_reason'] = 'macOS airport utility not found'
# macOS: Use networksetup to detect WiFi interfaces (same as /tscm/devices endpoint)
try:
result = subprocess.run(
['networksetup', '-listallhardwareports'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0 and ('Wi-Fi' in result.stdout or 'AirPort' in result.stdout):
available['wifi'] = True
available['wifi_reason'] = 'macOS WiFi available'
else:
available['wifi_reason'] = 'No WiFi hardware port found'
except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError):
available['wifi_reason'] = 'Cannot detect WiFi interfaces'
else:
# Linux: Check for wireless tools
if shutil.which('airodump-ng') or shutil.which('iwlist') or shutil.which('iw'):
@@ -1981,8 +1977,8 @@ def _run_sweep(
time.sleep(2) # Update every 2 seconds
# Complete sweep
if _sweep_running and _current_sweep_id:
# Complete sweep (run even if stopped by user so correlations/clusters are computed)
if _current_sweep_id:
# Run cross-protocol correlation analysis
correlations = correlation.correlate_devices()
findings = correlation.get_all_findings()

View File

@@ -69,11 +69,6 @@
min-height: 200px;
height: 200px;
}
/* Full-width panels (like Detected Threats) get more height */
.tscm-panel[style*="grid-column: span 2"] {
min-height: 150px;
height: 150px;
}
.tscm-panel-header {
padding: 10px 12px;
background: rgba(0,0,0,0.3);

View File

@@ -1557,7 +1557,7 @@
</div>
<!-- Threats Panel -->
<div class="tscm-panel" id="tscmThreatPanel" style="grid-column: span 2;">
<div class="tscm-panel" id="tscmThreatPanel">
<div class="tscm-panel-header">
Detected Threats
<span class="badge" id="tscmThreatCount">0</span>

View File

@@ -372,8 +372,10 @@ def _detect_rf_capabilities(caps: SweepCapabilities, sdr_device: Any) -> None:
if devices:
device = devices[0] # Use first device
rf_cap.available = True
rf_cap.device_type = device.get('type', 'unknown')
rf_cap.driver = device.get('driver', '')
rf_cap.device_type = getattr(device, 'sdr_type', 'unknown')
if hasattr(rf_cap.device_type, 'value'):
rf_cap.device_type = rf_cap.device_type.value
rf_cap.driver = getattr(device, 'driver', '')
# Set frequency ranges based on device type
if 'rtl' in rf_cap.device_type.lower():