v2.26.0: fix SSE fanout crash and branded logo FOUC

- Fix SSE fanout thread AttributeError when source queue is None during
  interpreter shutdown by snapshotting to local variable with null guard
- Fix branded "i" logo rendering oversized on first page load (FOUC) by
  adding inline width/height to SVG elements across 10 templates
- Bump version to 2.26.0 in config.py, pyproject.toml, and CHANGELOG.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-03-13 11:51:27 +00:00
parent 00362bcd57
commit e00fbfddc1
183 changed files with 2006 additions and 4243 deletions
+2 -2
View File
@@ -4,11 +4,11 @@ WiFi scan output parsers.
Each parser converts tool-specific output into WiFiObservation objects.
"""
from .airodump import parse_airodump_csv
from .airport import parse_airport_scan
from .nmcli import parse_nmcli_scan
from .iw import parse_iw_scan
from .iwlist import parse_iwlist_scan
from .airodump import parse_airodump_csv
from .nmcli import parse_nmcli_scan
__all__ = [
'parse_airport_scan',
+14 -15
View File
@@ -22,29 +22,28 @@ import io
import logging
import re
from datetime import datetime
from typing import Optional
from ..models import WiFiObservation
from ..constants import (
AUTH_EAP,
AUTH_OPEN,
AUTH_OWE,
AUTH_PSK,
AUTH_SAE,
AUTH_UNKNOWN,
CHANNEL_FREQUENCIES,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_UNKNOWN,
CIPHER_WEP,
SECURITY_OPEN,
SECURITY_UNKNOWN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA3,
SECURITY_WPA_WPA2,
SECURITY_UNKNOWN,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_WEP,
CIPHER_UNKNOWN,
AUTH_PSK,
AUTH_SAE,
AUTH_EAP,
AUTH_OWE,
AUTH_OPEN,
AUTH_UNKNOWN,
CHANNEL_FREQUENCIES,
)
from ..models import WiFiObservation
logger = logging.getLogger(__name__)
@@ -63,7 +62,7 @@ def parse_airodump_csv(filepath: str) -> tuple[list[WiFiObservation], list[dict]
clients = []
try:
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
with open(filepath, encoding='utf-8', errors='replace') as f:
content = f.read()
# airodump-ng separates sections with blank lines
+15 -19
View File
@@ -12,33 +12,31 @@ from __future__ import annotations
import logging
import re
from datetime import datetime
from typing import Optional
from ..models import WiFiObservation
from ..constants import (
AUTH_EAP,
AUTH_OPEN,
AUTH_PSK,
AUTH_SAE,
AUTH_UNKNOWN,
CHANNEL_FREQUENCIES,
CIPHER_CCMP,
CIPHER_NONE,
CIPHER_TKIP,
CIPHER_UNKNOWN,
CIPHER_WEP,
SECURITY_OPEN,
SECURITY_UNKNOWN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA2_WPA3,
SECURITY_WPA3,
SECURITY_WPA_WPA2,
SECURITY_WPA2_WPA3,
SECURITY_UNKNOWN,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_WEP,
CIPHER_NONE,
CIPHER_UNKNOWN,
AUTH_PSK,
AUTH_SAE,
AUTH_EAP,
AUTH_OPEN,
AUTH_UNKNOWN,
WIDTH_20_MHZ,
WIDTH_40_MHZ,
get_band_from_channel,
CHANNEL_FREQUENCIES,
)
from ..models import WiFiObservation
logger = logging.getLogger(__name__)
@@ -68,7 +66,7 @@ def parse_airport_scan(output: str) -> list[WiFiObservation]:
return observations
def _parse_airport_line(line: str) -> Optional[WiFiObservation]:
def _parse_airport_line(line: str) -> WiFiObservation | None:
"""Parse a single line of airport output."""
# airport output is space-aligned, need careful parsing
# Format: SSID (variable width) BSSID RSSI CHANNEL HT CC SECURITY
@@ -95,10 +93,8 @@ def _parse_airport_line(line: str) -> Optional[WiFiObservation]:
ssid = line[:bssid_pos].strip()
# Handle hidden network indicator
is_hidden = False
if ssid == '--' or not ssid:
ssid = None
is_hidden = True
# Parse remainder after BSSID
remainder = line[bssid_match.end():].strip()
+13 -16
View File
@@ -24,35 +24,32 @@ from __future__ import annotations
import logging
import re
from datetime import datetime
from typing import Optional
from ..models import WiFiObservation
from ..constants import (
AUTH_EAP,
AUTH_OPEN,
AUTH_OWE,
AUTH_PSK,
AUTH_SAE,
AUTH_UNKNOWN,
CIPHER_CCMP,
CIPHER_GCMP,
CIPHER_TKIP,
CIPHER_UNKNOWN,
CIPHER_WEP,
SECURITY_OPEN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA3,
SECURITY_WPA_WPA2,
SECURITY_WPA2_WPA3,
SECURITY_UNKNOWN,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_GCMP,
CIPHER_WEP,
CIPHER_UNKNOWN,
AUTH_PSK,
AUTH_SAE,
AUTH_EAP,
AUTH_OWE,
AUTH_OPEN,
AUTH_UNKNOWN,
WIDTH_20_MHZ,
WIDTH_40_MHZ,
WIDTH_80_MHZ,
WIDTH_160_MHZ,
get_channel_from_frequency,
)
from ..models import WiFiObservation
logger = logging.getLogger(__name__)
@@ -90,7 +87,7 @@ def parse_iw_scan(output: str) -> list[WiFiObservation]:
return observations
def _parse_iw_block(lines: list[str]) -> Optional[WiFiObservation]:
def _parse_iw_block(lines: list[str]) -> WiFiObservation | None:
"""Parse a single BSS block from iw output."""
try:
# First line: BSS 00:11:22:33:44:55(on wlan0) -- associated
+11 -13
View File
@@ -25,27 +25,25 @@ from __future__ import annotations
import logging
import re
from datetime import datetime
from typing import Optional
from ..models import WiFiObservation
from ..constants import (
AUTH_EAP,
AUTH_OPEN,
AUTH_PSK,
AUTH_UNKNOWN,
CHANNEL_FREQUENCIES,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_UNKNOWN,
CIPHER_WEP,
SECURITY_OPEN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA_WPA2,
SECURITY_UNKNOWN,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_WEP,
CIPHER_UNKNOWN,
AUTH_PSK,
AUTH_EAP,
AUTH_OPEN,
AUTH_UNKNOWN,
get_channel_from_frequency,
CHANNEL_FREQUENCIES,
)
from ..models import WiFiObservation
logger = logging.getLogger(__name__)
@@ -83,7 +81,7 @@ def parse_iwlist_scan(output: str) -> list[WiFiObservation]:
return observations
def _parse_iwlist_block(lines: list[str]) -> Optional[WiFiObservation]:
def _parse_iwlist_block(lines: list[str]) -> WiFiObservation | None:
"""Parse a single Cell block from iwlist output."""
try:
# Extract BSSID from first line
+17 -19
View File
@@ -11,30 +11,28 @@ from __future__ import annotations
import logging
import re
from datetime import datetime
from typing import Optional
from ..models import WiFiObservation
from ..constants import (
SECURITY_OPEN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA3,
SECURITY_WPA_WPA2,
SECURITY_WPA2_WPA3,
SECURITY_ENTERPRISE,
SECURITY_UNKNOWN,
AUTH_EAP,
AUTH_OPEN,
AUTH_PSK,
AUTH_SAE,
AUTH_UNKNOWN,
CIPHER_CCMP,
CIPHER_TKIP,
CIPHER_UNKNOWN,
AUTH_PSK,
AUTH_SAE,
AUTH_EAP,
AUTH_OPEN,
AUTH_UNKNOWN,
SECURITY_ENTERPRISE,
SECURITY_OPEN,
SECURITY_UNKNOWN,
SECURITY_WEP,
SECURITY_WPA,
SECURITY_WPA2,
SECURITY_WPA2_WPA3,
SECURITY_WPA3,
SECURITY_WPA_WPA2,
get_channel_from_frequency,
get_band_from_frequency,
)
from ..models import WiFiObservation
logger = logging.getLogger(__name__)
@@ -62,7 +60,7 @@ def parse_nmcli_scan(output: str) -> list[WiFiObservation]:
return observations
def _parse_nmcli_line(line: str) -> Optional[WiFiObservation]:
def _parse_nmcli_line(line: str) -> WiFiObservation | None:
"""Parse a single line of nmcli terse output."""
try:
# nmcli terse format uses : as delimiter but escapes colons in values with \:
@@ -188,7 +186,7 @@ def _parse_nmcli_security(security_str: str) -> tuple[str, str, str]:
cipher = CIPHER_UNKNOWN
if security in (SECURITY_WPA2, SECURITY_WPA3, SECURITY_WPA2_WPA3, SECURITY_ENTERPRISE):
cipher = CIPHER_CCMP
elif security == SECURITY_WPA or security == SECURITY_WPA_WPA2:
elif security in (SECURITY_WPA, SECURITY_WPA_WPA2):
cipher = CIPHER_TKIP # Often TKIP for mixed mode
# Determine auth