Files
intercept/tests/test_wifi.py
T
James Smith 96172ca593 style: apply ruff-format to entire codebase
First-time run of ruff-format via pre-commit hook normalises quote
style, trailing commas, and whitespace across 188 Python files.
No logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 14:48:11 +01:00

234 lines
8.2 KiB
Python

import os
import sys
from unittest.mock import MagicMock, mock_open, patch
import pytest
from flask import Flask
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from routes.wifi import parse_airodump_csv, wifi_bp
@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 == 400
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"]