import React, { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { ArrowLeft, Crosshair, Fingerprint, Shield, Clock, Wifi, Lock, FileKey } from 'lucide-react'; import api from '../utils/api'; import './Dashboard.css'; interface AttackerData { uuid: string; ip: string; first_seen: string; last_seen: string; event_count: number; service_count: number; decky_count: number; services: string[]; deckies: string[]; traversal_path: string | null; is_traversal: boolean; bounty_count: number; credential_count: number; fingerprints: any[]; commands: { service: string; decky: string; command: string; timestamp: string }[]; updated_at: string; } // ─── Fingerprint rendering ─────────────────────────────────────────────────── const fpTypeLabel: Record = { ja3: 'TLS FINGERPRINT', ja4l: 'LATENCY (JA4L)', tls_resumption: 'SESSION RESUMPTION', tls_certificate: 'CERTIFICATE', http_useragent: 'HTTP USER-AGENT', vnc_client_version: 'VNC CLIENT', }; const fpTypeIcon: Record = { ja3: , ja4l: , tls_resumption: , tls_certificate: , http_useragent: , vnc_client_version: , }; function getPayload(bounty: any): any { if (bounty?.payload && typeof bounty.payload === 'object') return bounty.payload; if (bounty?.payload && typeof bounty.payload === 'string') { try { return JSON.parse(bounty.payload); } catch { return bounty; } } return bounty; } const HashRow: React.FC<{ label: string; value?: string | null }> = ({ label, value }) => { if (!value) return null; return (
{label} {value}
); }; const Tag: React.FC<{ children: React.ReactNode; color?: string }> = ({ children, color }) => ( {children} ); const FpTlsHashes: React.FC<{ p: any }> = ({ p }) => (
{(p.tls_version || p.sni || p.alpn) && (
{p.tls_version && {p.tls_version}} {p.sni && SNI: {p.sni}} {p.alpn && ALPN: {p.alpn}} {p.dst_port && :{p.dst_port}}
)}
); const FpLatency: React.FC<{ p: any }> = ({ p }) => (
RTT {p.rtt_ms} ms
{p.client_ttl && (
TTL {p.client_ttl}
)}
); const FpResumption: React.FC<{ p: any }> = ({ p }) => { const mechanisms = typeof p.mechanisms === 'string' ? p.mechanisms.split(',') : Array.isArray(p.mechanisms) ? p.mechanisms : []; return (
{mechanisms.map((m: string) => ( {m.trim().toUpperCase().replace(/_/g, ' ')} ))}
); }; const FpCertificate: React.FC<{ p: any }> = ({ p }) => (
{p.subject_cn} {p.self_signed === 'true' && ( SELF-SIGNED )}
{p.issuer && (
ISSUER: {p.issuer}
)} {(p.not_before || p.not_after) && (
VALIDITY: {p.not_before || '?'} — {p.not_after || '?'}
)} {p.sans && (
SANs: {(typeof p.sans === 'string' ? p.sans.split(',') : p.sans).map((san: string) => ( {san.trim()} ))}
)}
); const FpGeneric: React.FC<{ p: any }> = ({ p }) => (
{p.value ? ( {p.value} ) : ( {JSON.stringify(p)} )}
); const FingerprintCard: React.FC<{ bounty: any }> = ({ bounty }) => { const p = getPayload(bounty); const fpType: string = p.fingerprint_type || 'unknown'; const label = fpTypeLabel[fpType] || fpType.toUpperCase().replace(/_/g, ' '); const icon = fpTypeIcon[fpType] || ; let content: React.ReactNode; switch (fpType) { case 'ja3': content = ; break; case 'ja4l': content = ; break; case 'tls_resumption': content = ; break; case 'tls_certificate': content = ; break; default: content = ; } return (
{icon} {label}
{content}
); }; // ─── Main component ───────────────────────────────────────────────────────── const AttackerDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [attacker, setAttacker] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchAttacker = async () => { setLoading(true); try { const res = await api.get(`/attackers/${id}`); setAttacker(res.data); } catch (err: any) { if (err.response?.status === 404) { setError('ATTACKER NOT FOUND'); } else { setError('FAILED TO LOAD ATTACKER PROFILE'); } } finally { setLoading(false); } }; fetchAttacker(); }, [id]); if (loading) { return (
LOADING THREAT PROFILE...
); } if (error || !attacker) { return (
{error || 'ATTACKER NOT FOUND'}
); } return (
{/* Back Button */} {/* Header */}

{attacker.ip}

{attacker.is_traversal && ( TRAVERSAL )}
{/* Stats Row */}
{attacker.event_count}
EVENTS
{attacker.bounty_count}
BOUNTIES
{attacker.credential_count}
CREDENTIALS
{attacker.service_count}
SERVICES
{attacker.decky_count}
DECKIES
{/* Timestamps */}

TIMELINE

FIRST SEEN: {new Date(attacker.first_seen).toLocaleString()}
LAST SEEN: {new Date(attacker.last_seen).toLocaleString()}
UPDATED: {new Date(attacker.updated_at).toLocaleString()}
{/* Services */}

SERVICES TARGETED

{attacker.services.length > 0 ? attacker.services.map((svc) => ( navigate(`/attackers?service=${encodeURIComponent(svc)}`)} title={`Filter attackers by ${svc.toUpperCase()}`} > {svc.toUpperCase()} )) : ( No services recorded )}
{/* Deckies & Traversal */}

DECKY INTERACTIONS

{attacker.traversal_path ? (
TRAVERSAL PATH: {attacker.traversal_path}
) : (
{attacker.deckies.map((d) => ( {d} ))} {attacker.deckies.length === 0 && No deckies recorded}
)}
{/* Commands */}

COMMANDS ({attacker.commands.length})

{attacker.commands.length > 0 ? (
{attacker.commands.map((cmd, i) => ( ))}
TIMESTAMP SERVICE DECKY COMMAND
{cmd.timestamp ? new Date(cmd.timestamp).toLocaleString() : '-'} {cmd.service} {cmd.decky} {cmd.command}
) : (
NO COMMANDS CAPTURED
)}
{/* Fingerprints */}

FINGERPRINTS ({attacker.fingerprints.length})

{attacker.fingerprints.length > 0 ? (
{attacker.fingerprints.map((fp, i) => ( ))}
) : (
NO FINGERPRINTS CAPTURED
)}
{/* UUID footer */}
UUID: {attacker.uuid}
); }; export default AttackerDetail;