fix 3
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s

This commit is contained in:
2026-03-01 14:43:59 +01:00
parent b0d1528142
commit e9ff417df1

View File

@@ -1,6 +1,6 @@
# src/routers/sniffer.py # src/routers/sniffer.py
from fastapi import APIRouter, HTTPException, Query from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field, root_validator from pydantic import BaseModel, Field
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
from src.network_sniffer import ( from src.network_sniffer import (
@@ -18,51 +18,31 @@ router = APIRouter()
class SnifferStartRequest(BaseModel): class SnifferStartRequest(BaseModel):
""" """
Request model for starting the sniffer on a specific bridge OR interface. Request model for starting the sniffer on a specific bridge OR interface.
Exactly one of `bridge` or `interface` must be provided. Both fields are optional here; the endpoint will validate that exactly one is present.
""" """
bridge: Optional[str] = Field(None, example="br0", description="Name of the Linux bridge to sniff on") bridge: Optional[str] = Field(None, example="br0", description="Name of the Linux bridge to sniff on")
interface: Optional[str] = Field(None, example="eth0", description="Name of the network interface to sniff on") interface: Optional[str] = Field(None, example="eth0", description="Name of the network interface to sniff on")
@root_validator
def exactly_one_target(cls, values):
b, i = values.get("bridge"), values.get("interface")
if bool(b) == bool(i):
# either both set or both unset -> error
raise ValueError("Exactly one of 'bridge' or 'interface' must be provided")
return values
class SnifferStartResponse(BaseModel): class SnifferStartResponse(BaseModel):
"""
Response model returned when sniffer starts successfully.
"""
started: bool = Field(..., description="Whether the sniffer was started successfully") started: bool = Field(..., description="Whether the sniffer was started successfully")
target: str = Field(..., description="Target that was started (bridge or interface)") target: str = Field(..., description="Target that was started (bridge or interface)")
target_type: str = Field(..., description="Either 'bridge' or 'interface'") target_type: str = Field(..., description="Either 'bridge' or 'interface'")
class SnifferStopResponse(BaseModel): class SnifferStopResponse(BaseModel):
"""
Response model returned when the sniffer stops successfully.
"""
stopped: bool = Field(..., description="Whether the sniffer was stopped successfully") stopped: bool = Field(..., description="Whether the sniffer was stopped successfully")
target: Optional[str] = Field(None, description="Target stopped; null if global stop") target: Optional[str] = Field(None, description="Target stopped; null if global stop")
target_type: Optional[str] = Field(None, description="'bridge' or 'interface' or None") target_type: Optional[str] = Field(None, description="'bridge' or 'interface' or None")
class InterfaceSnifferStatus(BaseModel): class InterfaceSnifferStatus(BaseModel):
"""
Status of an individual interface monitored by the AF_PACKET sniffer.
"""
running: bool = Field(..., description="Whether the sniffer thread/socket is active") running: bool = Field(..., description="Whether the sniffer thread/socket is active")
exists: bool = Field(..., description="Whether the interface exists in /sys/class/net") exists: bool = Field(..., description="Whether the interface exists in /sys/class/net")
up: bool = Field(..., description="Whether the interface is operationally UP") up: bool = Field(..., description="Whether the interface is operationally UP")
class SnifferStatusResponse(BaseModel): class SnifferStatusResponse(BaseModel):
"""
Response model for the sniffer status endpoint.
"""
interfaces: Dict[str, InterfaceSnifferStatus] = Field( interfaces: Dict[str, InterfaceSnifferStatus] = Field(
..., description="Map of interface names to their sniffer status" ..., description="Map of interface names to their sniffer status"
) )
@@ -75,13 +55,19 @@ class SnifferStatusResponse(BaseModel):
def sniffer_start(req: SnifferStartRequest): def sniffer_start(req: SnifferStartRequest):
""" """
Start the AF_PACKET sniffer for the given bridge OR interface. Start the AF_PACKET sniffer for the given bridge OR interface.
Exactly one of `bridge` or `interface` must be provided; validate here explicitly so this
is compatible across Pydantic versions.
""" """
# explicit validation (works with pydantic v1 & v2)
if bool(req.bridge) == bool(req.interface):
raise HTTPException(status_code=400, detail="Exactly one of 'bridge' or 'interface' must be provided")
try: try:
if req.interface: if req.interface:
start_afpacket_sniffer(req.interface, target_is_interface=True) start_afpacket_sniffer(req.interface, target_is_interface=True)
return SnifferStartResponse(started=True, target=req.interface, target_type="interface") return SnifferStartResponse(started=True, target=req.interface, target_type="interface")
else: else:
# req.bridge is guaranteed present by validator
start_afpacket_sniffer(req.bridge, target_is_interface=False) start_afpacket_sniffer(req.bridge, target_is_interface=False)
return SnifferStartResponse(started=True, target=req.bridge, target_type="bridge") return SnifferStartResponse(started=True, target=req.bridge, target_type="bridge")
except Exception as exc: except Exception as exc:
@@ -100,15 +86,17 @@ def sniffer_stop(
- If `?interface=eth0` provided: stop sniffing on that interface only (close socket). - If `?interface=eth0` provided: stop sniffing on that interface only (close socket).
- If `?bridge=br0` provided: stop snapshot/sockets associated with that bridge. - If `?bridge=br0` provided: stop snapshot/sockets associated with that bridge.
""" """
if bridge and interface:
raise HTTPException(status_code=400, detail="Only one of 'bridge' or 'interface' may be provided")
try: try:
if interface and bridge:
raise HTTPException(status_code=400, detail="Only one of 'bridge' or 'interface' may be provided")
if interface: if interface:
stop_afpacket_sniffer(target=interface, target_is_interface=True) stop_afpacket_sniffer(target=interface, target_is_interface=True)
return SnifferStopResponse(stopped=True, target=interface, target_type="interface") return SnifferStopResponse(stopped=True, target=interface, target_type="interface")
if bridge: if bridge:
stop_afpacket_sniffer(target=bridge, target_is_interface=False) stop_afpacket_sniffer(target=bridge, target_is_interface=False)
return SnifferStopResponse(stopped=True, target=bridge, target_type="bridge") return SnifferStopResponse(stopped=True, target=bridge, target_type="bridge")
# global stop # global stop
stop_afpacket_sniffer() stop_afpacket_sniffer()
return SnifferStopResponse(stopped=True, target=None, target_type=None) return SnifferStopResponse(stopped=True, target=None, target_type=None)