diff --git a/decnet_web/src/components/AttackerDetail.tsx b/decnet_web/src/components/AttackerDetail.tsx
index 3c7d5e7..3c8eda6 100644
--- a/decnet_web/src/components/AttackerDetail.tsx
+++ b/decnet_web/src/components/AttackerDetail.tsx
@@ -435,27 +435,8 @@ const KeyValueRow: React.FC<{ label: string; value: React.ReactNode }> = ({ labe
);
-const ToolBadges: React.FC<{ tools: string[] }> = ({ tools }) => (
-
- {tools.map(t => (
-
- {TOOL_LABELS[t] || t.toUpperCase()}
-
- ))}
-
-);
+// Tools detected via beacon timing (C2 frameworks).
+const _C2_TOOLS = new Set(['cobalt_strike', 'sliver', 'havoc', 'mythic']);
const BehaviorHeadline: React.FC<{ b: AttackerBehavior }> = ({ b }) => {
const osLabel = b.os_guess ? (OS_LABELS[b.os_guess] || b.os_guess.toUpperCase()) : '—';
@@ -463,17 +444,56 @@ const BehaviorHeadline: React.FC<{ b: AttackerBehavior }> = ({ b }) => {
? (BEHAVIOR_LABELS[b.behavior_class] || b.behavior_class.toUpperCase())
: 'UNKNOWN';
const behaviorColor = b.behavior_class ? BEHAVIOR_COLORS[b.behavior_class] : undefined;
- const tools = b.tool_guesses && b.tool_guesses.length > 0 ? b.tool_guesses : null;
return (
-
+
- : '—'}
- color={tools ? '#ff6b6b' : undefined}
- />
+
+ );
+};
+
+const DetectedToolsBlock: React.FC<{ b: AttackerBehavior }> = ({ b }) => {
+ const tools = b.tool_guesses && b.tool_guesses.length > 0 ? b.tool_guesses : null;
+ if (!tools) return null;
+ return (
+
+
+
+
+ DETECTED TOOLS
+
+
+
+ {tools.map(t => (
+
+
+ {TOOL_LABELS[t] || t.toUpperCase()}
+
+
+ {_C2_TOOLS.has(t) ? 'BEACON TIMING' : 'HTTP HEADER'}
+
+
+ ))}
+
);
};
@@ -885,6 +905,7 @@ const AttackerDetail: React.FC = () => {