uaf.git

uaf

~
git clone https://github.com/debuman/uaf.git

ap_scanner/models.py (3364B)

      1 """Data models for AP Scanner."""
      2 
      3 from __future__ import annotations
      4 
      5 from dataclasses import dataclass, field
      6 from datetime import datetime
      7 
      8 
      9 @dataclass
     10 class ActiveScanClient:
     11     """A client discovered during active ARP scanning."""
     12 
     13     ip: str
     14     mac: str
     15     vendor: str = ""
     16 
     17 
     18 @dataclass
     19 class ActiveScanResult:
     20     """Results from an active scan of an open AP."""
     21 
     22     bssid: str
     23     ssid: str
     24     scan_time: datetime = field(default_factory=datetime.now)
     25     connected: bool = False
     26     assigned_ip: str = ""
     27     subnet_mask: str = ""
     28     gateway_ip: str = ""
     29     gateway_mac: str = ""
     30     dns_servers: list[str] = field(default_factory=list)
     31     dhcp_server: str = ""
     32     clients: list[ActiveScanClient] = field(default_factory=list)
     33     gateway_open_ports: list[int] = field(default_factory=list)
     34     duration_secs: float = 0.0
     35     error: str = ""
     36 
     37     @property
     38     def client_count(self) -> int:
     39         return len(self.clients)
     40 
     41     @property
     42     def summary(self) -> str:
     43         if self.error:
     44             return f"Failed: {self.error}"
     45         parts = []
     46         if self.assigned_ip:
     47             parts.append(f"IP: {self.assigned_ip}")
     48         if self.gateway_ip:
     49             parts.append(f"GW: {self.gateway_ip}")
     50         if self.clients:
     51             parts.append(f"Clients: {self.client_count}")
     52         if self.gateway_open_ports:
     53             parts.append(f"Ports: {','.join(str(p) for p in self.gateway_open_ports)}")
     54         return " | ".join(parts) if parts else "No data collected"
     55 
     56 
     57 @dataclass
     58 class AccessPoint:
     59     """Represents a discovered wireless access point."""
     60 
     61     bssid: str
     62     ssid: str  # empty string for hidden networks
     63     channel: int  # primary channel
     64     rssi: int | None  # dBm (e.g., -42)
     65     encryption: str  # OPN, WEP, WPA, WPA2, WPA3, WPA2/WPA3
     66     cipher: str  # CCMP, TKIP, CCMP/TKIP, --
     67     auth: str  # PSK, SAE, MGT, PSK/SAE, --
     68     band: str  # "2.4GHz" or "5GHz"
     69     wifi_standard: str  # "b, g, n" or "a, n, ac, ax" (comma-separated, all supported)
     70     width: int = 20  # Channel width: 20, 40, 80, 160 MHz
     71     channel_desc: str = ""  # Bonded channels: "36+40+44+48"
     72     vendor: str = ""  # OUI vendor name
     73     wps: str = ""  # WPS version: "1.0", "2.0", or ""
     74     wep: str = ""  # WEP security detail (auth-cipher) or ""
     75     wpa: str = ""  # WPA security detail (auth-cipher) or ""
     76     wpa2: str = ""  # WPA2 security detail (auth-cipher) or ""
     77     wpa3: str = ""  # WPA3 security detail (auth-cipher) or ""
     78     first_seen: datetime = field(default_factory=datetime.now)
     79     last_seen: datetime = field(default_factory=datetime.now)
     80     beacon_count: int = 0
     81     frequency: int = 0  # MHz
     82     active_scan_result: ActiveScanResult | None = None
     83 
     84     @property
     85     def is_hidden(self) -> bool:
     86         return len(self.ssid.strip("\x00").strip()) == 0
     87 
     88     @property
     89     def display_ssid(self) -> str:
     90         return "[Hidden]" if self.is_hidden else self.ssid
     91 
     92     @property
     93     def rssi_display(self) -> str:
     94         return str(self.rssi) if self.rssi is not None else "N/A"
     95 
     96     @property
     97     def standard_display(self) -> str:
     98         return self.wifi_standard if self.wifi_standard else "--"
     99 
    100     @property
    101     def channel_display(self) -> str:
    102         """Show bonded channels if available, otherwise primary channel."""
    103         return self.channel_desc if self.channel_desc else str(self.channel)