Files
mitm-webserver/frontend/src/components/analysis/IntelligenceRiskViews.tsx
malmert 74b961e152
All checks were successful
Build and Deploy MITM Webserver / traffic_target (push) Successful in 1s
Build and Deploy MITM Webserver / build (push) Successful in 11s
add visualizations, better scaling
2026-04-01 23:10:59 +02:00

105 lines
4.6 KiB
TypeScript

import { Card, Drawer, Space, Tag, Typography } from 'antd';
import type { HostIntelligenceEvidence } from '../../types/analysis';
import {
endpointText,
formatBytes,
formatTimestamp,
protocolColor,
renderLabelTags,
} from './shared.tsx';
const { Text } = Typography;
export function HostDetailDrawer({
host,
open,
onClose,
}: {
host: HostIntelligenceEvidence | null;
open: boolean;
onClose: () => void;
}) {
return (
<Drawer title={host ? endpointText(host.ip_address, host.mac_address) : 'Host details'} placement="right" width={520} open={open} onClose={onClose}>
{host == null ? null : (
<Space direction="vertical" size={16} style={{ width: '100%' }}>
<Card size="small" title="Identity">
<Space direction="vertical" size={8} style={{ width: '100%' }}>
<Text>IP: {host.ip_address ?? '—'}</Text>
<Text>MAC: {host.mac_address ?? '—'}</Text>
<Text>Interfaces: {host.interfaces.join(', ') || '—'}</Text>
<Text>First seen: {formatTimestamp(host.first_seen)} | Last seen: {formatTimestamp(host.last_seen)}</Text>
<div>{renderLabelTags(host.hostnames, 'geekblue')}</div>
</Space>
</Card>
<Card size="small" title="Protocol Profile">
<Space direction="vertical" size={10} style={{ width: '100%' }}>
{host.top_protocols.length === 0 ? (
<Text type="secondary">No protocol profile available yet.</Text>
) : (
host.top_protocols.map((protocol) => {
const maxCount = Math.max(...host.top_protocols.map((item) => item.packet_count), 1);
const widthPct = (protocol.packet_count / maxCount) * 100;
return (
<div key={protocol.label}>
<Space style={{ width: '100%', justifyContent: 'space-between' }}>
<Tag color={protocolColor(protocol.label)}>{protocol.label}</Tag>
<Text>{protocol.packet_count}</Text>
</Space>
<div style={{ height: 8, background: '#eef3f8', borderRadius: 999, overflow: 'hidden' }}>
<div style={{ width: `${widthPct}%`, height: '100%', background: protocolColor(protocol.label) }} />
</div>
</div>
);
})
)}
</Space>
</Card>
<Card size="small" title="Top Peers">
<Space direction="vertical" size={10} style={{ width: '100%' }}>
{host.peers.length === 0 ? (
<Text type="secondary">No peer details available yet.</Text>
) : (
host.peers.map((peer) => {
const maxCount = Math.max(...host.peers.map((item) => item.packet_count), 1);
const widthPct = (peer.packet_count / maxCount) * 100;
return (
<div key={`${peer.ip_address ?? 'no-ip'}|${peer.mac_address ?? 'no-mac'}`}>
<Space direction="vertical" size={4} style={{ width: '100%' }}>
<Text>{endpointText(peer.ip_address, peer.mac_address)}</Text>
<Text type="secondary">{peer.packet_count} packets · {formatBytes(peer.byte_count)} · {formatTimestamp(peer.last_seen)}</Text>
<div style={{ height: 8, background: '#eef3f8', borderRadius: 999, overflow: 'hidden' }}>
<div style={{ width: `${widthPct}%`, height: '100%', background: '#5b8ff9' }} />
</div>
<div>{renderLabelTags(peer.protocols, 'purple')}</div>
</Space>
</div>
);
})
)}
</Space>
</Card>
<Card size="small" title="Likely Services">
<Space direction="vertical" size={10} style={{ width: '100%' }}>
{host.services.length === 0 ? (
<Text type="secondary">No service evidence inferred yet.</Text>
) : (
host.services.map((service) => (
<div key={`${service.port ?? 'no-port'}|${service.protocol}`}>
<Text>Port {service.port ?? '—'} · {service.protocol} · {service.packet_count} packets · {formatBytes(service.byte_count)}</Text>
<div style={{ marginTop: 4 }}>{renderLabelTags(service.hostnames, 'geekblue')}</div>
</div>
))
)}
</Space>
</Card>
</Space>
)}
</Drawer>
);
}