-
{icon}
-
{label}
+
+
+ {icon}
+ {label}
+ {items.length > 1 && (
+ ({items.length})
+ )}
+
+
+ {items.map((fp, i) => {
+ const p = getPayload(fp);
+ switch (fpType) {
+ case 'ja3': return ;
+ case 'ja4l': return ;
+ case 'tls_resumption': return ;
+ case 'tls_certificate': return ;
+ case 'jarm': return ;
+ case 'hassh_server': return ;
+ case 'tcpfp': return ;
+ default: return ;
+ }
+ })}
-
{content}
);
};
@@ -591,7 +582,7 @@ const AttackerDetail: React.FC = () => {
);
})()}
- {/* Fingerprints */}
+ {/* Fingerprints — grouped by type */}
{(() => {
const filteredFps = serviceFilter
? attacker.fingerprints.filter((fp) => {
@@ -599,16 +590,62 @@ const AttackerDetail: React.FC = () => {
return p.service === serviceFilter;
})
: attacker.fingerprints;
+
+ // Group fingerprints by type
+ const groups: Record
= {};
+ filteredFps.forEach((fp) => {
+ const p = getPayload(fp);
+ const fpType: string = p.fingerprint_type || 'unknown';
+ if (!groups[fpType]) groups[fpType] = [];
+ groups[fpType].push(fp);
+ });
+
+ // Active probes first, then passive, then unknown
+ const activeTypes = ['jarm', 'hassh_server', 'tcpfp'];
+ const passiveTypes = ['ja3', 'ja4l', 'tls_resumption', 'tls_certificate', 'http_useragent', 'vnc_client_version'];
+ const knownTypes = [...activeTypes, ...passiveTypes];
+ const unknownTypes = Object.keys(groups).filter((t) => !knownTypes.includes(t));
+ const orderedTypes = [...activeTypes, ...passiveTypes, ...unknownTypes].filter((t) => groups[t]);
+
+ const hasActive = activeTypes.some((t) => groups[t]);
+ const hasPassive = [...passiveTypes, ...unknownTypes].some((t) => groups[t]);
+
return (
FINGERPRINTS ({filteredFps.length}{serviceFilter ? ` / ${attacker.fingerprints.length}` : ''})
{filteredFps.length > 0 ? (
-
- {filteredFps.map((fp, i) => (
-
- ))}
+
+ {/* Active probes section */}
+ {hasActive && (
+
+
+
+ ACTIVE PROBES
+
+
+ {activeTypes.filter((t) => groups[t]).map((fpType) => (
+
+ ))}
+
+
+ )}
+
+ {/* Passive fingerprints section */}
+ {hasPassive && (
+
+
+
+ PASSIVE FINGERPRINTS
+
+
+ {[...passiveTypes, ...unknownTypes].filter((t) => groups[t]).map((fpType) => (
+
+ ))}
+
+
+ )}
) : (