-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
954 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { | ||
Chart as ChartJS, | ||
Filler, | ||
Legend, | ||
LineElement, | ||
PointElement, | ||
RadialLinearScale, | ||
Tooltip, | ||
} from 'chart.js'; | ||
import { t } from 'i18next'; | ||
import React from 'react'; | ||
import { Radar } from 'react-chartjs-2'; | ||
|
||
ChartJS.register( | ||
RadialLinearScale, | ||
PointElement, | ||
LineElement, | ||
Filler, | ||
Tooltip, | ||
Legend, | ||
); | ||
|
||
type CIAData = { | ||
subject: string; | ||
current: 0 | 1 | 2; | ||
target: 0 | 1 | 2; | ||
}; | ||
|
||
type Props = { | ||
data: CIAData[]; | ||
}; | ||
|
||
export const CIATriadChart: React.FC<Props> = ({ data }) => { | ||
if (!data.length) { | ||
return ( | ||
<p className="text-sm text-gray-500">{t('err.noMatchingRecords')}</p> | ||
); | ||
} | ||
const chartData = { | ||
labels: data.map(d => d.subject), | ||
datasets: [ | ||
{ | ||
label: 'Average', | ||
data: data.map(d => d.current), | ||
backgroundColor: 'rgba(130, 202, 157, 0.5)', | ||
borderColor: 'rgb(130, 202, 157)', | ||
borderWidth: 1, | ||
}, | ||
], | ||
}; | ||
|
||
const options = { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
scales: { | ||
r: { | ||
angleLines: { | ||
display: true, | ||
color: 'rgba(255, 255, 255, 0.1)', | ||
}, | ||
suggestedMin: 0, | ||
suggestedMax: 2, | ||
ticks: { | ||
stepSize: 1, | ||
callback: (value: number | string) => { | ||
if (value === 0) { | ||
return 'None'; | ||
} | ||
if (value === 1) { | ||
return 'Low'; | ||
} | ||
if (value === 2) { | ||
return 'High'; | ||
} | ||
return value; | ||
}, | ||
display: true, | ||
color: 'black', | ||
}, | ||
pointLabels: { | ||
font: { | ||
size: 14, | ||
}, | ||
color: 'white', | ||
}, | ||
grid: { | ||
color: 'rgba(255, 255, 255, 0.1)', | ||
}, | ||
}, | ||
}, | ||
plugins: { | ||
legend: { | ||
position: 'bottom' as const, | ||
labels: { | ||
color: 'white', | ||
boxWidth: 20, | ||
padding: 20, | ||
}, | ||
}, | ||
tooltip: { | ||
backgroundColor: 'rgba(0, 0, 0, 0.8)', | ||
titleColor: 'white', | ||
bodyColor: 'white', | ||
borderColor: 'white', | ||
borderWidth: 1, | ||
}, | ||
}, | ||
}; | ||
|
||
return ( | ||
<div className="h-[300px] w-full"> | ||
<Radar data={chartData} options={options} /> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { | ||
BarElement, | ||
CategoryScale, | ||
Chart as ChartJS, | ||
LinearScale, | ||
Title, | ||
Tooltip, | ||
} from 'chart.js'; | ||
import annotationPlugin from 'chartjs-plugin-annotation'; | ||
import { t } from 'i18next'; | ||
import React from 'react'; | ||
import { Bar } from 'react-chartjs-2'; | ||
|
||
ChartJS.register( | ||
CategoryScale, | ||
LinearScale, | ||
BarElement, | ||
Title, | ||
Tooltip, | ||
annotationPlugin, | ||
); | ||
|
||
type CVSSData = { | ||
name: string; | ||
score: number; | ||
}; | ||
|
||
type Props = { | ||
data: CVSSData[]; | ||
}; | ||
|
||
export const CVSSChart: React.FC<Props> = ({ data }) => { | ||
if (!data.length) { | ||
return ( | ||
<p className="text-sm text-gray-500">{t('err.noMatchingRecords')}</p> | ||
); | ||
} | ||
|
||
const averageCVSS = ( | ||
data.reduce((acc, d) => acc + d.score, 0) / data.length | ||
).toFixed(2); | ||
|
||
const chartData = { | ||
labels: data.map(d => d.name), | ||
datasets: [ | ||
{ | ||
data: data.map(d => d.score), | ||
backgroundColor: data.map(d => { | ||
if (d.score >= 9.0) { | ||
return '#dc3545'; | ||
} else if (d.score >= 7.0) { | ||
return '#fd7e14'; | ||
} else if (d.score >= 4.0) { | ||
return '#ffc107'; | ||
} else if (d.score >= 0.1) { | ||
return '#28a745'; | ||
} else { | ||
return '#6c757d'; | ||
} | ||
}), | ||
}, | ||
], | ||
}; | ||
|
||
const options = { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
indexAxis: 'y' as const, | ||
plugins: { | ||
legend: { | ||
display: false, | ||
}, | ||
annotation: { | ||
annotations: { | ||
line1: { | ||
type: 'line' as const, | ||
xMin: parseFloat(averageCVSS), | ||
xMax: parseFloat(averageCVSS), | ||
borderColor: '#2ecc71', | ||
borderWidth: 2, | ||
borderDash: [5, 5], | ||
}, | ||
}, | ||
}, | ||
}, | ||
scales: { | ||
x: { | ||
min: 0, | ||
max: 10, | ||
grid: { | ||
drawOnChartArea: true, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
return ( | ||
<div className="relative"> | ||
<div className="absolute top-0 left-0 w-full text-right pr-4 text-green-400 text-sm"> | ||
Average CVSS: {averageCVSS} | ||
</div> | ||
<div className="h-[300px] w-full"> | ||
<Bar data={chartData} options={options} /> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { t } from 'i18next'; | ||
import React from 'react'; | ||
import { FaBug } from 'react-icons/fa'; | ||
|
||
import { | ||
Tooltip, | ||
TooltipContent, | ||
TooltipProvider, | ||
TooltipTrigger, | ||
} from '../ui/tooltip'; | ||
|
||
type CWEItem = { | ||
id: string; | ||
size: number; | ||
}; | ||
|
||
type Props = { | ||
items: CWEItem[]; | ||
mostCommon: string; | ||
}; | ||
|
||
export const CWECloud: React.FC<Props> = ({ items, mostCommon }) => { | ||
items = items.filter(item => item.id !== ''); | ||
if (!items.length) { | ||
return ( | ||
<p className="text-sm text-gray-500">{t('err.noMatchingRecords')}</p> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="bg-gray-900 rounded-lg p-4"> | ||
<p className="text-sm text-gray-400 mb-4"> | ||
{t('mostCommon')}: {mostCommon} | ||
</p> | ||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> | ||
<TooltipProvider> | ||
{items.map(item => ( | ||
<Tooltip key={item.id}> | ||
<TooltipTrigger> | ||
<div className="bg-gray-800 p-4 rounded-lg shadow-md flex items-center"> | ||
<FaBug className="text-red-500 mr-3" size={24} /> | ||
<div> | ||
<p className="text-white text-lg font-semibold"> | ||
{item.id.split(' ')[0].replace(':', '')} | ||
</p> | ||
<p className="text-gray-400"> | ||
{t('occurrences')}: {item.size} | ||
</p> | ||
</div> | ||
</div> | ||
</TooltipTrigger> | ||
<TooltipContent> | ||
<p>{item.id}</p> | ||
</TooltipContent> | ||
</Tooltip> | ||
))} | ||
</TooltipProvider> | ||
</div> | ||
</div> | ||
); | ||
}; |
69 changes: 69 additions & 0 deletions
69
frontend/src/components/charts/RemediationPriorityChart.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
BarElement, | ||
CategoryScale, | ||
Chart as ChartJS, | ||
Legend, | ||
LinearScale, | ||
Title, | ||
Tooltip, | ||
} from 'chart.js'; | ||
import { t } from 'i18next'; | ||
import React from 'react'; | ||
import { Bar } from 'react-chartjs-2'; | ||
|
||
ChartJS.register( | ||
CategoryScale, | ||
LinearScale, | ||
BarElement, | ||
Title, | ||
Tooltip, | ||
Legend, | ||
); | ||
|
||
type PriorityData = { | ||
name: string; | ||
count: number; | ||
color: string; | ||
}; | ||
|
||
type Props = { | ||
data: PriorityData[]; | ||
}; | ||
|
||
export const RemediationPriorityChart: React.FC<Props> = ({ data }) => { | ||
if (!data.length) { | ||
return ( | ||
<p className="text-sm text-gray-500">{t('err.noMatchingRecords')}</p> | ||
); | ||
} | ||
const chartData = { | ||
labels: data.map(d => d.name), | ||
datasets: [ | ||
{ | ||
data: data.map(d => d.count), | ||
backgroundColor: data.map(d => d.color), | ||
}, | ||
], | ||
}; | ||
|
||
const options = { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
plugins: { | ||
legend: { | ||
display: false, | ||
}, | ||
}, | ||
scales: { | ||
y: { | ||
beginAtZero: true, | ||
}, | ||
}, | ||
}; | ||
|
||
return ( | ||
<div className="h-[300px] w-full"> | ||
<Bar data={chartData} options={options} /> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.