mirror of
https://github.com/smittix/intercept.git
synced 2026-07-27 18:18:10 -07:00
Improve WiFi layout and fix capture functionality
Layout changes: - Move device list to right column beside visualizations - Add dedicated WiFi device list panel with header and count - Hide waterfall and generic output for WiFi mode - Add responsive styles for smaller screens Capture fixes: - Fix handshake capture: add interface param, stop existing scan first - Fix PMKID capture: add interface param, stop existing scan first - Add proper error handling with try/catch for both capture functions - Use monitorInterface variable when available Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+84
-5
@@ -2545,22 +2545,101 @@ header p {
|
|||||||
background: var(--text-dim);
|
background: var(--text-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* WiFi Layout Container - side by side layout */
|
||||||
|
.wifi-layout-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
margin: 0 15px 10px 15px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
height: calc(100vh - 200px); /* Take most of the available height */
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
/* WiFi Visualizations */
|
/* WiFi Visualizations */
|
||||||
.wifi-visuals {
|
.wifi-visuals {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 15px;
|
flex: 1;
|
||||||
background: var(--bg-secondary);
|
overflow-y: auto;
|
||||||
margin: 0 15px 10px 15px;
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WiFi Device List (right column) */
|
||||||
|
.wifi-device-list {
|
||||||
|
width: 350px;
|
||||||
|
min-width: 300px;
|
||||||
|
background: var(--bg-primary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
max-height: 50vh; /* Limit height to leave room for device cards */
|
border-radius: 4px;
|
||||||
overflow-y: auto; /* Allow scrolling within visualizations */
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-device-list-header {
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-device-list-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--accent-cyan);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-device-list-header .device-count {
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-device-list-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WiFi network cards in device list - more compact */
|
||||||
|
.wifi-network-card {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-network-card .header {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-network-card .sensor-data {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-network-card .preset-btn {
|
||||||
|
font-size: 9px !important;
|
||||||
|
padding: 3px 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
|
.wifi-layout-container {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
max-height: calc(100vh - 200px);
|
||||||
|
}
|
||||||
|
|
||||||
.wifi-visuals {
|
.wifi-visuals {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
max-height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wifi-device-list {
|
||||||
|
width: 100%;
|
||||||
|
min-width: auto;
|
||||||
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+139
-62
@@ -1199,15 +1199,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- WiFi Visualizations (shown only in WiFi mode) -->
|
<!-- WiFi Layout Container (visualizations left, device cards right) -->
|
||||||
<div class="wifi-visuals" id="wifiVisuals" style="display: none;">
|
<div class="wifi-layout-container" id="wifiLayoutContainer" style="display: none;">
|
||||||
<!-- Selected WiFi Device Info - at top for visibility -->
|
<!-- Left: WiFi Visualizations -->
|
||||||
<div class="wifi-visual-panel" style="grid-column: span 2;">
|
<div class="wifi-visuals" id="wifiVisuals">
|
||||||
<h5>📋 Selected Device</h5>
|
<!-- Selected WiFi Device Info - at top for visibility -->
|
||||||
<div id="wifiSelectedDevice" style="font-size: 11px; min-height: 100px;">
|
<div class="wifi-visual-panel" style="grid-column: span 2;">
|
||||||
<div style="color: var(--text-dim); padding: 20px; text-align: center;">Click a network or client to view details</div>
|
<h5>📋 Selected Device</h5>
|
||||||
|
<div id="wifiSelectedDevice" style="font-size: 11px; min-height: 100px;">
|
||||||
|
<div style="color: var(--text-dim); padding: 20px; text-align: center;">Click a network or client to view details</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="wifi-visual-panel">
|
<div class="wifi-visual-panel">
|
||||||
<h5>Network Radar</h5>
|
<h5>Network Radar</h5>
|
||||||
<div class="radar-container">
|
<div class="radar-container">
|
||||||
@@ -1313,6 +1315,19 @@
|
|||||||
<div style="color: var(--text-dim);">Waiting for client probe requests...</div>
|
<div style="color: var(--text-dim);">Waiting for client probe requests...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Right: WiFi Device Cards -->
|
||||||
|
<div class="wifi-device-list" id="wifiDeviceList">
|
||||||
|
<div class="wifi-device-list-header">
|
||||||
|
<h5>📡 Discovered Networks</h5>
|
||||||
|
<span class="device-count">(<span id="wifiDeviceListCount">0</span>)</span>
|
||||||
|
</div>
|
||||||
|
<div class="wifi-device-list-content" id="wifiDeviceListContent">
|
||||||
|
<div style="color: var(--text-dim); text-align: center; padding: 30px;">
|
||||||
|
Start scanning to discover WiFi networks
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bluetooth Visualizations -->
|
<!-- Bluetooth Visualizations -->
|
||||||
@@ -2345,7 +2360,7 @@
|
|||||||
'listening': 'LISTENING POST'
|
'listening': 'LISTENING POST'
|
||||||
};
|
};
|
||||||
document.getElementById('activeModeIndicator').innerHTML = '<span class="pulse-dot"></span>' + modeNames[mode];
|
document.getElementById('activeModeIndicator').innerHTML = '<span class="pulse-dot"></span>' + modeNames[mode];
|
||||||
document.getElementById('wifiVisuals').style.display = mode === 'wifi' ? 'grid' : 'none';
|
document.getElementById('wifiLayoutContainer').style.display = mode === 'wifi' ? 'flex' : 'none';
|
||||||
document.getElementById('btVisuals').style.display = mode === 'bluetooth' ? 'grid' : 'none';
|
document.getElementById('btVisuals').style.display = mode === 'bluetooth' ? 'grid' : 'none';
|
||||||
// Respect the "Show Radar Display" checkbox for aircraft mode
|
// Respect the "Show Radar Display" checkbox for aircraft mode
|
||||||
const showRadar = document.getElementById('adsbEnableMap').checked;
|
const showRadar = document.getElementById('adsbEnableMap').checked;
|
||||||
@@ -2391,7 +2406,7 @@
|
|||||||
|
|
||||||
// Hide waterfall and output console for modes with their own visualizations
|
// Hide waterfall and output console for modes with their own visualizations
|
||||||
document.querySelector('.waterfall-container').style.display = (mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi') ? 'none' : 'block';
|
document.querySelector('.waterfall-container').style.display = (mode === 'satellite' || mode === 'listening' || mode === 'aircraft' || mode === 'wifi') ? 'none' : 'block';
|
||||||
document.getElementById('output').style.display = (mode === 'satellite' || mode === 'aircraft') ? 'none' : 'block';
|
document.getElementById('output').style.display = (mode === 'satellite' || mode === 'aircraft' || mode === 'wifi') ? 'none' : 'block';
|
||||||
document.querySelector('.status-bar').style.display = (mode === 'satellite') ? 'none' : 'flex';
|
document.querySelector('.status-bar').style.display = (mode === 'satellite') ? 'none' : 'flex';
|
||||||
|
|
||||||
// Load interfaces and initialize visualizations when switching modes
|
// Load interfaces and initialize visualizations when switching modes
|
||||||
@@ -5032,11 +5047,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add WiFi network card to output
|
// Add WiFi network card to device list
|
||||||
function addWifiNetworkCard(net, isNew) {
|
function addWifiNetworkCard(net, isNew) {
|
||||||
const output = document.getElementById('output');
|
// Use the WiFi device list panel instead of the generic output
|
||||||
const placeholder = output.querySelector('.placeholder');
|
const deviceList = document.getElementById('wifiDeviceListContent');
|
||||||
if (placeholder) placeholder.remove();
|
if (!deviceList) return;
|
||||||
|
|
||||||
|
// Remove placeholder if present
|
||||||
|
const placeholder = deviceList.querySelector('div[style*="text-align: center"]');
|
||||||
|
if (placeholder && placeholder.textContent.includes('Start scanning')) {
|
||||||
|
placeholder.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// Check if card already exists
|
// Check if card already exists
|
||||||
let card = document.getElementById('wifi_' + net.bssid.replace(/:/g, ''));
|
let card = document.getElementById('wifi_' + net.bssid.replace(/:/g, ''));
|
||||||
@@ -5044,13 +5065,17 @@
|
|||||||
if (!card) {
|
if (!card) {
|
||||||
card = document.createElement('div');
|
card = document.createElement('div');
|
||||||
card.id = 'wifi_' + net.bssid.replace(/:/g, '');
|
card.id = 'wifi_' + net.bssid.replace(/:/g, '');
|
||||||
card.className = 'sensor-card';
|
card.className = 'sensor-card wifi-network-card';
|
||||||
card.style.borderLeftColor = net.privacy.includes('WPA') ? 'var(--accent-orange)' :
|
card.style.borderLeftColor = net.privacy.includes('WPA') ? 'var(--accent-orange)' :
|
||||||
net.privacy.includes('WEP') ? 'var(--accent-red)' :
|
net.privacy.includes('WEP') ? 'var(--accent-red)' :
|
||||||
'var(--accent-green)';
|
'var(--accent-green)';
|
||||||
card.style.cursor = 'pointer';
|
card.style.cursor = 'pointer';
|
||||||
card.onclick = () => selectWifiDevice(net.bssid, 'network');
|
card.onclick = () => selectWifiDevice(net.bssid, 'network');
|
||||||
output.insertBefore(card, output.firstChild);
|
deviceList.insertBefore(card, deviceList.firstChild);
|
||||||
|
|
||||||
|
// Update device count
|
||||||
|
const countEl = document.getElementById('wifiDeviceListCount');
|
||||||
|
if (countEl) countEl.textContent = Object.keys(wifiNetworks).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle signal strength - airodump returns -1 when not measured
|
// Handle signal strength - airodump returns -1 when not measured
|
||||||
@@ -5105,47 +5130,75 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start handshake capture
|
// Start handshake capture
|
||||||
function captureHandshake(bssid, channel) {
|
async function captureHandshake(bssid, channel) {
|
||||||
if (!confirm('Start handshake capture for ' + bssid + '? This will stop the current scan.')) {
|
if (!confirm('Start handshake capture for ' + bssid + '? This will stop the current scan.')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch('/wifi/handshake/capture', {
|
const iface = monitorInterface || document.getElementById('wifiInterfaceSelect').value;
|
||||||
method: 'POST',
|
if (!iface) {
|
||||||
headers: {'Content-Type': 'application/json'},
|
showError('No monitor interface available. Enable monitor mode first.');
|
||||||
body: JSON.stringify({bssid: bssid, channel: channel})
|
return;
|
||||||
}).then(r => r.json())
|
}
|
||||||
.then(data => {
|
|
||||||
if (data.status === 'started') {
|
|
||||||
showInfo('🎯 Capturing handshakes for ' + bssid);
|
|
||||||
setWifiRunning(true);
|
|
||||||
|
|
||||||
// Update handshake indicator to show active capture
|
// Stop any existing scan first
|
||||||
const hsSpan = document.getElementById('handshakeCount');
|
if (isWifiRunning) {
|
||||||
hsSpan.style.animation = 'pulse 1s infinite';
|
showInfo('Stopping current scan...');
|
||||||
hsSpan.title = 'Capturing: ' + bssid;
|
try {
|
||||||
|
await fetch('/wifi/scan/stop', {method: 'POST'});
|
||||||
|
if (wifiEventSource) {
|
||||||
|
wifiEventSource.close();
|
||||||
|
wifiEventSource = null;
|
||||||
|
}
|
||||||
|
setWifiRunning(false);
|
||||||
|
// Brief delay to ensure process stops
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error stopping scan:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show capture status panel
|
try {
|
||||||
const panel = document.getElementById('captureStatusPanel');
|
const response = await fetch('/wifi/handshake/capture', {
|
||||||
panel.style.display = 'block';
|
method: 'POST',
|
||||||
document.getElementById('captureTargetBssid').textContent = bssid;
|
headers: {'Content-Type': 'application/json'},
|
||||||
document.getElementById('captureTargetChannel').textContent = channel;
|
body: JSON.stringify({bssid: bssid, channel: channel, interface: iface})
|
||||||
document.getElementById('captureFilePath').textContent = data.capture_file;
|
});
|
||||||
document.getElementById('captureStatus').textContent = 'Waiting for handshake...';
|
const data = await response.json();
|
||||||
document.getElementById('captureStatus').style.color = 'var(--accent-orange)';
|
|
||||||
|
|
||||||
// Store active capture info and start polling
|
if (data.status === 'started') {
|
||||||
activeCapture = {
|
showInfo('🎯 Capturing handshakes for ' + bssid);
|
||||||
bssid: bssid,
|
setWifiRunning(true);
|
||||||
channel: channel,
|
|
||||||
file: data.capture_file,
|
// Update handshake indicator to show active capture
|
||||||
startTime: Date.now(),
|
const hsSpan = document.getElementById('handshakeCount');
|
||||||
pollInterval: setInterval(checkCaptureStatus, 5000) // Check every 5 seconds
|
hsSpan.style.animation = 'pulse 1s infinite';
|
||||||
};
|
hsSpan.title = 'Capturing: ' + bssid;
|
||||||
} else {
|
|
||||||
alert('Error: ' + data.message);
|
// Show capture status panel
|
||||||
}
|
const panel = document.getElementById('captureStatusPanel');
|
||||||
});
|
panel.style.display = 'block';
|
||||||
|
document.getElementById('captureTargetBssid').textContent = bssid;
|
||||||
|
document.getElementById('captureTargetChannel').textContent = channel;
|
||||||
|
document.getElementById('captureFilePath').textContent = data.capture_file;
|
||||||
|
document.getElementById('captureStatus').textContent = 'Waiting for handshake...';
|
||||||
|
document.getElementById('captureStatus').style.color = 'var(--accent-orange)';
|
||||||
|
|
||||||
|
// Store active capture info and start polling
|
||||||
|
activeCapture = {
|
||||||
|
bssid: bssid,
|
||||||
|
channel: channel,
|
||||||
|
file: data.capture_file,
|
||||||
|
startTime: Date.now(),
|
||||||
|
pollInterval: setInterval(checkCaptureStatus, 5000) // Check every 5 seconds
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
showError('Handshake capture failed: ' + (data.message || 'Unknown error'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showError('Handshake capture error: ' + err.message);
|
||||||
|
console.error('Handshake capture error:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check handshake capture status
|
// Check handshake capture status
|
||||||
@@ -5222,20 +5275,41 @@
|
|||||||
// PMKID Capture
|
// PMKID Capture
|
||||||
let activePmkid = null;
|
let activePmkid = null;
|
||||||
|
|
||||||
function capturePmkid(bssid, channel) {
|
async function capturePmkid(bssid, channel) {
|
||||||
if (!confirm('Start PMKID capture for ' + bssid + '?\\n\\nThis uses hcxdumptool to capture PMKID without needing clients.\\n\\n⚠ Only use on networks you own or have authorization to test!')) {
|
if (!confirm('Start PMKID capture for ' + bssid + '?\n\nThis uses hcxdumptool to capture PMKID without needing clients.\n\n⚠ Only use on networks you own or have authorization to test!')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iface = document.getElementById('wifiInterfaceSelect').value;
|
const iface = monitorInterface || document.getElementById('wifiInterfaceSelect').value;
|
||||||
|
if (!iface) {
|
||||||
|
showError('No monitor interface available. Enable monitor mode first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any existing scan first
|
||||||
|
if (isWifiRunning) {
|
||||||
|
showInfo('Stopping current scan...');
|
||||||
|
try {
|
||||||
|
await fetch('/wifi/scan/stop', {method: 'POST'});
|
||||||
|
if (wifiEventSource) {
|
||||||
|
wifiEventSource.close();
|
||||||
|
wifiEventSource = null;
|
||||||
|
}
|
||||||
|
setWifiRunning(false);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error stopping scan:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/wifi/pmkid/capture', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ interface: iface, bssid: bssid, channel: channel })
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
fetch('/wifi/pmkid/capture', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ interface: iface, bssid: bssid, channel: channel })
|
|
||||||
})
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.status === 'started') {
|
if (data.status === 'started') {
|
||||||
activePmkid = { bssid: bssid, file: data.file, startTime: Date.now() };
|
activePmkid = { bssid: bssid, file: data.file, startTime: Date.now() };
|
||||||
document.getElementById('pmkidPanel').style.display = 'block';
|
document.getElementById('pmkidPanel').style.display = 'block';
|
||||||
@@ -5247,9 +5321,12 @@
|
|||||||
// Poll for PMKID
|
// Poll for PMKID
|
||||||
activePmkid.pollInterval = setInterval(checkPmkidStatus, 3000);
|
activePmkid.pollInterval = setInterval(checkPmkidStatus, 3000);
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to start PMKID capture: ' + data.message);
|
showError('PMKID capture failed: ' + (data.message || 'Unknown error'));
|
||||||
}
|
}
|
||||||
});
|
} catch (err) {
|
||||||
|
showError('PMKID capture error: ' + err.message);
|
||||||
|
console.error('PMKID capture error:', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPmkidStatus() {
|
function checkPmkidStatus() {
|
||||||
|
|||||||
Reference in New Issue
Block a user