proxy_list_tls_fingerprints
List unique TLS client fingerprints (JA3/JA4) from captured network traffic with occurrence counts to identify and analyze client applications.
Instructions
List unique client JA3/JA4 fingerprints across captured traffic with occurrence counts.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max fingerprints to return (default: 20) | |
| hostname_filter | No | Filter by hostname substring |
Implementation Reference
- src/tools/tls.ts:42-109 (handler)Handler for the "proxy_list_tls_fingerprints" tool, which aggregates and lists client JA3/JA4 fingerprints from captured traffic.
server.tool( "proxy_list_tls_fingerprints", "List unique client JA3/JA4 fingerprints across captured traffic with occurrence counts.", { limit: z.number().optional().default(20).describe("Max fingerprints to return (default: 20)"), hostname_filter: z.string().optional().describe("Filter by hostname substring"), }, async ({ limit, hostname_filter }) => { let traffic = proxyManager.getTraffic(); if (hostname_filter) { const h = hostname_filter.toLowerCase(); traffic = traffic.filter((t) => t.request.hostname.toLowerCase().includes(h)); } // Aggregate JA3 fingerprints const ja3Counts = new Map<string, { count: number; hostnames: Set<string> }>(); const ja4Counts = new Map<string, { count: number; hostnames: Set<string> }>(); for (const t of traffic) { if (t.tls?.client?.ja3Fingerprint) { const fp = t.tls.client.ja3Fingerprint; const entry = ja3Counts.get(fp) || { count: 0, hostnames: new Set() }; entry.count++; entry.hostnames.add(t.request.hostname); ja3Counts.set(fp, entry); } if (t.tls?.client?.ja4Fingerprint) { const fp = t.tls.client.ja4Fingerprint; const entry = ja4Counts.get(fp) || { count: 0, hostnames: new Set() }; entry.count++; entry.hostnames.add(t.request.hostname); ja4Counts.set(fp, entry); } } // Sort by count descending const ja3List = [...ja3Counts.entries()] .sort((a, b) => b[1].count - a[1].count) .slice(0, limit) .map(([fp, { count, hostnames }]) => ({ fingerprint: fp, count, hostnames: [...hostnames].slice(0, 5), })); const ja4List = [...ja4Counts.entries()] .sort((a, b) => b[1].count - a[1].count) .slice(0, limit) .map(([fp, { count, hostnames }]) => ({ fingerprint: fp, count, hostnames: [...hostnames].slice(0, 5), })); return { content: [{ type: "text" as const, text: truncateResult({ status: "success", totalExchangesWithTls: traffic.filter((t) => t.tls?.client).length, ja3: ja3List, ja4: ja4List, }), }], }; }, );