This commit is contained in:
2025-07-25 21:45:07 -04:00
parent f75c757b12
commit d77dd386f3
19 changed files with 2131 additions and 38 deletions

View File

@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING
from .navigation import NavigationHandler
from .panels import FlowListPanel, DetailPanel, TimelinePanel
from ..utils.signal_visualizer import signal_visualizer
if TYPE_CHECKING:
from ..analysis.core import EthernetAnalyzer
@@ -61,6 +62,8 @@ class TUIInterface:
if self.analyzer.is_live:
self.analyzer.stop_capture = True
break
elif action == 'visualize':
self._handle_visualization()
def _draw_main_view(self, stdscr):
"""Draw three-panel main view: flows list, details, and timeline"""
@@ -183,4 +186,55 @@ class TUIInterface:
self.analyzer.statistics_engine.get_max_sigma_deviation(x),
x.frame_count
), reverse=True)
return flows_list
return flows_list
def _handle_visualization(self):
"""Handle Chapter 10 signal visualization for selected flow"""
flows_list = self._get_flows_list()
if not flows_list or self.navigation.selected_flow >= len(flows_list):
return
selected_flow = flows_list[self.navigation.selected_flow]
flow_key = f"{selected_flow.src_ip}->{selected_flow.dst_ip}"
# Check if this flow has Chapter 10 data
if not self.navigation.has_chapter10_data(selected_flow):
return
# Get packets for this flow
flow_packets = self._get_flow_packets(selected_flow)
if not flow_packets:
return
# Launch visualization in TUI mode (will save plots to files)
try:
# Set TUI context to avoid GUI windows
signal_visualizer._in_tui_context = True
# Temporarily show status (will be overwritten by next TUI refresh)
print(f"Generating signal visualization for flow {flow_key}...")
signal_visualizer.visualize_flow_signals(selected_flow, flow_packets, gui_mode=False)
except Exception as e:
# Log error but don't disrupt TUI
print(f"Visualization error: {e}")
pass
def _get_flow_packets(self, flow):
"""Get all packets belonging to a specific flow"""
flow_packets = []
# Iterate through all packets and filter by source/destination
for packet in self.analyzer.all_packets:
try:
# Check if packet matches this flow
if hasattr(packet, 'haslayer'):
from scapy.all import IP
if packet.haslayer(IP):
ip_layer = packet[IP]
if ip_layer.src == flow.src_ip and ip_layer.dst == flow.dst_ip:
flow_packets.append(packet)
except:
continue
return flow_packets

View File

@@ -55,6 +55,8 @@ class NavigationHandler:
max_items = self._get_total_display_items(flows_list)
self.selected_flow = max_items - 1
return 'selection_change'
elif key == ord('v') and self.current_view == 'main': # Visualize Chapter 10 signals
return 'visualize'
return 'none'
@@ -70,8 +72,21 @@ class NavigationHandler:
"""Get status bar text based on current view"""
if self.current_view == 'main':
timeline_status = "ON" if self.show_timeline else "OFF"
return f"[↑↓]navigate [PgUp/PgDn]scroll [t]imeline:{timeline_status} [d]issection [q]uit"
return f"[↑↓]navigate [PgUp/PgDn]scroll [t]imeline:{timeline_status} [v]isualize CH10 [d]issection [q]uit"
elif self.current_view == 'dissection':
return "[m]ain view [q]uit"
else:
return "[m]ain [d]issection [q]uit"
return "[m]ain [d]issection [q]uit"
def has_chapter10_data(self, flow: FlowStats) -> bool:
"""Check if a flow contains Chapter 10 data"""
# Check if any frame types in the flow are Chapter 10 related
for frame_type in flow.frame_types.keys():
if 'CH10' in frame_type.upper() or 'TMATS' in frame_type.upper():
return True
# Check detected protocol types
if 'CHAPTER10' in flow.detected_protocol_types or 'CH10' in flow.detected_protocol_types:
return True
return False