From 8c5bb32ec6b1a3ab09641351d982b15759e88c21 Mon Sep 17 00:00:00 2001 From: Jon Ander Oribe Date: Sat, 10 Jan 2026 07:35:28 +0100 Subject: [PATCH] Testing for Wifi --- tests/test_wifi.py | 221 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 tests/test_wifi.py diff --git a/tests/test_wifi.py b/tests/test_wifi.py new file mode 100644 index 0000000..15bd830 --- /dev/null +++ b/tests/test_wifi.py @@ -0,0 +1,221 @@ +import pytest +import sys +import os +from unittest.mock import MagicMock, patch, mock_open +from flask import Flask +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from routes.wifi import wifi_bp, parse_airodump_csv + +@pytest.fixture +def mock_app_module(mocker): + """Mock the app_module imported inside routes.wifi.""" + mock = mocker.patch("routes.wifi.app_module") + mock.wifi_lock = MagicMock() + mock.wifi_process = None + mock.wifi_monitor_interface = None + mock.wifi_queue = MagicMock() + mock.wifi_networks = {} + mock_app_module.wifi_clients = {} + return mock + +@pytest.fixture +def app(): + app = Flask(__name__) + app.register_blueprint(wifi_bp) + return app + +@pytest.fixture +def client(app): + return app.test_client() + +def test_parse_airodump_csv(mocker): + """Test parsing logic for airodump CSV format.""" + csv_content = ( + "BSSID, First time seen, Last time seen, channel, Speed, Privacy, Cipher, Authentication, Power, # beacons, # IV, LAN IP, ID-length, ESSID, Key\n" + "AA:BB:CC:DD:EE:FF, 2023-01-01, 2023-01-01, 6, 54, WPA2, CCMP, PSK, -50, 10, 5, 0.0.0.0, 7, MyWiFi, \n" + "\n" + "Station MAC, First time seen, Last time seen, Power, # packets, BSSID, Probes\n" + "11:22:33:44:55:66, 2023-01-01, 2023-01-01, -60, 20, AA:BB:CC:DD:EE:FF, MyWiFi\n" + ) + + with patch("builtins.open", mock_open(read_data=csv_content)): + mocker.patch("routes.wifi.get_manufacturer", return_value="Apple") + networks, clients = parse_airodump_csv("dummy.csv") + + assert "AA:BB:CC:DD:EE:FF" in networks + assert networks["AA:BB:CC:DD:EE:FF"]["essid"] == "MyWiFi" + assert "11:22:33:44:55:66" in clients + assert clients["11:22:33:44:55:66"]["vendor"] == "Apple" + +### --- ROUTE TESTS --- ### + +def test_get_interfaces(client, mocker): + """Test the /interfaces endpoint.""" + mocker.patch("routes.wifi.detect_wifi_interfaces", return_value=[{'name': 'wlan0', 'type': 'managed'}]) + mocker.patch("routes.wifi.check_tool", return_value=True) + + response = client.get('/wifi/interfaces') + data = response.get_json() + + assert response.status_code == 200 + assert len(data['interfaces']) == 1 + assert data['tools']['airmon'] is True + +def test_toggle_monitor_start_success(client, mocker): + """Test enabling monitor mode via airmon-ng.""" + mocker.patch("routes.wifi.validate_network_interface", return_value="wlan0") + mocker.patch("routes.wifi.check_tool", return_value=True) + mock_run = mocker.patch("routes.wifi.subprocess.run") + mock_run.return_value = MagicMock(stdout="enabled on [phy0]wlan0mon", stderr="", returncode=0) + + with patch("os.path.exists", return_value=True): + response = client.post('/wifi/monitor', json={'action': 'start', 'interface': 'wlan0'}) + + assert response.status_code == 200 + assert response.get_json()['status'] == 'success' + assert response.get_json()['monitor_interface'] == 'wlan0mon' + +def test_start_scan_already_running(client, mock_app_module): + """Test that we can't start a scan if one is already active.""" + mock_app_module.wifi_process = MagicMock() + + response = client.post('/wifi/scan/start', json={'interface': 'wlan0mon'}) + data = response.get_json() + assert data['status'] == 'error' + assert 'already running' in data['message'] + +def test_start_scan_execution(client, mock_app_module, mocker): + """Test the full command construction of airodump-ng.""" + mock_app_module.wifi_process = None + mocker.patch("os.path.exists", return_value=True) + mocker.patch("routes.wifi.get_tool_path", return_value="/usr/bin/airodump-ng") + + mock_popen = mocker.patch("routes.wifi.subprocess.Popen") + mock_proc = MagicMock() + mock_proc.poll.return_value = None + mock_popen.return_value = mock_proc + + payload = {'interface': 'wlan0mon', 'channel': 6, 'band': 'g'} + response = client.post('/wifi/scan/start', json=payload) + + assert response.status_code == 200 + assert response.get_json()['status'] == 'started' + + args, _ = mock_popen.call_args + cmd = args[0] + assert "-c" in cmd and "6" in cmd + assert "wlan0mon" in cmd + +def test_stop_scan(client, mock_app_module): + """Test terminating the scanning process.""" + mock_proc = MagicMock() + mock_app_module.wifi_process = mock_proc + + response = client.post('/wifi/scan/stop') + + assert response.status_code == 200 + assert response.get_json()['status'] == 'stopped' + mock_proc.terminate.assert_called_once() + +def test_send_deauth_success(client, mock_app_module, mocker): + """Verify deauth command construction and execution.""" + mocker.patch("routes.wifi.check_tool", return_value=True) + mocker.patch("routes.wifi.get_tool_path", return_value="/usr/bin/aireplay-ng") + mock_run = mocker.patch("routes.wifi.subprocess.run") + mock_run.return_value = MagicMock(returncode=0) + + payload = { + 'bssid': 'AA:BB:CC:DD:EE:FF', + 'count': 10, + 'interface': 'wlan0mon' + } + response = client.post('/wifi/deauth', json=payload) + + assert response.status_code == 200 + args, _ = mock_run.call_args + cmd = args[0] + assert "--deauth" in cmd + assert "10" in cmd + assert "AA:BB:CC:DD:EE:FF" in cmd + +### --- HANDSHAKE TESTS --- ### + +def test_capture_handshake_start(client, mock_app_module, mocker): + """Test starting airodump-ng for handshake capture.""" + mock_app_module.wifi_process = None + mocker.patch("routes.wifi.get_tool_path", return_value="/usr/bin/airodump-ng") + mock_popen = mocker.patch("routes.wifi.subprocess.Popen") + + payload = {'bssid': 'AA:BB:CC:DD:EE:FF', 'channel': '6', 'interface': 'wlan0mon'} + response = client.post('/wifi/handshake/capture', json=payload) + + assert response.status_code == 200 + assert 'capture_file' in response.get_json() + assert mock_popen.called + +def test_check_handshake_status_found(client, mocker): + """Verify detection of 'KEY FOUND' in aircrack output.""" + mocker.patch("os.path.exists", return_value=True) + mocker.patch("os.path.getsize", return_value=1024) + mocker.patch("routes.wifi.get_tool_path", return_value="aircrack-ng") + + mock_run = mocker.patch("routes.wifi.subprocess.run") + mock_run.return_value = MagicMock(stdout="WPA (1 handshake)", stderr="", returncode=0) + + payload = {'file': '/tmp/intercept_handshake_test.cap', 'bssid': 'AA:BB:CC:DD:EE:FF'} + response = client.post('/wifi/handshake/status', json=payload) + + assert response.get_json()['handshake_found'] is True + +### --- PMKID TESTS --- ### + +def test_capture_pmkid_path_traversal_prevention(client): + """Ensure the status check rejects invalid paths.""" + payload = {'file': '/etc/passwd'} # Malicious path + response = client.post('/wifi/pmkid/status', json=payload) + + assert response.status_code == 200 + assert response.get_json()['status'] == 'error' + assert 'Invalid capture file path' in response.get_json()['message'] + +### --- CRACKING TESTS --- ### + +def test_crack_handshake_success(client, mocker): + """Test successful password extraction using Regex.""" + mocker.patch("os.path.exists", return_value=True) + mocker.patch("routes.wifi.get_tool_path", return_value="aircrack-ng") + + mock_run = mocker.patch("routes.wifi.subprocess.run") + # Simulate the actual aircrack-ng success output + mock_run.return_value = MagicMock( + stdout="KEY FOUND! [ secret123 ]", + stderr="", + returncode=0 + ) + + payload = { + 'capture_file': '/tmp/intercept_handshake_test.cap', + 'wordlist': '/home/user/passwords.txt', + 'bssid': 'AA:BB:CC:DD:EE:FF' + } + response = client.post('/wifi/handshake/crack', json=payload) + + data = response.get_json() + assert data['status'] == 'success' + assert data['password'] == 'secret123' + +### --- DATA FETCHING TESTS --- ### + +def test_get_wifi_networks(client, mock_app_module): + """Test that the networks endpoint correctly formats internal data.""" + mock_app_module.wifi_networks = { + 'AA:BB:CC:DD:EE:FF': {'essid': 'Home-WiFi', 'bssid': 'AA:BB:CC:DD:EE:FF'} + } + mock_app_module.wifi_handshakes = ['AA:BB:CC:DD:EE:FF'] + + response = client.get('/wifi/networks') + data = response.get_json() + + assert len(data['networks']) == 1 + assert data['networks'][0]['essid'] == 'Home-WiFi' + assert 'AA:BB:CC:DD:EE:FF' in data['handshakes'] \ No newline at end of file