mirror of
https://github.com/smittix/intercept.git
synced 2026-07-26 09:48:09 -07:00
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:
+22
-22
@@ -7,6 +7,7 @@ threat detection, and reporting.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import queue
|
||||
@@ -23,9 +24,9 @@ from data.tscm_frequencies import (
|
||||
get_sweep_preset,
|
||||
)
|
||||
from utils.database import (
|
||||
acknowledge_tscm_threat,
|
||||
add_device_timeline_entry,
|
||||
add_tscm_threat,
|
||||
acknowledge_tscm_threat,
|
||||
cleanup_old_timeline_entries,
|
||||
create_tscm_schedule,
|
||||
create_tscm_sweep,
|
||||
@@ -43,6 +44,8 @@ from utils.database import (
|
||||
update_tscm_schedule,
|
||||
update_tscm_sweep,
|
||||
)
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.sse import sse_stream_fanout
|
||||
from utils.tscm.baseline import (
|
||||
BaselineComparator,
|
||||
BaselineRecorder,
|
||||
@@ -56,12 +59,10 @@ from utils.tscm.correlation import (
|
||||
from utils.tscm.detector import ThreatDetector
|
||||
from utils.tscm.device_identity import (
|
||||
get_identity_engine,
|
||||
reset_identity_engine,
|
||||
ingest_ble_dict,
|
||||
ingest_wifi_dict,
|
||||
reset_identity_engine,
|
||||
)
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.sse import sse_stream_fanout
|
||||
|
||||
# Import unified Bluetooth scanner helper for TSCM integration
|
||||
try:
|
||||
@@ -659,8 +660,8 @@ def _scan_bluetooth_devices(interface: str, duration: int = 10) -> list[dict]:
|
||||
Uses the BLE scanner module (bleak library) for proper manufacturer ID
|
||||
detection, with fallback to system tools if bleak is unavailable.
|
||||
"""
|
||||
import platform
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -874,10 +875,8 @@ def _scan_bluetooth_devices(interface: str, duration: int = 10) -> list[dict]:
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
|
||||
try:
|
||||
with contextlib.suppress(OSError):
|
||||
os.close(master_fd)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
logger.info(f"bluetoothctl scan found {len(devices)} devices")
|
||||
|
||||
@@ -914,7 +913,8 @@ def _scan_rf_signals(
|
||||
"""
|
||||
# Default stop check uses module-level _sweep_running
|
||||
if stop_check is None:
|
||||
stop_check = lambda: not _sweep_running
|
||||
def stop_check():
|
||||
return not _sweep_running
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -954,11 +954,11 @@ def _scan_rf_signals(
|
||||
# Tool exists but no device detected — try anyway (detection may have failed)
|
||||
sdr_type = 'rtlsdr'
|
||||
sweep_tool_path = rtl_power_path
|
||||
logger.info(f"No SDR detected but rtl_power found, attempting RTL-SDR scan")
|
||||
logger.info("No SDR detected but rtl_power found, attempting RTL-SDR scan")
|
||||
elif hackrf_sweep_path:
|
||||
sdr_type = 'hackrf'
|
||||
sweep_tool_path = hackrf_sweep_path
|
||||
logger.info(f"No SDR detected but hackrf_sweep found, attempting HackRF scan")
|
||||
logger.info("No SDR detected but hackrf_sweep found, attempting HackRF scan")
|
||||
|
||||
if not sweep_tool_path:
|
||||
logger.warning("No supported sweep tool found (rtl_power or hackrf_sweep)")
|
||||
@@ -1059,14 +1059,14 @@ def _scan_rf_signals(
|
||||
|
||||
# Parse the CSV output (same format for both rtl_power and hackrf_sweep)
|
||||
if os.path.exists(tmp_path) and os.path.getsize(tmp_path) > 0:
|
||||
with open(tmp_path, 'r') as f:
|
||||
with open(tmp_path) as f:
|
||||
for line in f:
|
||||
parts = line.strip().split(',')
|
||||
if len(parts) >= 7:
|
||||
try:
|
||||
# CSV format: date, time, hz_low, hz_high, hz_step, samples, db_values...
|
||||
hz_low = int(parts[2].strip())
|
||||
hz_high = int(parts[3].strip())
|
||||
int(parts[3].strip())
|
||||
hz_step = float(parts[4].strip())
|
||||
db_values = [float(x) for x in parts[6:] if x.strip()]
|
||||
|
||||
@@ -1100,10 +1100,8 @@ def _scan_rf_signals(
|
||||
|
||||
finally:
|
||||
# Cleanup temp file
|
||||
try:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(tmp_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# Deduplicate nearby frequencies (within 100kHz)
|
||||
if signals:
|
||||
@@ -1816,9 +1814,11 @@ def _generate_assessment(summary: dict) -> str:
|
||||
# =============================================================================
|
||||
# Import sub-modules to register routes on tscm_bp
|
||||
# =============================================================================
|
||||
from routes.tscm import sweep # noqa: E402, F401
|
||||
from routes.tscm import baseline # noqa: E402, F401
|
||||
from routes.tscm import cases # noqa: E402, F401
|
||||
from routes.tscm import meeting # noqa: E402, F401
|
||||
from routes.tscm import analysis # noqa: E402, F401
|
||||
from routes.tscm import schedules # noqa: E402, F401
|
||||
from routes.tscm import (
|
||||
analysis, # noqa: E402, F401
|
||||
baseline, # noqa: E402, F401
|
||||
cases, # noqa: E402, F401
|
||||
meeting, # noqa: E402, F401
|
||||
schedules, # noqa: E402, F401
|
||||
sweep, # noqa: E402, F401
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@ from datetime import datetime
|
||||
from flask import Response, jsonify, request
|
||||
|
||||
from routes.tscm import (
|
||||
_current_sweep_id,
|
||||
_generate_assessment,
|
||||
tscm_bp,
|
||||
)
|
||||
@@ -253,9 +252,9 @@ def get_pdf_report():
|
||||
summary, and mandatory disclaimers.
|
||||
"""
|
||||
try:
|
||||
from utils.tscm.reports import generate_report, get_pdf_report
|
||||
from utils.tscm.advanced import detect_sweep_capabilities, get_timeline_manager
|
||||
from routes.tscm import _current_sweep_id
|
||||
from utils.tscm.advanced import detect_sweep_capabilities, get_timeline_manager
|
||||
from utils.tscm.reports import generate_report, get_pdf_report
|
||||
|
||||
sweep_id = request.args.get('sweep_id', _current_sweep_id, type=int)
|
||||
if not sweep_id:
|
||||
@@ -306,9 +305,9 @@ def get_technical_annex():
|
||||
for audit purposes. No packet data included.
|
||||
"""
|
||||
try:
|
||||
from utils.tscm.reports import generate_report, get_json_annex, get_csv_annex
|
||||
from utils.tscm.advanced import detect_sweep_capabilities, get_timeline_manager
|
||||
from routes.tscm import _current_sweep_id
|
||||
from utils.tscm.advanced import detect_sweep_capabilities, get_timeline_manager
|
||||
from utils.tscm.reports import generate_report, get_csv_annex, get_json_annex
|
||||
|
||||
sweep_id = request.args.get('sweep_id', _current_sweep_id, type=int)
|
||||
format_type = request.args.get('format', 'json')
|
||||
@@ -900,8 +899,8 @@ def get_device_timeline_endpoint(identifier: str):
|
||||
and meeting window correlation.
|
||||
"""
|
||||
try:
|
||||
from utils.tscm.advanced import get_timeline_manager
|
||||
from utils.database import get_device_timeline
|
||||
from utils.tscm.advanced import get_timeline_manager
|
||||
|
||||
protocol = request.args.get('protocol', 'bluetooth')
|
||||
since_hours = request.args.get('since_hours', 24, type=int)
|
||||
|
||||
@@ -25,7 +25,6 @@ from utils.database import (
|
||||
set_active_tscm_baseline,
|
||||
)
|
||||
from utils.tscm.baseline import (
|
||||
BaselineComparator,
|
||||
get_comparison_for_active_baseline,
|
||||
)
|
||||
|
||||
@@ -213,7 +212,6 @@ def get_baseline_diff(baseline_id: int, sweep_id: int):
|
||||
def get_baseline_health(baseline_id: int):
|
||||
"""Get health assessment for a baseline."""
|
||||
try:
|
||||
from utils.tscm.advanced import BaselineHealth
|
||||
|
||||
baseline = get_tscm_baseline(baseline_id)
|
||||
if not baseline:
|
||||
|
||||
@@ -91,7 +91,6 @@ def start_tracked_meeting():
|
||||
"""
|
||||
from utils.database import start_meeting_window
|
||||
from utils.tscm.advanced import get_timeline_manager
|
||||
from routes.tscm import _current_sweep_id
|
||||
|
||||
data = request.get_json() or {}
|
||||
|
||||
@@ -156,9 +155,9 @@ def end_tracked_meeting(meeting_id: int):
|
||||
def get_meeting_summary_endpoint(meeting_id: int):
|
||||
"""Get detailed summary of device activity during a meeting."""
|
||||
try:
|
||||
from routes.tscm import _current_sweep_id
|
||||
from utils.database import get_meeting_windows
|
||||
from utils.tscm.advanced import generate_meeting_summary, get_timeline_manager
|
||||
from routes.tscm import _current_sweep_id
|
||||
|
||||
# Get meeting window
|
||||
windows = get_meeting_windows(_current_sweep_id or 0)
|
||||
@@ -194,7 +193,6 @@ def get_meeting_summary_endpoint(meeting_id: int):
|
||||
def get_active_meeting():
|
||||
"""Get currently active meeting window."""
|
||||
from utils.database import get_active_meeting_window
|
||||
from routes.tscm import _current_sweep_id
|
||||
|
||||
meeting = get_active_meeting_window(_current_sweep_id)
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ from routes.tscm import (
|
||||
_get_schedule_timezone,
|
||||
_next_run_from_cron,
|
||||
_start_sweep_internal,
|
||||
_sweep_running,
|
||||
tscm_bp,
|
||||
)
|
||||
from utils.database import (
|
||||
|
||||
+3
-11
@@ -7,27 +7,25 @@ Handles /sweep/*, /status, /devices, /presets/*, /feed/*,
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
from flask import Response, jsonify, request
|
||||
|
||||
from data.tscm_frequencies import get_all_sweep_presets, get_sweep_preset
|
||||
from routes.tscm import (
|
||||
_baseline_recorder,
|
||||
_current_sweep_id,
|
||||
_emit_event,
|
||||
_start_sweep_internal,
|
||||
_sweep_running,
|
||||
tscm_bp,
|
||||
tscm_queue,
|
||||
_baseline_recorder,
|
||||
)
|
||||
from data.tscm_frequencies import get_all_sweep_presets, get_sweep_preset
|
||||
from utils.database import get_tscm_sweep, update_tscm_sweep
|
||||
from utils.event_pipeline import process_event
|
||||
from utils.sse import sse_stream_fanout
|
||||
@@ -38,7 +36,6 @@ logger = logging.getLogger('intercept.tscm')
|
||||
@tscm_bp.route('/status')
|
||||
def tscm_status():
|
||||
"""Check if any TSCM operation is currently running."""
|
||||
from routes.tscm import _sweep_running
|
||||
return jsonify({'running': _sweep_running})
|
||||
|
||||
|
||||
@@ -98,7 +95,6 @@ def stop_sweep():
|
||||
@tscm_bp.route('/sweep/status')
|
||||
def sweep_status():
|
||||
"""Get current sweep status."""
|
||||
from routes.tscm import _sweep_running, _current_sweep_id
|
||||
|
||||
status = {
|
||||
'running': _sweep_running,
|
||||
@@ -116,7 +112,6 @@ def sweep_status():
|
||||
@tscm_bp.route('/sweep/stream')
|
||||
def sweep_stream():
|
||||
"""SSE stream for real-time sweep updates."""
|
||||
from routes.tscm import tscm_queue
|
||||
|
||||
def _on_msg(msg: dict[str, Any]) -> None:
|
||||
process_event('tscm', msg, msg.get('type'))
|
||||
@@ -218,7 +213,7 @@ def get_tscm_devices():
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
blocks = re.split(r'(?=^hci\d+:)', result.stdout, flags=re.MULTILINE)
|
||||
for idx, block in enumerate(blocks):
|
||||
for _idx, block in enumerate(blocks):
|
||||
if block.strip():
|
||||
first_line = block.split('\n')[0]
|
||||
match = re.match(r'(hci\d+):', first_line)
|
||||
@@ -353,7 +348,6 @@ def get_preset(preset_name: str):
|
||||
@tscm_bp.route('/feed/wifi', methods=['POST'])
|
||||
def feed_wifi():
|
||||
"""Feed WiFi device data for baseline recording."""
|
||||
from routes.tscm import _baseline_recorder
|
||||
|
||||
data = request.get_json()
|
||||
if data:
|
||||
@@ -367,7 +361,6 @@ def feed_wifi():
|
||||
@tscm_bp.route('/feed/bluetooth', methods=['POST'])
|
||||
def feed_bluetooth():
|
||||
"""Feed Bluetooth device data for baseline recording."""
|
||||
from routes.tscm import _baseline_recorder
|
||||
|
||||
data = request.get_json()
|
||||
if data:
|
||||
@@ -378,7 +371,6 @@ def feed_bluetooth():
|
||||
@tscm_bp.route('/feed/rf', methods=['POST'])
|
||||
def feed_rf():
|
||||
"""Feed RF signal data for baseline recording."""
|
||||
from routes.tscm import _baseline_recorder
|
||||
|
||||
data = request.get_json()
|
||||
if data:
|
||||
|
||||
Reference in New Issue
Block a user