diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx index 0e48e57366..041247865d 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -19,6 +19,10 @@ import { import { ModelVulnerability } from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { FilterBadge } from '@/components/filters/FilterBadge'; +import { SearchableClusterList } from '@/components/forms/SearchableClusterList'; +import { SearchableContainerList } from '@/components/forms/SearchableContainerList'; +import { SearchableHostList } from '@/components/forms/SearchableHostList'; +import { SearchableImageList } from '@/components/forms/SearchableImageList'; import { FilterIcon } from '@/components/icons/common/Filter'; import { PopOutIcon } from '@/components/icons/common/PopOut'; import { TimesIcon } from '@/components/icons/common/Times'; @@ -26,6 +30,7 @@ import { CveCVSSScore, SeverityBadge } from '@/components/SeverityBadge'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; import { TruncatedText } from '@/components/TruncatedText'; import { queries } from '@/queries'; +import { ScanTypeEnum } from '@/types/common'; const DEFAULT_PAGE_SIZE = 10; @@ -126,6 +131,89 @@ const Filters = () => { ); })} + { + setSearchParams((prev) => { + prev.delete('hosts'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('hosts'); + value.forEach((host) => { + prev.append('hosts', host); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('containers'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('containers'); + value.forEach((container) => { + prev.append('containers', container); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('containerImages'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('containerImages'); + value.forEach((containerImage) => { + prev.append('containerImages', containerImage); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('clusters'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('clusters'); + value.forEach((cluster) => { + prev.append('clusters', cluster); + }); + prev.delete('page'); + return prev; + }); + }} + /> {appliedFilterCount > 0 ? (
@@ -290,6 +378,10 @@ const MostExploitTable = () => { ...queries.vulnerability.mostExploitableVulnerabilities({ liveConnection: searchParams.getAll('liveConnection'), severity: searchParams.getAll('severity'), + hostIds: searchParams.getAll('hosts'), + containerIds: searchParams.getAll('containers'), + containerImageIds: searchParams.getAll('containerImages'), + clusterIds: searchParams.getAll('clusters'), }), }); diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx index 7b7bb35ceb..e5f1d33cc3 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx @@ -20,6 +20,10 @@ import { import { ModelVulnerability } from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { FilterBadge } from '@/components/filters/FilterBadge'; +import { SearchableClusterList } from '@/components/forms/SearchableClusterList'; +import { SearchableContainerList } from '@/components/forms/SearchableContainerList'; +import { SearchableHostList } from '@/components/forms/SearchableHostList'; +import { SearchableImageList } from '@/components/forms/SearchableImageList'; import { FilterIcon } from '@/components/icons/common/Filter'; import { PopOutIcon } from '@/components/icons/common/PopOut'; import { TimesIcon } from '@/components/icons/common/Times'; @@ -27,6 +31,7 @@ import { CveCVSSScore, SeverityBadge } from '@/components/SeverityBadge'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; import { TruncatedText } from '@/components/TruncatedText'; import { queries } from '@/queries'; +import { ScanTypeEnum } from '@/types/common'; import { getOrderFromSearchParams, useSortingState } from '@/utils/table'; const DEFAULT_PAGE_SIZE = 10; @@ -128,6 +133,89 @@ const Filters = () => { ); })} + { + setSearchParams((prev) => { + prev.delete('hosts'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('hosts'); + value.forEach((host) => { + prev.append('hosts', host); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('containers'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('containers'); + value.forEach((container) => { + prev.append('containers', container); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('containerImages'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('containerImages'); + value.forEach((containerImage) => { + prev.append('containerImages', containerImage); + }); + prev.delete('page'); + return prev; + }); + }} + /> + { + setSearchParams((prev) => { + prev.delete('clusters'); + prev.delete('page'); + return prev; + }); + }} + onChange={(value) => { + setSearchParams((prev) => { + prev.delete('clusters'); + value.forEach((cluster) => { + prev.append('clusters', cluster); + }); + prev.delete('page'); + return prev; + }); + }} + />
{appliedFilterCount > 0 ? (
@@ -284,6 +372,10 @@ const UniqueTable = () => { page: parseInt(searchParams.get('page') ?? '0', 10), order: getOrderFromSearchParams(searchParams), severity: searchParams.getAll('severity'), + hostIds: searchParams.getAll('hosts'), + containerIds: searchParams.getAll('containers'), + containerImageIds: searchParams.getAll('containerImages'), + clusterIds: searchParams.getAll('clusters'), }), keepPreviousData: true, }); diff --git a/deepfence_frontend/apps/dashboard/src/queries/vulnerability.tsx b/deepfence_frontend/apps/dashboard/src/queries/vulnerability.tsx index f103851deb..ca0f85a83a 100644 --- a/deepfence_frontend/apps/dashboard/src/queries/vulnerability.tsx +++ b/deepfence_frontend/apps/dashboard/src/queries/vulnerability.tsx @@ -547,8 +547,22 @@ export const vulnerabilityQueries = createQueryKeys('vulnerability', { pageSize: number; severity: string[]; liveConnection: string[]; + hostIds: string[]; + containerIds: string[]; + containerImageIds: string[]; + clusterIds: string[]; }) => { - const { page = 1, order, severity, liveConnection, pageSize } = filters; + const { + page = 1, + order, + severity, + liveConnection, + pageSize, + hostIds, + containerIds, + containerImageIds, + clusterIds, + } = filters; return { queryKey: [{ filters }], queryFn: async () => { @@ -579,6 +593,60 @@ export const vulnerabilityQueries = createQueryKeys('vulnerability', { }, window: { offset: page * pageSize, size: pageSize }, }; + const nodeIds = [...hostIds, ...containerIds, ...containerImageIds]; + + if (nodeIds.length || clusterIds.length) { + const nodeTypes = []; + if (hostIds.length) { + nodeTypes.push('host'); + } + if (containerIds.length) { + nodeTypes.push('container'); + } + if (containerImageIds.length) { + nodeTypes.push('container_image'); + } + + const containsFilter = { + filter_in: {}, + }; + if (nodeIds.length) { + containsFilter.filter_in = { + node_id: nodeIds, + node_type: nodeTypes, + }; + } + if (clusterIds.length) { + containsFilter.filter_in = { + ...containsFilter.filter_in, + kubernetes_cluster_id: clusterIds, + }; + } + searchVulnerabilitiesRequestParams.related_node_filter = { + relation_ship: 'DETECTED', + node_filter: { + filters: { + contains_filter: { filter_in: {} }, + order_filter: { order_fields: [] }, + match_filter: { filter_in: {} }, + compare_filter: null, + }, + in_field_filter: null, + window: { + offset: 0, + size: 0, + }, + }, + next_filter: { + relation_ship: 'SCANNED', + node_filter: { + filters: { + contains_filter: containsFilter, + }, + }, + }, + }; + } if (severity.length) { searchVulnerabilitiesRequestParams.node_filter.filters.contains_filter.filter_in![ @@ -641,8 +709,19 @@ export const vulnerabilityQueries = createQueryKeys('vulnerability', { mostExploitableVulnerabilities: (filters: { severity: string[]; liveConnection: string[]; + hostIds: string[]; + containerIds: string[]; + containerImageIds: string[]; + clusterIds: string[]; }) => { - const { severity, liveConnection } = filters; + const { + severity, + liveConnection, + hostIds, + containerIds, + containerImageIds, + clusterIds, + } = filters; return { queryKey: [{ filters }], queryFn: async () => { @@ -698,6 +777,61 @@ export const vulnerabilityQueries = createQueryKeys('vulnerability', { window: { offset: 0, size: 1000 }, }; + const nodeIds = [...hostIds, ...containerIds, ...containerImageIds]; + + if (nodeIds.length || clusterIds.length) { + const nodeTypes = []; + if (hostIds.length) { + nodeTypes.push('host'); + } + if (containerIds.length) { + nodeTypes.push('container'); + } + if (containerImageIds.length) { + nodeTypes.push('container_image'); + } + + const containsFilter = { + filter_in: {}, + }; + if (nodeIds.length) { + containsFilter.filter_in = { + node_id: nodeIds, + node_type: nodeTypes, + }; + } + if (clusterIds.length) { + containsFilter.filter_in = { + ...containsFilter.filter_in, + kubernetes_cluster_id: clusterIds, + }; + } + searchVulnerabilitiesRequestParams.related_node_filter = { + relation_ship: 'DETECTED', + node_filter: { + filters: { + contains_filter: { filter_in: {} }, + order_filter: { order_fields: [] }, + match_filter: { filter_in: {} }, + compare_filter: null, + }, + in_field_filter: null, + window: { + offset: 0, + size: 0, + }, + }, + next_filter: { + relation_ship: 'SCANNED', + node_filter: { + filters: { + contains_filter: containsFilter, + }, + }, + }, + }; + } + const searchVulnerabilitiesApi = apiWrapper({ fn: getSearchApiClient().searchVulnerabilities, });