Fix 5 bugs: TypeError in Attack class, native module crash, unsafe in…#456
Conversation
…dex access, placeholder dep check, command splitting - model/attack.py: Replace class-level `min(60, None)` with safe static method that handles uninitialized Configuration.wpa_attack_timeout - native/__init__.py + all 6 native submodules: Catch BaseException (not just ImportError) on scapy imports to handle pyo3 PanicException gracefully - native/__init__.py: Wrap top-level imports in try/except for graceful degradation when scapy/cryptography/cffi unavailable - attack/pmkid.py, util/scanner.py: Broaden exception handling for native module imports to catch BaseException - model/target.py: Guard against empty string in encryption.split() fallback - attack/eviltwin.py: Replace placeholder dependency check with actual hostapd/dnsmasq availability verification - util/process.py: Use shlex.split() instead of str.split() for proper handling of quoted arguments in commands - wifite.py: Log session cleanup errors instead of silently swallowing them Test results: 43 failures → 0 failures (575 passed, 5 skipped) https://claude.ai/code/session_012qvM97c3D9M98CrMjmzuQd
There was a problem hiding this comment.
Pull request overview
This PR focuses on improving runtime robustness across the attack pipeline by preventing startup/import crashes, hardening command execution argument parsing, and fixing a handful of edge-case bugs in target parsing and dependency checks.
Changes:
- Fixes a class-load
TypeErrorinAttackby deferring timeout computation until runtime. - Makes native/scapy-backed modules degrade gracefully when imports fail/crash, and broadens native-import exception handling in callers.
- Improves operational correctness by using
shlex.split()for command parsing, adding real EvilTwin dependency checks, and surfacing session cleanup errors in verbose mode.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| wifite/wifite.py | Logs session cleanup errors (in verbose) instead of silently ignoring them. |
| wifite/util/scanner.py | Treats native scanner import failures as “native unavailable” to allow fallback behavior. |
| wifite/util/process.py | Switches from naive string splitting to shlex.split() for subprocess argument parsing. |
| wifite/native/init.py | Wraps native module imports to allow partial availability and adds broader availability checks. |
| wifite/native/wps.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/native/scanner.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/native/pmkid.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/native/handshake.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/native/deauth.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/native/beacon.py | Broadens scapy import exception handling and toggles SCAPY_AVAILABLE. |
| wifite/model/target.py | Hardens encryption parsing to avoid unsafe index access in fallback logic. |
| wifite/model/attack.py | Fixes Attack.target_wait initialization by introducing a safe runtime getter. |
| wifite/attack/pmkid.py | Broadens native PMKID import exception handling for graceful fallback. |
| wifite/attack/eviltwin.py | Replaces placeholder dependency check with actual hostapd/dnsmasq availability verification. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if Configuration.verbose > 1: | ||
| Color.pe(f'\n {{C}}[?]{{W}} Executing: {{B}}{command}{{W}}') | ||
| command = command.split() | ||
| command = shlex.split(command) |
There was a problem hiding this comment.
shlex.split() can raise ValueError for malformed/unbalanced quoting. Right now that would bubble up and potentially crash the caller in cases that previously worked (even if parsed imperfectly). Consider catching ValueError here and either (a) raising a clearer error that includes the original command string, or (b) falling back to a simpler split strategy.
| command = shlex.split(command) | |
| orig_command = command | |
| try: | |
| command = shlex.split(command) | |
| except ValueError as e: | |
| # Fall back to a naive split if shlex.split fails (e.g., malformed quoting) | |
| log_warning( | |
| f"Failed to parse command with shlex.split(): {e}. " | |
| f"Falling back to naive split for command: {orig_command!r}" | |
| ) | |
| command = orig_command.split() |
| try: | ||
| from .mac import NativeMac | ||
| status['mac'] = True | ||
| except ImportError: | ||
| except BaseException: | ||
| status['mac'] = False | ||
|
|
||
| try: | ||
| from .deauth import SCAPY_AVAILABLE | ||
| status['deauth'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['deauth'] = False | ||
|
|
||
| try: | ||
| from .handshake import SCAPY_AVAILABLE | ||
| status['handshake'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['handshake'] = False | ||
|
|
||
| try: | ||
| from .wps import SCAPY_AVAILABLE | ||
| status['wps'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['wps'] = False | ||
|
|
||
| try: | ||
| from .interface import NativeInterface | ||
| status['interface'] = True | ||
| except ImportError: | ||
| except BaseException: | ||
| status['interface'] = False | ||
|
|
||
| try: | ||
| from .pmkid import SCAPY_AVAILABLE | ||
| status['pmkid'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['pmkid'] = False | ||
|
|
||
| try: | ||
| from .scanner import SCAPY_AVAILABLE | ||
| status['scanner'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['scanner'] = False | ||
|
|
||
| try: | ||
| from .beacon import SCAPY_AVAILABLE | ||
| status['beacon'] = SCAPY_AVAILABLE | ||
| except ImportError: | ||
| except BaseException: | ||
| status['beacon'] = False |
There was a problem hiding this comment.
check_native_availability() catches BaseException, which includes KeyboardInterrupt/SystemExit and also masks unexpected coding/runtime errors in the native modules. Consider re-raising KeyboardInterrupt/SystemExit and recording/logging the caught exception (at least under verbose/debug) so users can distinguish "not installed" from "native module crashed during import".
| except BaseException: | ||
| NATIVE_SCANNER_AVAILABLE = False |
There was a problem hiding this comment.
Catching BaseException here will also intercept KeyboardInterrupt/SystemExit, which can prevent expected termination during startup. If the goal is to handle rare import-time crashes, consider re-raising interrupt/exit exceptions and (optionally) logging the underlying import error in verbose mode to aid debugging when native scanning is disabled.
| except BaseException: | |
| NATIVE_SCANNER_AVAILABLE = False | |
| except BaseException as e: | |
| # Do not swallow interrupts/exits during startup | |
| if isinstance(e, (KeyboardInterrupt, SystemExit)): | |
| raise | |
| # Any other error means the native scanner is not available | |
| NATIVE_SCANNER_AVAILABLE = False | |
| # Optionally log the underlying import error when in verbose mode | |
| if Configuration.verbose > 0: | |
| Color.pexception(e) |
| except BaseException: | ||
| SCAPY_AVAILABLE = False |
There was a problem hiding this comment.
Same concern as other native modules: except BaseException also catches KeyboardInterrupt/SystemExit and hides unexpected runtime/coding errors as "Scapy unavailable". Consider re-raising interrupt/exit exceptions and optionally storing/logging the underlying import exception for debugging.
| except BaseException: | |
| SCAPY_AVAILABLE = False | |
| SCAPY_IMPORT_ERROR = None | |
| except BaseException as e: | |
| # Do not swallow critical exceptions like KeyboardInterrupt/SystemExit. | |
| if isinstance(e, (KeyboardInterrupt, SystemExit)): | |
| raise | |
| # Scapy failed to import for some other reason; mark as unavailable | |
| # and keep the original exception for potential debugging. | |
| SCAPY_AVAILABLE = False | |
| SCAPY_IMPORT_ERROR = e |
| sniff, sendp, conf as scapy_conf | ||
| ) | ||
| SCAPY_AVAILABLE = True | ||
| except ImportError: | ||
| except BaseException: | ||
| SCAPY_AVAILABLE = False | ||
|
|
There was a problem hiding this comment.
Same pattern: except BaseException catches KeyboardInterrupt/SystemExit and can hide unexpected exceptions as a simple "Scapy unavailable" flag. Consider re-raising interrupt/exit exceptions and logging/preserving the underlying exception for debugging.
| try: | ||
| from .mac import NativeMac | ||
| except BaseException: | ||
| NativeMac = None | ||
|
|
||
| try: | ||
| from .deauth import ScapyDeauth, ContinuousDeauth as NativeDeauth | ||
| except BaseException: | ||
| ScapyDeauth = None | ||
| NativeDeauth = None | ||
|
|
||
| try: | ||
| from .handshake import ScapyHandshake | ||
| except BaseException: | ||
| ScapyHandshake = None | ||
|
|
||
| try: | ||
| from .wps import ScapyWPS, WPSInfo | ||
| except BaseException: | ||
| ScapyWPS = None | ||
| WPSInfo = None | ||
|
|
||
| try: | ||
| from .interface import NativeInterface, InterfaceInfo | ||
| except BaseException: | ||
| NativeInterface = None | ||
| InterfaceInfo = None | ||
|
|
||
| try: | ||
| from .pmkid import ScapyPMKID, PMKIDResult, PMKIDCapture | ||
| except BaseException: | ||
| ScapyPMKID = None | ||
| PMKIDResult = None | ||
| PMKIDCapture = None | ||
|
|
||
| try: | ||
| from .scanner import ChannelHopper, NativeScanner, AccessPoint, Client | ||
| except BaseException: | ||
| ChannelHopper = None | ||
| NativeScanner = None | ||
| AccessPoint = None | ||
| Client = None | ||
|
|
||
| try: | ||
| from .beacon import BeaconGenerator, create_fake_ap as create_beacon | ||
| except BaseException: | ||
| BeaconGenerator = None | ||
| create_beacon = None |
There was a problem hiding this comment.
Catching BaseException will also swallow KeyboardInterrupt and SystemExit, which can make Ctrl+C or normal interpreter shutdown behave unexpectedly during imports. If BaseException is required for specific import-time crashes, consider re-raising KeyboardInterrupt/SystemExit (and possibly GeneratorExit) while handling the rest, and/or capturing/logging the original exception for debugging when native features are unexpectedly unavailable.
| from ..native.pmkid import ScapyPMKID, PMKIDResult as NativePMKIDResult | ||
| NATIVE_PMKID_AVAILABLE = ScapyPMKID.is_available() | ||
| except ImportError: | ||
| except BaseException: |
There was a problem hiding this comment.
Catching BaseException here will also swallow KeyboardInterrupt/SystemExit, which can lead to surprising behavior at import time. If BaseException is needed to handle specific native import crashes, consider re-raising interrupt/exit exceptions and/or logging the underlying error when verbose so native PMKID availability doesn't silently flip to False on unexpected failures.
| except BaseException: | |
| except BaseException as e: | |
| # Do not swallow interrupt/exit exceptions; re-raise them. | |
| if isinstance(e, (KeyboardInterrupt, SystemExit)): | |
| raise | |
| # For any other failure, disable native PMKID support and log the reason. | |
| log_debug("Native PMKID unavailable due to import error: %s" % (e,)) |
| try: | ||
| from scapy.all import ( | ||
| rdpcap, Dot11, Dot11Beacon, Dot11ProbeResp, Dot11Elt, | ||
| conf as scapy_conf | ||
| ) | ||
| SCAPY_AVAILABLE = True | ||
| except ImportError: | ||
| except BaseException: | ||
| SCAPY_AVAILABLE = False |
There was a problem hiding this comment.
Catching BaseException here will also catch KeyboardInterrupt/SystemExit and can mask unexpected errors (e.g., a programming error inside Scapy) as simply "Scapy unavailable". Consider re-raising interrupt/exit exceptions and, if possible, preserving/logging the original exception for debugging while still allowing graceful fallback when the import truly fails.
| try: | ||
| from scapy.all import ( | ||
| RadioTap, Dot11, Dot11Deauth, Dot11Disas, | ||
| sendp, conf as scapy_conf | ||
| ) | ||
| SCAPY_AVAILABLE = True | ||
| except ImportError: | ||
| except BaseException: | ||
| SCAPY_AVAILABLE = False | ||
|
|
There was a problem hiding this comment.
except BaseException here will catch KeyboardInterrupt/SystemExit and can make import-time interrupts behave oddly, while also hiding unexpected errors in Scapy as "unavailable". Consider re-raising interrupt/exit exceptions and logging the underlying exception when falling back.
| Dot11Elt, Raw, conf as scapy_conf | ||
| ) | ||
| SCAPY_AVAILABLE = True | ||
| except ImportError: | ||
| except BaseException: | ||
| SCAPY_AVAILABLE = False | ||
|
|
There was a problem hiding this comment.
Catching BaseException here will also swallow KeyboardInterrupt/SystemExit and can mask genuine bugs in the Scapy import path as a simple availability toggle. Consider re-raising interrupt/exit exceptions and capturing/logging the underlying exception so unexpected failures are diagnosable.
…dex access, placeholder dep check, command splitting
min(60, None)with safe static method that handles uninitialized Configuration.wpa_attack_timeoutTest results: 43 failures → 0 failures (575 passed, 5 skipped)