import { memo, useEffect, useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { api } from '../../api/client'; import { HelpTip } from '../HelpTip'; import { FLEET_SPREAD_PORTS, PORT_BUNDLES, RECON_PORT_HINTS, SCAN_PROFILES, crucibleSpreadLink, curlInstallLine, type PortBundleId, type ScanProfileId, } from '../../help/deployRecon'; import type { ReconActionRow, ReconFormFingerprintCard, } from '../../help/deployRecon'; import { buildAdminSurfaceMap, buildFormFingerprintCards, buildTechStackHints, type AdminSurfaceMapRow, } from '../../help/deployRecon'; import type { ReconPortBanner, ReconPortResult, ReconScanReport, ReconSsrfCanaryInfo, ReconStackEntry, ReconWebFindingCard, } from '../../types/recon'; import { ssrfCanaryToken } from '../../help/deployRecon'; function CopyChip({ text, label }: { text: string; label: string }) { const [ok, setOk] = useState(false); const copy = () => { void navigator.clipboard?.writeText(text).then(() => { setOk(true); setTimeout(() => setOk(false), 1500); }).catch(() => {}); }; return ( ); } export const ScanProfileSelector = memo(function ScanProfileSelector({ value, onChange, }: { value: ScanProfileId; onChange: (v: ScanProfileId) => void; }) { return (
Scan profile
{(Object.keys(SCAN_PROFILES) as ScanProfileId[]).map((id) => ( ))}
); }); export const PortBundleSelector = memo(function PortBundleSelector({ value, onChange, }: { value: PortBundleId; onChange: (v: PortBundleId) => void; }) { return (
Port bundle
); }); export const StreamingPortMatrix = memo(function StreamingPortMatrix({ ports, streaming, onHint, }: { ports: ReconPortResult[]; streaming?: boolean; onHint: (hint: string) => void; }) { return (

Port matrix

{ports.map((p) => ( ))}
); }); export const BannerGrabPanel = memo(function BannerGrabPanel({ banners }: { banners: ReconPortBanner[] }) { if (!banners?.length) return null; return (

Banner grab

); }); export const FormFingerprintCards = memo(function FormFingerprintCards({ cards, }: { cards: ReconFormFingerprintCard[]; }) { if (cards.length === 0) return null; return (

Form fingerprints

{cards.map((c) => (
{c.name} score {c.score}

{c.page_url}

{c.matches.length ?

{c.matches.join(' · ')}

: null}
))}
); }); export const SsrfCanaryPanel = memo(function SsrfCanaryPanel({ canary, scanId, wsHitScanId, }: { canary?: ReconSsrfCanaryInfo; scanId?: string; wsHitScanId?: string | null; }) { const token = ssrfCanaryToken(canary?.scan_id || scanId || ''); const [status, setStatus] = useState(canary?.status ?? 'pending'); const [hitAt, setHitAt] = useState(canary?.hit_at); useEffect(() => { if (canary?.status) setStatus(canary.status); if (canary?.hit_at) setHitAt(canary.hit_at); }, [canary]); useEffect(() => { if (wsHitScanId && token && wsHitScanId === token) { setStatus('confirmed'); setHitAt(new Date().toISOString()); } }, [wsHitScanId, token]); useEffect(() => { if (!token || status === 'confirmed') return undefined; const timer = setInterval(() => { void api.getSsrfCanaryStatus(token).then((info) => { if (!info) return; setStatus(info.status); if (info.hit_at) setHitAt(info.hit_at); }); }, 4000); return () => clearInterval(timer); }, [token, status]); const url = canary?.url; if (!url && !token) return null; return (

SSRF canary {status}

{url ? : null} {hitAt ?

Hit at {hitAt}

: null} {canary?.paste_field_name ? (

Paste into field: {canary.paste_field_name}

) : null}
); }); export const UploadAdminMap = memo(function UploadAdminMap({ rows }: { rows: AdminSurfaceMapRow[] }) { if (!rows.length) return null; return (

Upload / admin map

{rows.map((r) => { const live = r.status_code >= 200 && r.status_code < 400; return ( ); })}
Path Status Signal
{r.path} {r.status_code || '—'} {r.signal}
); }); export const TechStackBanner = memo(function TechStackBanner({ stack }: { stack: ReconStackEntry[] }) { if (!stack.length) return null; return (

Tech stack

); }); export const ActionMatrix = memo(function ActionMatrix({ rows, onOpenPlaybook, onAction, }: { rows: ReconActionRow[]; onOpenPlaybook?: (lane: string) => void; onAction?: (lane: string, action: string) => void; }) { if (!rows.length) return null; return (

Action matrix

{rows.map((r) => ( ))}
Lane Reason Actions
{r.title} {r.reason}
{r.actions.map((a) => ( ))}
); }); export const FindingCard = memo(function FindingCard({ card, curlLine, host, fleetSpreadOpen, deployKitLane, }: { card: ReconWebFindingCard; curlLine: string; host: string; fleetSpreadOpen: boolean; deployKitLane?: string; }) { const finding = deployKitLane || card.spread_lane; return (
{card.title} {card.confidence} {card.spread_lane ? {card.spread_lane} : null}

{card.detail}

{card.mermaid ?
{card.mermaid}
: null}
{card.probe_url ? : null} {fleetSpreadOpen ? ( Fleet spread to {host} ) : null}
); }); export function useDeployReconDerived(report: ReconScanReport | null, deckOrigin: string) { return useMemo(() => { if (!report) { return { fingerprintCards: [] as ReconFormFingerprintCard[], adminRows: [] as AdminSurfaceMapRow[], stack: [] as ReconStackEntry[], }; } return { fingerprintCards: buildFormFingerprintCards(report, deckOrigin), adminRows: buildAdminSurfaceMap(report), stack: buildTechStackHints(report), }; }, [report, deckOrigin]); }