From 8c1b065fca6ef094606c3fc1414d6e88a71b2001 Mon Sep 17 00:00:00 2001 From: Rush Kapoor Date: Mon, 27 Apr 2026 14:11:31 -0700 Subject: [PATCH] feat(#218): Add Critical CVE Severity to support CVSS v3 scores 9.0-10.0 Adds Critical labeling to all `Severity`-related data and filtering options --- .../Dashboard/buttons/ExposureReport.vue | 1 + .../BarChart4TopVulnerableContainers.vue | 13 +++++++ .../charts/BarChart4TopVulnerableHosts.vue | 13 +++++++ .../Dashboard/grids/ExposureGrid.vue | 13 ++++--- .../components/Dashboard/panels/Exposures.vue | 1 + .../charts/TopVulnerableImagesBarChart.vue | 13 +++++++ .../charts/TopVulnerableNodesBarChart.vue | 13 +++++++ .../contents/VulnerabilityItemsChart.vue | 7 ++-- .../dialogs/AdvancedFilterModal.vue | 1 + .../VulnerabilityScoreCellComponent.vue | 36 +++++++++++++++++-- .../components/common/dialogs/NodeInfo.vue | 8 ++++- .../components/common/dialogs/PodInfo.vue | 8 ++++- .../components/ImpactModalNodeBrief.vue | 7 +++- .../components/ImpactModalWorkloadBrief.vue | 7 +++- .../common/grids/VulnerabilitiesGrid.vue | 9 +++-- .../formatters/NodeVulnerabilitySummary.vue | 1 + pkg/neuvector-ui-ext/l10n/en-us.yaml | 12 +++++-- .../plugins/vulnerabilities-csv-class.js | 4 +++ pkg/neuvector-ui-ext/styles/neuvector.scss | 4 +++ pkg/neuvector-ui-ext/types/neuvector.ts | 6 ++-- pkg/neuvector-ui-ext/types/vulnerabilities.ts | 2 ++ pkg/neuvector-ui-ext/utils/response-rules.ts | 2 +- 22 files changed, 160 insertions(+), 21 deletions(-) diff --git a/pkg/neuvector-ui-ext/components/Dashboard/buttons/ExposureReport.vue b/pkg/neuvector-ui-ext/components/Dashboard/buttons/ExposureReport.vue index b8eb2f1f..71c61845 100644 --- a/pkg/neuvector-ui-ext/components/Dashboard/buttons/ExposureReport.vue +++ b/pkg/neuvector-ui-ext/components/Dashboard/buttons/ExposureReport.vue @@ -36,6 +36,7 @@ export default { Direction: direction, Service: exposure.service, Pod: exposure.pod_name, + 'Critical Vuls': exposure.critical, 'High Vuls': exposure.high, 'Medium Vuls': exposure.medium, 'Policy Mode': exposure.policy_mode, diff --git a/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableContainers.vue b/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableContainers.vue index b2ee18d8..a47cc807 100644 --- a/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableContainers.vue +++ b/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableContainers.vue @@ -41,9 +41,11 @@ setup(props) { let isEmptyData = ref(false); let topVulnerableAssetsLabel = new Array(5); + let topCriticalVulnerableAssetsData = new Array(5); let topHighVulnerableAssetsData = new Array(5); let topMediumVulnerableAssetsData = new Array(5); topVulnerableAssetsLabel.fill(''); + topCriticalVulnerableAssetsData.fill(0); topHighVulnerableAssetsData.fill(0); topMediumVulnerableAssetsData.fill(0); if (props.topVulContainers.top5Containers.length === 0) { @@ -52,6 +54,7 @@ isEmptyData.value = false; props.topVulContainers.top5Containers.forEach((asset, index) => { topVulnerableAssetsLabel[index] = asset.display_name; + topCriticalVulnerableAssetsData[index] = asset.critical4Dashboard; topHighVulnerableAssetsData[index] = asset.high4Dashboard; topMediumVulnerableAssetsData[index] = asset.medium4Dashboard; }); @@ -59,6 +62,16 @@ const chartData = ref({ labels: topVulnerableAssetsLabel, datasets: [ + { + data: topCriticalVulnerableAssetsData, + label: props.parentContext.t('enum.CRITICAL'), + backgroundColor: 'rgba(233, 30, 99, 0.3)', + borderColor: '#e91e63', + hoverBackgroundColor: 'rgba(233, 30, 99, 0.3)', + hoverBorderColor: '#e91e63', + barThickness: 8, + borderWidth: 2, + }, { data: topHighVulnerableAssetsData, label: props.parentContext.t('enum.HIGH'), diff --git a/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableHosts.vue b/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableHosts.vue index e1e3d42c..d040421c 100644 --- a/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableHosts.vue +++ b/pkg/neuvector-ui-ext/components/Dashboard/charts/BarChart4TopVulnerableHosts.vue @@ -34,9 +34,11 @@ setup(props) { let isEmptyData = ref(false); let topVulnerableAssetsLabel = new Array(5); + let topCriticalVulnerableAssetsData = new Array(5); let topHighVulnerableAssetsData = new Array(5); let topMediumVulnerableAssetsData = new Array(5); topVulnerableAssetsLabel.fill(''); + topCriticalVulnerableAssetsData.fill(0); topHighVulnerableAssetsData.fill(0); topMediumVulnerableAssetsData.fill(0); if (props.topVulHosts.top5Nodes.length === 0) { @@ -45,6 +47,7 @@ isEmptyData.value = false; props.topVulHosts.top5Nodes.forEach((asset, index) => { topVulnerableAssetsLabel[index] = asset.name; + topCriticalVulnerableAssetsData[index] = asset.scan_summary.critical; topHighVulnerableAssetsData[index] = asset.scan_summary.high; topMediumVulnerableAssetsData[index] = asset.scan_summary.medium; }); @@ -52,6 +55,16 @@ const chartData = ref({ labels: topVulnerableAssetsLabel, datasets: [ + { + data: topCriticalVulnerableAssetsData, + label: props.parentContext.t('enum.CRITICAL'), + backgroundColor: 'rgba(233, 30, 99, 0.3)', + borderColor: '#e91e63', + hoverBackgroundColor: 'rgba(233, 30, 99, 0.3)', + hoverBorderColor: '#e91e63', + barThickness: 8, + borderWidth: 2, + }, { data: topHighVulnerableAssetsData, label: props.parentContext.t('enum.HIGH'), diff --git a/pkg/neuvector-ui-ext/components/Dashboard/grids/ExposureGrid.vue b/pkg/neuvector-ui-ext/components/Dashboard/grids/ExposureGrid.vue index 3c10b829..bb283442 100644 --- a/pkg/neuvector-ui-ext/components/Dashboard/grids/ExposureGrid.vue +++ b/pkg/neuvector-ui-ext/components/Dashboard/grids/ExposureGrid.vue @@ -78,15 +78,16 @@ const vueApp = createApp({}); cellRenderer: params => { if (params && params.data) { return ( - `${params.data.high} + `${params.data.critical} + ${params.data.high} ${params.data.medium}` ); } return ''; }, - width: 110, - maxWidth: 110, - minWidth: 110, + width: 130, + maxWidth: 130, + minWidth: 130, sortable: false, }, { @@ -186,6 +187,10 @@ const vueApp = createApp({}); margin-left: 8px; padding: 4px 8px; } + .nv-badge-critical { + background-color: #E91E63; + color: #fff; + } .nv-badge-danger { background-color: #c5161f; color: #fff; diff --git a/pkg/neuvector-ui-ext/components/Dashboard/panels/Exposures.vue b/pkg/neuvector-ui-ext/components/Dashboard/panels/Exposures.vue index 0021431a..21ae130f 100644 --- a/pkg/neuvector-ui-ext/components/Dashboard/panels/Exposures.vue +++ b/pkg/neuvector-ui-ext/components/Dashboard/panels/Exposures.vue @@ -76,6 +76,7 @@ export default { peerEndpoint: '', service: k, policy_mode: v[0].policy_mode, + critical: v[0].critical, high: v[0].high, medium: v[0].medium, workload: '', diff --git a/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableImagesBarChart.vue b/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableImagesBarChart.vue index 057bff55..c3a54add 100644 --- a/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableImagesBarChart.vue +++ b/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableImagesBarChart.vue @@ -27,15 +27,18 @@ }, setup(props) { let topVulnerableAssetsLabel = new Array(5); + let topCriticalVulnerableAssetsData = new Array(5); let topHighVulnerableAssetsData = new Array(5); let topMediumVulnerableAssetsData = new Array(5); let topLowVulnerableAssetsData = new Array(5); topVulnerableAssetsLabel.fill(''); + topCriticalVulnerableAssetsData.fill(0); topHighVulnerableAssetsData.fill(0); topMediumVulnerableAssetsData.fill(0); topLowVulnerableAssetsData.fill(0); props.topVulImages.forEach((asset, index) => { topVulnerableAssetsLabel[index] = asset.display_name; + topCriticalVulnerableAssetsData[index] = asset.critical; topHighVulnerableAssetsData[index] = asset.high; topMediumVulnerableAssetsData[index] = asset.medium; topLowVulnerableAssetsData[index] = asset.low; @@ -43,6 +46,16 @@ const chartData = ref({ labels: topVulnerableAssetsLabel, datasets: [ + { + data: topCriticalVulnerableAssetsData, + label: props.parentContext.t('enum.CRITICAL'), + backgroundColor: 'rgba(233, 30, 99, 0.3)', + borderColor: '#e91e63', + hoverBackgroundColor: 'rgba(233, 30, 99, 0.3)', + hoverBorderColor: '#e91e63', + barThickness: 8, + borderWidth: 2, + }, { data: topHighVulnerableAssetsData, label: props.parentContext.t('enum.HIGH'), diff --git a/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableNodesBarChart.vue b/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableNodesBarChart.vue index b9c88ff0..571f5b7b 100644 --- a/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableNodesBarChart.vue +++ b/pkg/neuvector-ui-ext/components/Vulnerabilities/charts/TopVulnerableNodesBarChart.vue @@ -27,15 +27,18 @@ }, setup(props) { let topVulnerableAssetsLabel = new Array(5); + let topCriticalVulnerableAssetsData = new Array(5); let topHighVulnerableAssetsData = new Array(5); let topMediumVulnerableAssetsData = new Array(5); let topLowVulnerableAssetsData = new Array(5); topVulnerableAssetsLabel.fill(''); + topCriticalVulnerableAssetsData.fill(0); topHighVulnerableAssetsData.fill(0); topMediumVulnerableAssetsData.fill(0); topLowVulnerableAssetsData.fill(0); props.topVulHosts.forEach((asset, index) => { topVulnerableAssetsLabel[index] = asset.display_name; + topCriticalVulnerableAssetsData[index] = asset.critical; topHighVulnerableAssetsData[index] = asset.high; topMediumVulnerableAssetsData[index] = asset.medium; topLowVulnerableAssetsData[index] = asset.low; @@ -43,6 +46,16 @@ const chartData = ref({ labels: topVulnerableAssetsLabel, datasets: [ + { + data: topCriticalVulnerableAssetsData, + label: props.parentContext.t('enum.CRITICAL'), + backgroundColor: 'rgba(233, 30, 99, 0.3)', + borderColor: '#e91e63', + hoverBackgroundColor: 'rgba(233, 30, 99, 0.3)', + hoverBorderColor: '#e91e63', + barThickness: 8, + borderWidth: 2, + }, { data: topHighVulnerableAssetsData, label: props.parentContext.t('enum.HIGH'), diff --git a/pkg/neuvector-ui-ext/components/Vulnerabilities/contents/VulnerabilityItemsChart.vue b/pkg/neuvector-ui-ext/components/Vulnerabilities/contents/VulnerabilityItemsChart.vue index d555f511..5f44e5a3 100644 --- a/pkg/neuvector-ui-ext/components/Vulnerabilities/contents/VulnerabilityItemsChart.vue +++ b/pkg/neuvector-ui-ext/components/Vulnerabilities/contents/VulnerabilityItemsChart.vue @@ -14,12 +14,13 @@ export default defineComponent({ }, setup(props) { const targetDistChartData = ref({ - labels: [['High'], ['Medium'], ['Low']], + labels: [['Critical'], ['High'], ['Medium'], ['Low']], datasets: [ { - hoverBackgroundColor: ['#ef5350', '#ff9800', '#4caf50'], - backgroundColor: ['#ef5350', '#ff9800', '#4caf50'], + hoverBackgroundColor: ['#e91e63', '#ef5350', '#ff9800', '#4caf50'], + backgroundColor: ['#e91e63', '#ef5350', '#ff9800', '#4caf50'], data: [ + props.countDistribution.critical, props.countDistribution.high, props.countDistribution.medium, props.countDistribution.low, diff --git a/pkg/neuvector-ui-ext/components/Vulnerabilities/dialogs/AdvancedFilterModal.vue b/pkg/neuvector-ui-ext/components/Vulnerabilities/dialogs/AdvancedFilterModal.vue index 2fe9e819..12bc5228 100644 --- a/pkg/neuvector-ui-ext/components/Vulnerabilities/dialogs/AdvancedFilterModal.vue +++ b/pkg/neuvector-ui-ext/components/Vulnerabilities/dialogs/AdvancedFilterModal.vue @@ -103,6 +103,7 @@ ], severityOptions: [ { label: this.t('setting.ALL'), value: 'all' }, + { label: this.t('enum.CRITICAL'), value: 'critical' }, { label: this.t('enum.HIGH'), value: 'high' }, { label: this.t('enum.MEDIUM'), value: 'medium' }, { label: this.t('enum.LOW'), value: 'low' }, diff --git a/pkg/neuvector-ui-ext/components/Vulnerabilities/grids/components/VulnerabilityScoreCellComponent.vue b/pkg/neuvector-ui-ext/components/Vulnerabilities/grids/components/VulnerabilityScoreCellComponent.vue index b05002ae..face66e2 100644 --- a/pkg/neuvector-ui-ext/components/Vulnerabilities/grids/components/VulnerabilityScoreCellComponent.vue +++ b/pkg/neuvector-ui-ext/components/Vulnerabilities/grids/components/VulnerabilityScoreCellComponent.vue @@ -2,7 +2,21 @@