test path visu
All checks were successful
Build and Deploy MITM Webserver / traffic_target (push) Successful in 1s
Build and Deploy MITM Webserver / build (push) Successful in 10s

This commit is contained in:
2026-03-30 23:21:36 +02:00
parent 5f6eedf859
commit cedc52eb8d
5 changed files with 345 additions and 8 deletions

View File

@@ -85,6 +85,36 @@ class InterfaceHostProtocolAnalysisResponse(BaseModel):
)
class InterfaceProtocolPathEvidence(BaseModel):
ingress_interface: Optional[str] = Field(None, description="Observed ingress interface for the packet path.")
egress_interface: Optional[str] = Field(None, description="Observed egress interface for the packet path.")
src_ip_address: Optional[str] = Field(None, description="Observed source IP address.")
src_mac_address: Optional[str] = Field(None, description="Observed source MAC address.")
dst_ip_address: Optional[str] = Field(None, description="Observed destination IP address.")
dst_mac_address: Optional[str] = Field(None, description="Observed destination MAC address.")
protocol: str = Field(..., description="Detected application or fallback protocol for the packet path.")
ethernet_protocol: Optional[str] = Field(None, description="Dominant Ethernet protocol associated with this path.")
ip_protocol: Optional[str] = Field(None, description="Dominant IP protocol associated with this path.")
packet_count: int = Field(..., description="Packet observations supporting this end-to-end path.")
last_seen: datetime = Field(..., description="Most recent packet timestamp supporting this path.")
accept_count: int = Field(0, description="Packets with verdict=accept for this path.")
drop_count: int = Field(0, description="Packets with verdict=drop for this path.")
reject_count: int = Field(0, description="Packets with verdict=reject for this path.")
unknown_count: int = Field(0, description="Packets with verdict pending/unknown or without a verdict.")
class InterfaceProtocolPathAnalysisResponse(BaseModel):
since: Optional[datetime] = Field(None, description="Only packets at or after this timestamp were analyzed.")
paths: List[InterfaceProtocolPathEvidence] = Field(default_factory=list)
notes: List[str] = Field(
default_factory=lambda: [
"This Sankey view is built from packet paths, not from inferred interface-host attachment.",
"Each row represents a grouped ingress -> source endpoint -> protocol -> destination endpoint -> egress path.",
"Protocols prefer app_protocol and fall back to lower-layer protocol names.",
]
)
@router.get("/interface-hosts", response_model=InterfaceHostAnalysisResponse)
async def analysis_interface_hosts(
since_minutes: Optional[int] = Query(
@@ -159,3 +189,39 @@ async def analysis_interface_host_protocols(
interfaces = [InterfaceProtocolAttachment(**row) for row in rows]
return InterfaceHostProtocolAnalysisResponse(since=since, interfaces=interfaces)
@router.get("/interface-protocol-paths", response_model=InterfaceProtocolPathAnalysisResponse)
async def analysis_interface_protocol_paths(
since_minutes: Optional[int] = Query(
None,
ge=1,
le=60 * 24 * 30,
description="Analyze only packets seen within the last N minutes. Omit to cover all captured history.",
),
limit_paths: int = Query(
500,
ge=1,
le=5000,
description="Maximum number of grouped packet paths returned for the Sankey view.",
),
) -> InterfaceProtocolPathAnalysisResponse:
"""Aggregate directional packet paths for the Sankey diagram."""
db = shared.db
if db is None:
raise HTTPException(status_code=503, detail="Database not available")
since: Optional[datetime] = None
if since_minutes is not None:
since = datetime.now(timezone.utc) - timedelta(minutes=since_minutes)
try:
rows = await db.infer_interface_protocol_paths(
since=since,
limit_paths=limit_paths,
)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Failed to infer interface protocol paths: {exc}") from exc
paths = [InterfaceProtocolPathEvidence(**row) for row in rows]
return InterfaceProtocolPathAnalysisResponse(since=since, paths=paths)