feat: replace tool attribution stat with dedicated DETECTED TOOLS block
This commit is contained in:
@@ -435,27 +435,8 @@ const KeyValueRow: React.FC<{ label: string; value: React.ReactNode }> = ({ labe
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ToolBadges: React.FC<{ tools: string[] }> = ({ tools }) => (
|
// Tools detected via beacon timing (C2 frameworks).
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
|
const _C2_TOOLS = new Set(['cobalt_strike', 'sliver', 'havoc', 'mythic']);
|
||||||
{tools.map(t => (
|
|
||||||
<span
|
|
||||||
key={t}
|
|
||||||
style={{
|
|
||||||
fontSize: '0.7rem',
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
color: '#ff6b6b',
|
|
||||||
border: '1px solid #ff6b6b44',
|
|
||||||
borderRadius: '2px',
|
|
||||||
padding: '1px 5px',
|
|
||||||
letterSpacing: '0.5px',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{TOOL_LABELS[t] || t.toUpperCase()}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const BehaviorHeadline: React.FC<{ b: AttackerBehavior }> = ({ b }) => {
|
const BehaviorHeadline: React.FC<{ b: AttackerBehavior }> = ({ b }) => {
|
||||||
const osLabel = b.os_guess ? (OS_LABELS[b.os_guess] || b.os_guess.toUpperCase()) : '—';
|
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())
|
? (BEHAVIOR_LABELS[b.behavior_class] || b.behavior_class.toUpperCase())
|
||||||
: 'UNKNOWN';
|
: 'UNKNOWN';
|
||||||
const behaviorColor = b.behavior_class ? BEHAVIOR_COLORS[b.behavior_class] : undefined;
|
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 (
|
return (
|
||||||
<div className="stats-grid" style={{ gridTemplateColumns: 'repeat(4, 1fr)' }}>
|
<div className="stats-grid" style={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
||||||
<StatBlock label="OS GUESS" value={osLabel} />
|
<StatBlock label="OS GUESS" value={osLabel} />
|
||||||
<StatBlock label="HOP DISTANCE" value={fmtOpt(b.hop_distance)} />
|
<StatBlock label="HOP DISTANCE" value={fmtOpt(b.hop_distance)} />
|
||||||
<StatBlock label="ATTACK PATTERN" value={behaviorLabel} color={behaviorColor} />
|
<StatBlock label="ATTACK PATTERN" value={behaviorLabel} color={behaviorColor} />
|
||||||
<StatBlock
|
</div>
|
||||||
label="TOOL ATTRIBUTION"
|
);
|
||||||
value={tools ? <ToolBadges tools={tools} /> : '—'}
|
};
|
||||||
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 (
|
||||||
|
<div style={{ border: '1px solid var(--border-color)', padding: '12px 16px' }}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '10px' }}>
|
||||||
|
<Crosshair size={14} style={{ opacity: 0.6 }} />
|
||||||
|
<span style={{ fontSize: '0.75rem', letterSpacing: '2px', fontWeight: 'bold' }}>
|
||||||
|
DETECTED TOOLS
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
||||||
|
{tools.map(t => (
|
||||||
|
<div key={t} style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#ff6b6b',
|
||||||
|
minWidth: '160px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{TOOL_LABELS[t] || t.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: '0.65rem',
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
letterSpacing: '1px',
|
||||||
|
color: 'var(--dim-color)',
|
||||||
|
border: '1px solid var(--border-color)',
|
||||||
|
borderRadius: '2px',
|
||||||
|
padding: '1px 6px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{_C2_TOOLS.has(t) ? 'BEACON TIMING' : 'HTTP HEADER'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -885,6 +905,7 @@ const AttackerDetail: React.FC = () => {
|
|||||||
<BehaviorHeadline b={attacker.behavior} />
|
<BehaviorHeadline b={attacker.behavior} />
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
||||||
<BeaconBlock b={attacker.behavior} />
|
<BeaconBlock b={attacker.behavior} />
|
||||||
|
<DetectedToolsBlock b={attacker.behavior} />
|
||||||
<TcpStackBlock b={attacker.behavior} />
|
<TcpStackBlock b={attacker.behavior} />
|
||||||
<TimingStatsBlock b={attacker.behavior} />
|
<TimingStatsBlock b={attacker.behavior} />
|
||||||
<PhaseSequenceBlock b={attacker.behavior} />
|
<PhaseSequenceBlock b={attacker.behavior} />
|
||||||
|
|||||||
Reference in New Issue
Block a user