Skip to content

Commit d3cb20c

Browse files
committed
attack dist fix
1 parent 1a34757 commit d3cb20c

File tree

1 file changed

+30
-89
lines changed

1 file changed

+30
-89
lines changed

components/AttackDistributionChart.tsx

Lines changed: 30 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -63,64 +63,41 @@ export function AttackDistributionChart({
6363
const { totalAttacks, attackLabels, attackCounts, metrics } = useMemo(() => {
6464
console.log('AttackDistributionChart - Raw data:', data);
6565

66-
// Define all possible attack types to maintain consistent chart structure
67-
const allAttackTypes = [
68-
"SQL Injection",
69-
"XSS",
70-
"Command Injection",
71-
"Directory Traversal",
72-
"Brute Force"
73-
];
66+
// When there's an active filter, we want to show all attack types but highlight the filtered one
67+
// The parent component has already filtered the data, so we just need to work with what we have
68+
const displayData = { ...data };
7469

75-
// When there's an active filter, the parent has set non-matching types to 0
76-
// We still want to show all categories to maintain the radar chart structure
77-
const displayData: Record<string, number> = {};
70+
const total = Object.values(displayData).reduce((sum, value) => sum + (Number(value) || 0), 0);
71+
console.log('AttackDistributionChart - Total attacks:', total);
7872

79-
// Initialize all attack types with their values (or 0 if not present)
80-
allAttackTypes.forEach(type => {
81-
displayData[type] = data[type] || 0;
82-
});
83-
84-
// Calculate total based on the original data, not the display data with zeros
85-
const actualTotal = Object.values(data).reduce((sum, value) => sum + (Number(value) || 0), 0);
86-
console.log('AttackDistributionChart - Total attacks:', actualTotal);
87-
88-
if (!data || Object.keys(data).length === 0) {
73+
if (!data || Object.keys(displayData).length === 0) {
8974
console.log('AttackDistributionChart - No data or zero total attacks');
9075
return { totalAttacks: 0, attackLabels: [], attackCounts: [], metrics: [] };
9176
}
9277

93-
// Create arrays maintaining the order of allAttackTypes
94-
// but put the active filter first if it exists
95-
const labels: string[] = [];
96-
const counts: number[] = [];
97-
98-
// Add active filter first if it exists
99-
if (activeFilter && allAttackTypes.includes(activeFilter)) {
100-
labels.push(activeFilter);
101-
counts.push(displayData[activeFilter]);
102-
}
103-
104-
// Add remaining attack types
105-
allAttackTypes.forEach(type => {
106-
if (type !== activeFilter) {
107-
labels.push(type);
108-
counts.push(displayData[type]);
78+
// Sort all data but put the active filter first if it exists
79+
const sorted = Object.entries(displayData).sort(([aKey, aVal], [bKey, bVal]) => {
80+
// If we have an active filter, put it first
81+
if (activeFilter) {
82+
if (aKey === activeFilter) return -1;
83+
if (bKey === activeFilter) return 1;
10984
}
85+
// Otherwise sort by count descending
86+
return bVal - aVal;
11087
});
11188

112-
// Find top threat (excluding zero values)
113-
const nonZeroEntries = Object.entries(data).filter(([, value]) => value > 0);
114-
const sortedNonZero = nonZeroEntries.sort(([, a], [, b]) => b - a);
115-
const [topKey, topValue] = sortedNonZero[0] || [];
116-
const topPct = topValue ? (topValue / actualTotal) * 100 : 0;
89+
const labels = sorted.map(([key]) => key);
90+
const counts = sorted.map(([, value]) => value);
91+
92+
const [topKey, topValue] = sorted[0] || [];
93+
const topPct = topValue ? (topValue / total) * 100 : 0;
11794

11895
return {
119-
totalAttacks: actualTotal,
96+
totalAttacks: total,
12097
attackLabels: labels,
12198
attackCounts: counts,
12299
metrics: [
123-
{ title: "Total Attacks", value: actualTotal.toLocaleString(), icon: ShieldCheck, color: "red" },
100+
{ title: "Total Attacks", value: total.toLocaleString(), icon: ShieldCheck, color: "red" },
124101
{
125102
title: "Top Threat",
126103
value: topKey || "None",
@@ -132,7 +109,7 @@ export function AttackDistributionChart({
132109
},
133110
{
134111
title: "Attack Varieties",
135-
value: nonZeroEntries.length,
112+
value: labels.length,
136113
icon: ListTree,
137114
color: "purple",
138115
subtitle: "Unique attack types detected",
@@ -152,41 +129,17 @@ export function AttackDistributionChart({
152129
backgroundColor: isDarkMode ? 'rgba(255, 0, 0, 0.2)' : 'rgba(220, 38, 38, 0.15)',
153130
borderColor: isDarkMode ? 'rgba(255, 50, 50, 1)' : 'rgba(220, 38, 38, 1)',
154131
borderWidth: isDarkMode ? 4 : 3,
155-
pointBackgroundColor: attackLabels.map((label, index) => {
156-
// Highlight the active filter with a different color
157-
if (activeFilter && label === activeFilter) {
158-
return isDarkMode ? 'rgba(255, 215, 0, 1)' : 'rgba(255, 215, 0, 1)'; // Gold color for active filter
159-
}
160-
return isDarkMode ? 'rgba(255, 100, 100, 1)' : 'rgba(220, 38, 38, 1)';
161-
}),
132+
pointBackgroundColor: isDarkMode ? 'rgba(255, 100, 100, 1)' : 'rgba(220, 38, 38, 1)',
162133
pointBorderColor: isDarkMode ? 'rgba(255, 200, 200, 1)' : '#fff',
163-
pointHoverBackgroundColor: attackLabels.map((label, index) => {
164-
// Highlight the active filter with a different hover color
165-
if (activeFilter && label === activeFilter) {
166-
return isDarkMode ? 'rgba(255, 215, 0, 1)' : 'rgba(255, 215, 0, 1)'; // Gold color for active filter
167-
}
168-
return isDarkMode ? 'rgba(255, 0, 0, 1)' : '#fff';
169-
}),
134+
pointHoverBackgroundColor: isDarkMode ? 'rgba(255, 0, 0, 1)' : '#fff',
170135
pointHoverBorderColor: isDarkMode ? 'rgba(255, 255, 255, 1)' : 'rgba(220, 38, 38, 1)',
171-
pointRadius: attackLabels.map((label, index) => {
172-
// Make the active filter point larger
173-
if (activeFilter && label === activeFilter) {
174-
return isDarkMode ? 9 : 8;
175-
}
176-
return isDarkMode ? 7 : 6;
177-
}),
178-
pointHoverRadius: attackLabels.map((label, index) => {
179-
// Make the active filter point larger on hover
180-
if (activeFilter && label === activeFilter) {
181-
return isDarkMode ? 11 : 10;
182-
}
183-
return isDarkMode ? 9 : 8;
184-
}),
136+
pointRadius: isDarkMode ? 7 : 6,
137+
pointHoverRadius: isDarkMode ? 9 : 8,
185138
// Add glow effect through shadow
186139
pointStyle: 'circle',
187140
},
188141
],
189-
}), [attackLabels, attackCounts, isDarkMode, activeFilter]);
142+
}), [attackLabels, attackCounts, isDarkMode]);
190143

191144
const options: ChartOptions<"radar"> = {
192145
responsive: true,
@@ -218,13 +171,7 @@ export function AttackDistributionChart({
218171
cornerRadius: 8,
219172
displayColors: false,
220173
callbacks: {
221-
title: (ctx) => {
222-
const label = ctx[0].label as string;
223-
if (activeFilter && label === activeFilter) {
224-
return `Attack Type: ${label} (FILTERED)`;
225-
}
226-
return `Attack Type: ${label}`;
227-
},
174+
title: (ctx) => `Attack Type: ${ctx[0].label}`,
228175
label: (ctx) => {
229176
const count = ctx.raw as number;
230177
const pct = totalAttacks > 0 ? ((count / totalAttacks) * 100).toFixed(1) : 0;
@@ -258,13 +205,9 @@ export function AttackDistributionChart({
258205
},
259206
};
260207

261-
// Check if we have any non-zero values in the original data
262208
const hasNoData = Object.values(data).every(val => val === 0 || val === undefined);
263209

264-
// But if we have an active filter that exists in the data, we should show the chart
265-
const shouldShowChart = !hasNoData || (activeFilter && data[activeFilter] > 0);
266-
267-
if (!shouldShowChart) {
210+
if (hasNoData) {
268211
return (
269212
<div className={className}>
270213
<div className="p-6 pb-4 border-b border-gray-200/50 dark:border-gray-700/50">
@@ -307,9 +250,7 @@ export function AttackDistributionChart({
307250
<span>Attack Distribution</span>
308251
</h2>
309252
<p className="text-gray-600 dark:text-gray-400 mt-1">
310-
{activeFilter
311-
? `Distribution of ${totalAttacks.toLocaleString()} threats. Showing filtered view for "${activeFilter}".`
312-
: `Distribution of ${totalAttacks.toLocaleString()} threats. Click a point to filter.`}
253+
Distribution of {totalAttacks.toLocaleString()} threats. Click a point to filter.
313254
</p>
314255
</div>
315256

0 commit comments

Comments
 (0)