From dc3b984ed76ba8f84f31c8d3d50e3caf93ee5a90 Mon Sep 17 00:00:00 2001 From: milan-deepfence Date: Tue, 28 Feb 2023 15:28:41 +0530 Subject: [PATCH 1/6] wip: added most exploitable table --- .../apps/dashboard/src/api/api.ts | 1 + .../pages/MostExploitableVulnerabilities.tsx | 402 ++++++++++++------ .../vulnerabilities/pages/Vulnerability.tsx | 57 ++- .../apps/dashboard/src/routes/private.tsx | 7 + 4 files changed, 345 insertions(+), 122 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/api/api.ts b/deepfence_frontend/apps/dashboard/src/api/api.ts index 35e91fafce..e32ebce7ca 100644 --- a/deepfence_frontend/apps/dashboard/src/api/api.ts +++ b/deepfence_frontend/apps/dashboard/src/api/api.ts @@ -127,6 +127,7 @@ export function getSearchApiClient() { searchVulnerabilities: searchApi.searchVulnerabilities.bind(searchApi), searchVulnerabilitiesCount: searchApi.countVulnerabilities.bind(searchApi), searchVulnerabilityScanCount: searchApi.countVulnerabilityScans.bind(searchApi), + searchVulnerabilityCount: searchApi.countVulnerabilities.bind(searchApi), }; } 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 52ea41afb3..8295ebdd4a 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -1,83 +1,234 @@ import cx from 'classnames'; -import { useMemo } from 'react'; +import { toNumber } from 'lodash-es'; +import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; -import { HiArrowSmLeft, HiDotsVertical, HiExternalLink } from 'react-icons/hi'; -import { IoIosGitNetwork } from 'react-icons/io'; -import { Badge, createColumnHelper, getRowSelectionColumn, Table } from 'ui-components'; +import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; +import { + Await, + LoaderFunctionArgs, + Outlet, + useLoaderData, + useNavigation, + useSearchParams, +} from 'react-router-dom'; +import { + Badge, + CircleSpinner, + createColumnHelper, + Table, + TableSkeleton, +} from 'ui-components'; +import { getSearchApiClient } from '@/api/api'; +import { ApiDocsBadRequestResponse } from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; +import { ApiError, makeRequest } from '@/utils/api'; +import { typedDefer, TypedDeferredData } from '@/utils/router'; -type TableDataType = { - rank: number; - id: string; - severity: string; - score: number; - attackVector: string; - liveConnection: string; - exploit: string; - type: string; - description: string; - action?: null; +type CveType = { + cveId: string; + cveDescription: string; + cveLink: string; + cveType: string; + cveSeverity: string; + cveCVSSScore: number; + cveAttackVector: string; + cveAssetAffected: string; + active: boolean; + exploitPoc: string; }; -const data = Array.from(Array(25).keys()).map((i) => { - return { - rank: 1, - id: 'CVE-2022-234', - severity: i % 2 === 0 ? 'critical' : i % 3 === 0 ? 'medium' : 'low', - score: i, - attackVector: 'network', - liveConnection: i % 2 === 0 ? 'yes' : 'no', - exploit: 'Link', - type: 'deepfence-poc-agent-2 + 1 image(s)', - description: - 'Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and', +type LoaderDataType = { + error?: string; + message?: string; + data: Awaited>; +}; +function getPageFromSearchParams(searchParams: URLSearchParams): number { + const page = toNumber(searchParams.get('page') ?? '0'); + return isFinite(page) && !isNaN(page) && page > 0 ? page : 0; +} + +const PAGE_SIZE = 15; + +async function getVulnerability(searchParams: URLSearchParams): Promise<{ + vulnerabilities: CveType[]; + currentPage: number; + totalRows: number; + message?: string; +}> { + const results: { + vulnerabilities: CveType[]; + currentPage: number; + totalRows: number; + message?: string; + } = { + currentPage: 1, + totalRows: 0, + vulnerabilities: [], }; -}); + + const page = getPageFromSearchParams(searchParams); + + const result = await makeRequest({ + apiFunction: getSearchApiClient().searchVulnerabilities, + apiArgs: [ + { + searchSearchNodeReq: { + node_filter: { + filters: { + contains_filter: { + filter_in: {}, + }, + order_filter: { + order_field: 'exploitability_score', + }, + match_filter: { + filter_in: {}, + }, + }, + in_field_filter: null, + }, + window: { + offset: page * PAGE_SIZE, + size: PAGE_SIZE, + }, + }, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); + + if (ApiError.isApiError(result)) { + throw result.value(); + } + + const countsResult = await makeRequest({ + apiFunction: getSearchApiClient().searchVulnerabilityCount, + apiArgs: [ + { + searchSearchNodeReq: { + node_filter: { + filters: { + contains_filter: { + filter_in: {}, + }, + order_filter: { + order_field: 'exploitability_score', + }, + match_filter: { + filter_in: {}, + }, + }, + in_field_filter: null, + }, + window: { + offset: page * PAGE_SIZE, + size: 10 * PAGE_SIZE, + }, + }, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); + + if (ApiError.isApiError(countsResult)) { + throw countsResult.value(); + } + + if (result === null) { + return results; + } + + results.vulnerabilities = result + .map((res) => { + return { + cveId: res.cve_id, + cveDescription: res.cve_description, + cveLink: res.cve_link, + cveType: res.cve_type, + cveSeverity: res.cve_severity, + cveCVSSScore: res.cve_cvss_score, + cveAttackVector: res.cve_attack_vector, + cveAssetAffected: 'dummy text', + active: true, + exploitPoc: res.exploit_poc, + }; + }) + .sort((a, b) => b.cveCVSSScore - a.cveCVSSScore); + + results.currentPage = page; + results.totalRows = page * PAGE_SIZE + countsResult.count; + + return results; +} +const loader = async ({ + request, +}: LoaderFunctionArgs): Promise> => { + const searchParams = new URL(request.url).searchParams; + + return typedDefer({ + data: getVulnerability(searchParams), + }); +}; + const MostExploitableVulnerabilities = () => { - const columnHelper = createColumnHelper(); + const [searchParams, setSearchParams] = useSearchParams(); + const navigation = useNavigation(); + const columnHelper = createColumnHelper(); + const loaderData = useLoaderData() as LoaderDataType; const columns = useMemo(() => { const columns = [ - getRowSelectionColumn(columnHelper, { - size: 0, - minSize: 0, - maxSize: 0, - }), - columnHelper.accessor('rank', { - enableSorting: true, - cell: (info) => info.getValue(), - header: () => 'Rank', - minSize: 10, - size: 20, - maxSize: 20, - }), - columnHelper.accessor('id', { + columnHelper.accessor('cveId', { enableSorting: false, + enableResizing: true, cell: (info) => ( { - /** */ + to={{ + pathname: `./${info.getValue()}`, + search: searchParams.toString(), }} className="flex items-center gap-x-2" > -
-
- + <> +
+
+ +
-
- {info.getValue()} + {info.getValue()} + ), header: () => 'CVE ID', - minSize: 200, + minSize: 100, + size: 150, + maxSize: 250, }), - columnHelper.accessor('severity', { + columnHelper.accessor('cveSeverity', { enableSorting: false, + enableResizing: false, cell: (info) => ( { /> ), header: () => 'Severity', - minSize: 60, + minSize: 70, size: 80, - maxSize: 100, + maxSize: 90, }), - columnHelper.accessor('score', { + columnHelper.accessor('cveCVSSScore', { enableSorting: true, + enableResizing: false, cell: (info) => info.getValue(), header: () => 'Score', - minSize: 20, - size: 20, - maxSize: 40, + minSize: 70, + size: 80, + maxSize: 90, }), - columnHelper.accessor('attackVector', { + columnHelper.accessor('cveAttackVector', { enableSorting: false, - cell: (info) => ( -
-
- - - -
- {info.getValue()} -
- ), + enableResizing: false, + cell: (info) => info.getValue(), header: () => 'Attack Vector', minSize: 100, + size: 120, + maxSize: 250, }), - columnHelper.accessor('liveConnection', { + columnHelper.accessor('active', { enableSorting: false, + enableResizing: false, cell: (info) => (
), header: () => 'Live', - minSize: 50, + minSize: 40, size: 60, + maxSize: 50, }), - columnHelper.accessor('exploit', { + columnHelper.accessor('exploitPoc', { enableSorting: false, + enableResizing: false, cell: () => ( { ), header: () => 'Exploit', - minSize: 30, - size: 50, - maxSize: 50, + minSize: 60, + size: 60, + maxSize: 70, }), - columnHelper.accessor('type', { + columnHelper.accessor('cveAssetAffected', { enableSorting: false, + enableResizing: true, cell: (info) => info.getValue(), header: () => 'Asset Type', - minSize: 300, - size: 400, - maxSize: 400, + minSize: 200, + size: 200, + maxSize: 240, }), - columnHelper.accessor('description', { + columnHelper.accessor('cveDescription', { enableSorting: false, + enableResizing: true, cell: (info) => info.getValue(), header: () => 'Description', - minSize: 300, - size: 500, - maxSize: 500, - }), - columnHelper.accessor('action', { - enableSorting: false, - cell: () => ( - - - - ), - header: () => '', - minSize: 10, - size: 10, - maxSize: 10, + minSize: 200, + size: 250, + maxSize: 400, }), ]; return columns; }, []); + return (
@@ -205,32 +341,56 @@ const MostExploitableVulnerabilities = () => { - UNIQUE VULNERABILITIES + MOST EXPLOITABLE VULNERABILITIES + + + {navigation.state === 'loading' ? : null}
- { - return true; - }} - renderSubComponent={() => { - return ( -

- Error message will be displayed here -

- ); - }} - /> + }> + + {(resolvedData: LoaderDataType['data']) => { + return ( +
{ + let newPageIndex = 0; + if (typeof updaterOrValue === 'function') { + newPageIndex = updaterOrValue({ + pageIndex: resolvedData.currentPage, + pageSize: PAGE_SIZE, + }).pageIndex; + } else { + newPageIndex = updaterOrValue.pageIndex; + } + setSearchParams((prev) => { + prev.set('page', String(newPageIndex)); + return prev; + }); + }} + /> + ); + }} + + + ); }; export const module = { + loader, element: , }; diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx index 5bfd559812..532c0b263b 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx @@ -101,6 +101,59 @@ async function getTop5VulnerableImageData(nodeType: 'image' | 'host' | 'containe }); } +async function getMostExploitableVulnerabilityCount(): Promise { + const defaultResults: VulnerabilitiesCountsCardData = { + total: 0, + severityBreakdown: { + critical: 0, + high: 0, + medium: 0, + low: 0, + unknown: 0, + }, + }; + const mostExploitableVulenrabilityCounts = await makeRequest({ + apiFunction: getSearchApiClient().searchVulnerabilitiesCount, + apiArgs: [ + { + searchSearchNodeReq: { + node_filter: { + filters: { + contains_filter: { filter_in: {} }, + match_filter: { filter_in: {} }, + order_filter: { order_field: 'exploitability_score' }, + }, + in_field_filter: [], + }, + window: { + offset: 0, + size: 5, + }, + }, + }, + ], + errorHandler: () => { + return new ApiError({}); + }, + }); + + if (ApiError.isApiError(mostExploitableVulenrabilityCounts)) { + // TODO handle error + return defaultResults; + } + + return { + total: mostExploitableVulenrabilityCounts.count, + severityBreakdown: { + critical: mostExploitableVulenrabilityCounts.categories?.['critical'] ?? 0, + high: mostExploitableVulenrabilityCounts.categories?.['high'] ?? 0, + medium: mostExploitableVulenrabilityCounts.categories?.['medium'] ?? 0, + low: mostExploitableVulenrabilityCounts.categories?.['low'] ?? 0, + unknown: mostExploitableVulenrabilityCounts.categories?.['unknown'] ?? 0, + }, + }; +} + async function getUniqueVulenrabilityCount(): Promise { const defaultResults: VulnerabilitiesCountsCardData = { total: 0, @@ -159,6 +212,7 @@ type LoaderData = { hostSeverityResults: Array; containerSeverityResults: Array; uniqueVulenrabilityCounts: VulnerabilitiesCountsCardData; + mostExploitableVulnerabilityCounts: VulnerabilitiesCountsCardData; }; const loader = async (): Promise> => { @@ -167,6 +221,7 @@ const loader = async (): Promise> => { hostSeverityResults: getTop5VulnerableImageData('host'), containerSeverityResults: getTop5VulnerableImageData('container'), uniqueVulenrabilityCounts: getUniqueVulenrabilityCount(), + mostExploitableVulnerabilityCounts: getMostExploitableVulnerabilityCount(), }); }; @@ -286,7 +341,7 @@ const Vulnerability = () => { /> } > - + {(resolvedData: VulnerabilitiesCountsCardData) => { return ( Date: Wed, 1 Mar 2023 12:20:50 +0530 Subject: [PATCH 2/6] wip:update most exploitable table query --- .../apps/dashboard/src/api/api.ts | 2 + .../pages/MostExploitableVulnerabilities.tsx | 70 ++- .../pages/UniqueVulnerabilities.tsx | 432 ++++++++++++++++++ .../vulnerabilities/pages/Vulnerability.tsx | 12 +- 4 files changed, 499 insertions(+), 17 deletions(-) create mode 100644 deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx diff --git a/deepfence_frontend/apps/dashboard/src/api/api.ts b/deepfence_frontend/apps/dashboard/src/api/api.ts index e32ebce7ca..700bf8bfe7 100644 --- a/deepfence_frontend/apps/dashboard/src/api/api.ts +++ b/deepfence_frontend/apps/dashboard/src/api/api.ts @@ -142,5 +142,7 @@ export function getScanResultsApiClient() { notifyScanResult: scanResultsApi.notifyScanResult.bind(scanResultsApi), maskScanResult: scanResultsApi.maskScanResult.bind(scanResultsApi), unmaskScanResult: scanResultsApi.unmaskScanResult.bind(scanResultsApi), + getAllNodesInScanResults: + scanResultsApi.getAllNodesInScanResults.bind(scanResultsApi), }; } 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 8295ebdd4a..ec376eddae 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { toNumber } from 'lodash-es'; +import { groupBy, toNumber } from 'lodash-es'; import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; @@ -19,8 +19,11 @@ import { TableSkeleton, } from 'ui-components'; -import { getSearchApiClient } from '@/api/api'; -import { ApiDocsBadRequestResponse } from '@/api/generated'; +import { getScanResultsApiClient, getSearchApiClient } from '@/api/api'; +import { + ApiDocsBadRequestResponse, + GetAllNodesInScanResultScanTypeEnum, +} from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; import { ApiError, makeRequest } from '@/utils/api'; @@ -70,6 +73,11 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ const page = getPageFromSearchParams(searchParams); + let offsetSize = page * PAGE_SIZE; + if (offsetSize > 1000) { + offsetSize = (page - 1) * PAGE_SIZE; + } + const result = await makeRequest({ apiFunction: getSearchApiClient().searchVulnerabilities, apiArgs: [ @@ -78,10 +86,16 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ node_filter: { filters: { contains_filter: { - filter_in: {}, + filter_in: { + exploitability_score: [1, 2, 3], + }, }, order_filter: { - order_field: 'exploitability_score', + order_fields: [ + 'exploitability_score', + 'cve_severity', + 'vulnerability_score', + ], }, match_filter: { filter_in: {}, @@ -90,7 +104,7 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: page * PAGE_SIZE, + offset: offsetSize, size: PAGE_SIZE, }, }, @@ -120,10 +134,16 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ node_filter: { filters: { contains_filter: { - filter_in: {}, + filter_in: { + exploitability_score: [1, 2, 3], + }, }, order_filter: { - order_field: 'exploitability_score', + order_fields: [ + 'exploitability_score', + 'cve_severity', + 'vulnerability_score', + ], }, match_filter: { filter_in: {}, @@ -132,8 +152,8 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: page * PAGE_SIZE, - size: 10 * PAGE_SIZE, + offset: 0, + size: 1000, }, }, }, @@ -153,11 +173,33 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ if (ApiError.isApiError(countsResult)) { throw countsResult.value(); } + debugger; + const allNodes = await makeRequest({ + apiFunction: getScanResultsApiClient().getAllNodesInScanResults, + apiArgs: [ + { + resultId: result.map((res) => res.cve_id), + scanType: GetAllNodesInScanResultScanTypeEnum.VulnerabilityScan, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); if (result === null) { return results; } - + debugger; + const groupByNodes = groupBy(allNodes, 'name'); + console.log(groupByNodes); results.vulnerabilities = result .map((res) => { return { @@ -167,9 +209,9 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ cveType: res.cve_type, cveSeverity: res.cve_severity, cveCVSSScore: res.cve_cvss_score, - cveAttackVector: res.cve_attack_vector, - cveAssetAffected: 'dummy text', - active: true, + cveAttackVector: res.parsed_attack_vector, + // cveAssetAffected: groupByNodes[res.cve_id][0]., + active: res.has_live_connection, exploitPoc: res.exploit_poc, }; }) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx new file mode 100644 index 0000000000..b940f8529b --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx @@ -0,0 +1,432 @@ +import cx from 'classnames'; +import { groupBy, toNumber } from 'lodash-es'; +import { Suspense, useMemo } from 'react'; +import { IconContext } from 'react-icons'; +import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; +import { + Await, + LoaderFunctionArgs, + Outlet, + useLoaderData, + useNavigation, + useSearchParams, +} from 'react-router-dom'; +import { + Badge, + CircleSpinner, + createColumnHelper, + Table, + TableSkeleton, +} from 'ui-components'; + +import { getScanResultsApiClient, getSearchApiClient } from '@/api/api'; +import { + ApiDocsBadRequestResponse, + GetAllNodesInScanResultScanTypeEnum, +} from '@/api/generated'; +import { DFLink } from '@/components/DFLink'; +import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; +import { ApiError, makeRequest } from '@/utils/api'; +import { typedDefer, TypedDeferredData } from '@/utils/router'; + +type CveType = { + cveId: string; + cveDescription: string; + cveLink: string; + cveType: string; + cveSeverity: string; + cveCVSSScore: number; + cveAttackVector: string; + cveAssetAffected: string; + active: boolean; + exploitPoc: string; +}; + +type LoaderDataType = { + error?: string; + message?: string; + data: Awaited>; +}; +function getPageFromSearchParams(searchParams: URLSearchParams): number { + const page = toNumber(searchParams.get('page') ?? '0'); + return isFinite(page) && !isNaN(page) && page > 0 ? page : 0; +} + +const PAGE_SIZE = 15; + +async function getVulnerability(searchParams: URLSearchParams): Promise<{ + vulnerabilities: CveType[]; + currentPage: number; + totalRows: number; + message?: string; +}> { + const results: { + vulnerabilities: CveType[]; + currentPage: number; + totalRows: number; + message?: string; + } = { + currentPage: 1, + totalRows: 0, + vulnerabilities: [], + }; + + const page = getPageFromSearchParams(searchParams); + + let offsetSize = page * PAGE_SIZE; + if (offsetSize > 1000) { + offsetSize = (page - 1) * PAGE_SIZE; + } + + const result = await makeRequest({ + apiFunction: getSearchApiClient().searchVulnerabilities, + apiArgs: [ + { + searchSearchNodeReq: { + node_filter: { + filters: { + contains_filter: { + filter_in: {}, + }, + order_filter: { + order_fields: [], + }, + match_filter: { + filter_in: {}, + }, + }, + in_field_filter: null, + }, + window: { + offset: offsetSize, + size: PAGE_SIZE, + }, + }, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); + + if (ApiError.isApiError(result)) { + throw result.value(); + } + + const countsResult = await makeRequest({ + apiFunction: getSearchApiClient().searchVulnerabilityCount, + apiArgs: [ + { + searchSearchNodeReq: { + node_filter: { + filters: { + contains_filter: { + filter_in: { + exploitability_score: [1, 2, 3], + }, + }, + order_filter: { + order_fields: [ + 'exploitability_score', + 'cve_severity', + 'vulnerability_score', + ], + }, + match_filter: { + filter_in: {}, + }, + }, + in_field_filter: null, + }, + window: { + offset: 0, + size: 1000, + }, + }, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); + + if (ApiError.isApiError(countsResult)) { + throw countsResult.value(); + } + debugger; + const allNodes = await makeRequest({ + apiFunction: getScanResultsApiClient().getAllNodesInScanResults, + apiArgs: [ + { + resultId: result.map((res) => res.cve_id), + scanType: GetAllNodesInScanResultScanTypeEnum.VulnerabilityScan, + }, + ], + errorHandler: async (r) => { + const error = new ApiError(results); + if (r.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + ...results, + message: modelResponse.message, + }); + } + }, + }); + + if (result === null) { + return results; + } + debugger; + const groupByNodes = groupBy(allNodes, 'name'); + console.log(groupByNodes); + results.vulnerabilities = result + .map((res) => { + return { + cveId: res.cve_id, + cveDescription: res.cve_description, + cveLink: res.cve_link, + cveType: res.cve_type, + cveSeverity: res.cve_severity, + cveCVSSScore: res.cve_cvss_score, + cveAttackVector: res.parsed_attack_vector, + // cveAssetAffected: groupByNodes[res.cve_id][0]., + active: res.has_live_connection, + exploitPoc: res.exploit_poc, + }; + }) + .sort((a, b) => b.cveCVSSScore - a.cveCVSSScore); + + results.currentPage = page; + results.totalRows = page * PAGE_SIZE + countsResult.count; + + return results; +} +const loader = async ({ + request, +}: LoaderFunctionArgs): Promise> => { + const searchParams = new URL(request.url).searchParams; + + return typedDefer({ + data: getVulnerability(searchParams), + }); +}; + +const UniqueVulnerabilities = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const navigation = useNavigation(); + const columnHelper = createColumnHelper(); + const loaderData = useLoaderData() as LoaderDataType; + const columns = useMemo(() => { + const columns = [ + columnHelper.accessor('cveId', { + enableSorting: false, + enableResizing: true, + cell: (info) => ( + + <> +
+
+ +
+
+ {info.getValue()} + +
+ ), + header: () => 'CVE ID', + minSize: 100, + size: 150, + maxSize: 250, + }), + columnHelper.accessor('cveSeverity', { + enableSorting: false, + enableResizing: false, + cell: (info) => ( + + ), + header: () => 'Severity', + minSize: 70, + size: 80, + maxSize: 90, + }), + columnHelper.accessor('cveCVSSScore', { + enableSorting: true, + enableResizing: false, + cell: (info) => info.getValue(), + header: () => 'Score', + minSize: 70, + size: 80, + maxSize: 90, + }), + columnHelper.accessor('cveAttackVector', { + enableSorting: false, + enableResizing: false, + cell: (info) => info.getValue(), + header: () => 'Attack Vector', + minSize: 100, + size: 120, + maxSize: 250, + }), + columnHelper.accessor('active', { + enableSorting: false, + enableResizing: false, + cell: (info) => ( +
+ ), + header: () => 'Live', + minSize: 40, + size: 60, + maxSize: 50, + }), + columnHelper.accessor('exploitPoc', { + enableSorting: false, + enableResizing: false, + cell: () => ( + + + + + + ), + header: () => 'Exploit', + minSize: 60, + size: 60, + maxSize: 70, + }), + columnHelper.accessor('cveAssetAffected', { + enableSorting: false, + enableResizing: true, + cell: (info) => info.getValue(), + header: () => 'Asset Type', + minSize: 200, + size: 200, + maxSize: 240, + }), + columnHelper.accessor('cveDescription', { + enableSorting: false, + enableResizing: true, + cell: (info) => info.getValue(), + header: () => 'Description', + minSize: 200, + size: 250, + maxSize: 400, + }), + ]; + + return columns; + }, []); + + return ( +
+
+ + + + + + + MOST EXPLOITABLE VULNERABILITIES + + + {navigation.state === 'loading' ? : null} + +
+
+ }> + + {(resolvedData: LoaderDataType['data']) => { + return ( +
{ + let newPageIndex = 0; + if (typeof updaterOrValue === 'function') { + newPageIndex = updaterOrValue({ + pageIndex: resolvedData.currentPage, + pageSize: PAGE_SIZE, + }).pageIndex; + } else { + newPageIndex = updaterOrValue.pageIndex; + } + setSearchParams((prev) => { + prev.set('page', String(newPageIndex)); + return prev; + }); + }} + /> + ); + }} + + + + + + ); +}; + +export const module = { + loader, + element: , +}; diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx index d37331eb4c..8e5149a3cc 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx @@ -119,15 +119,21 @@ async function getMostExploitableVulnerabilityCount(): Promise Date: Wed, 1 Mar 2023 15:00:13 +0530 Subject: [PATCH 3/6] added asset api integrations --- .../apps/dashboard/api-spec.json | 97 +++++------------- .../api/generated/.openapi-generator/FILES | 1 + .../src/api/generated/apis/ScanResultsApi.ts | 99 +++---------------- .../models/ModelNodesInScanResultRequest.ts | 89 +++++++++++++++++ .../src/api/generated/models/index.ts | 1 + .../landing/VulnerabilitiesCountsCard.tsx | 2 +- .../pages/MostExploitableVulnerabilities.tsx | 38 ++++--- .../pages/UniqueVulnerabilities.tsx | 58 ++++++----- .../apps/dashboard/src/routes/private.tsx | 13 +++ 9 files changed, 202 insertions(+), 196 deletions(-) create mode 100644 deepfence_frontend/apps/dashboard/src/api/generated/models/ModelNodesInScanResultRequest.ts diff --git a/deepfence_frontend/apps/dashboard/api-spec.json b/deepfence_frontend/apps/dashboard/api-spec.json index b97a853107..3c22492c74 100644 --- a/deepfence_frontend/apps/dashboard/api-spec.json +++ b/deepfence_frontend/apps/dashboard/api-spec.json @@ -2452,82 +2452,18 @@ } }, "/deepfence/scan/nodes-in-result": { - "get": { + "post": { "tags": ["Scan Results"], "summary": "Get all nodes in given scan result ids", "description": "Get all nodes in given scan result ids", "operationId": "getAllNodesInScanResults", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { "$ref": "#/components/schemas/ModelScanResultBasicNode" } - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } - } - } - }, - "401": { "description": "Unauthorized" }, - "403": { "description": "Forbidden" }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } - } + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ModelNodesInScanResultRequest" } } } }, - "security": [{ "bearer_token": [] }] - } - }, - "/deepfence/scan/nodes/{scan_type}/{result_id}": { - "get": { - "tags": ["Scan Results"], - "summary": "Get all nodes in given scan result", - "description": "Get all nodes in given scan result", - "operationId": "getAllNodesInScanResult", - "parameters": [ - { - "name": "result_id", - "in": "path", - "required": true, - "schema": { "type": "string" } - }, - { - "name": "scan_type", - "in": "path", - "required": true, - "schema": { - "enum": [ - "SecretScan", - "VulnerabilityScan", - "MalwareScan", - "ComplianceScan", - "CloudComplianceScan" - ], - "type": "string" - } - } - ], "responses": { "200": { "description": "OK", @@ -2535,7 +2471,7 @@ "application/json": { "schema": { "type": "array", - "items": { "$ref": "#/components/schemas/ModelBasicNode" } + "items": { "$ref": "#/components/schemas/ModelScanResultBasicNode" } } } } @@ -7456,6 +7392,27 @@ } } }, + "ModelNodesInScanResultRequest": { + "required": ["result_ids", "scan_type"], + "type": "object", + "properties": { + "result_ids": { + "type": "array", + "items": { "type": "string" }, + "nullable": true + }, + "scan_type": { + "enum": [ + "SecretScan", + "VulnerabilityScan", + "MalwareScan", + "ComplianceScan", + "CloudComplianceScan" + ], + "type": "string" + } + } + }, "ModelPasswordResetRequest": { "required": ["email"], "type": "object", diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES index 0ba98b9430..ab70271bd1 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES +++ b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES @@ -82,6 +82,7 @@ models/ModelMalwareScanResult.ts models/ModelMalwareScanTriggerReq.ts models/ModelMessageResponse.ts models/ModelNodeIdentifier.ts +models/ModelNodesInScanResultRequest.ts models/ModelPasswordResetRequest.ts models/ModelPasswordResetVerifyRequest.ts models/ModelPod.ts diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/apis/ScanResultsApi.ts b/deepfence_frontend/apps/dashboard/src/api/generated/apis/ScanResultsApi.ts index 9f15a1cf83..03fd842ba9 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/apis/ScanResultsApi.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/apis/ScanResultsApi.ts @@ -17,8 +17,8 @@ import * as runtime from '../runtime'; import type { ApiDocsBadRequestResponse, ApiDocsFailureResponse, - ModelBasicNode, ModelDownloadReportResponse, + ModelNodesInScanResultRequest, ModelScanResultBasicNode, ModelScanResultsActionRequest, ModelScanResultsMaskRequest, @@ -28,10 +28,10 @@ import { ApiDocsBadRequestResponseToJSON, ApiDocsFailureResponseFromJSON, ApiDocsFailureResponseToJSON, - ModelBasicNodeFromJSON, - ModelBasicNodeToJSON, ModelDownloadReportResponseFromJSON, ModelDownloadReportResponseToJSON, + ModelNodesInScanResultRequestFromJSON, + ModelNodesInScanResultRequestToJSON, ModelScanResultBasicNodeFromJSON, ModelScanResultBasicNodeToJSON, ModelScanResultsActionRequestFromJSON, @@ -54,9 +54,8 @@ export interface DownloadScanResultsRequest { scanType: DownloadScanResultsScanTypeEnum; } -export interface GetAllNodesInScanResultRequest { - resultId: string; - scanType: GetAllNodesInScanResultScanTypeEnum; +export interface GetAllNodesInScanResultsRequest { + modelNodesInScanResultRequest?: ModelNodesInScanResultRequest; } export interface MaskScanResultRequest { @@ -128,37 +127,21 @@ export interface ScanResultsApiInterface { */ downloadScanResults(requestParameters: DownloadScanResultsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; - /** - * Get all nodes in given scan result - * @summary Get all nodes in given scan result - * @param {string} resultId - * @param {'SecretScan' | 'VulnerabilityScan' | 'MalwareScan' | 'ComplianceScan' | 'CloudComplianceScan'} scanType - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ScanResultsApiInterface - */ - getAllNodesInScanResultRaw(requestParameters: GetAllNodesInScanResultRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>>; - - /** - * Get all nodes in given scan result - * Get all nodes in given scan result - */ - getAllNodesInScanResult(requestParameters: GetAllNodesInScanResultRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; - /** * Get all nodes in given scan result ids * @summary Get all nodes in given scan result ids + * @param {ModelNodesInScanResultRequest} [modelNodesInScanResultRequest] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ScanResultsApiInterface */ - getAllNodesInScanResultsRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>>; + getAllNodesInScanResultsRaw(requestParameters: GetAllNodesInScanResultsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>>; /** * Get all nodes in given scan result ids * Get all nodes in given scan result ids */ - getAllNodesInScanResults(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + getAllNodesInScanResults(requestParameters: GetAllNodesInScanResultsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; /** * Mask scan results @@ -340,59 +323,17 @@ export class ScanResultsApi extends runtime.BaseAPI implements ScanResultsApiInt return await response.value(); } - /** - * Get all nodes in given scan result - * Get all nodes in given scan result - */ - async getAllNodesInScanResultRaw(requestParameters: GetAllNodesInScanResultRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { - if (requestParameters.resultId === null || requestParameters.resultId === undefined) { - throw new runtime.RequiredError('resultId','Required parameter requestParameters.resultId was null or undefined when calling getAllNodesInScanResult.'); - } - - if (requestParameters.scanType === null || requestParameters.scanType === undefined) { - throw new runtime.RequiredError('scanType','Required parameter requestParameters.scanType was null or undefined when calling getAllNodesInScanResult.'); - } - - const queryParameters: any = {}; - - const headerParameters: runtime.HTTPHeaders = {}; - - if (this.configuration && this.configuration.accessToken) { - const token = this.configuration.accessToken; - const tokenString = await token("bearer_token", []); - - if (tokenString) { - headerParameters["Authorization"] = `Bearer ${tokenString}`; - } - } - const response = await this.request({ - path: `/deepfence/scan/nodes/{scan_type}/{result_id}`.replace(`{${"result_id"}}`, encodeURIComponent(String(requestParameters.resultId))).replace(`{${"scan_type"}}`, encodeURIComponent(String(requestParameters.scanType))), - method: 'GET', - headers: headerParameters, - query: queryParameters, - }, initOverrides); - - return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelBasicNodeFromJSON)); - } - - /** - * Get all nodes in given scan result - * Get all nodes in given scan result - */ - async getAllNodesInScanResult(requestParameters: GetAllNodesInScanResultRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - const response = await this.getAllNodesInScanResultRaw(requestParameters, initOverrides); - return await response.value(); - } - /** * Get all nodes in given scan result ids * Get all nodes in given scan result ids */ - async getAllNodesInScanResultsRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { + async getAllNodesInScanResultsRaw(requestParameters: GetAllNodesInScanResultsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; + headerParameters['Content-Type'] = 'application/json'; + if (this.configuration && this.configuration.accessToken) { const token = this.configuration.accessToken; const tokenString = await token("bearer_token", []); @@ -403,9 +344,10 @@ export class ScanResultsApi extends runtime.BaseAPI implements ScanResultsApiInt } const response = await this.request({ path: `/deepfence/scan/nodes-in-result`, - method: 'GET', + method: 'POST', headers: headerParameters, query: queryParameters, + body: ModelNodesInScanResultRequestToJSON(requestParameters.modelNodesInScanResultRequest), }, initOverrides); return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(ModelScanResultBasicNodeFromJSON)); @@ -415,8 +357,8 @@ export class ScanResultsApi extends runtime.BaseAPI implements ScanResultsApiInt * Get all nodes in given scan result ids * Get all nodes in given scan result ids */ - async getAllNodesInScanResults(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - const response = await this.getAllNodesInScanResultsRaw(initOverrides); + async getAllNodesInScanResults(requestParameters: GetAllNodesInScanResultsRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const response = await this.getAllNodesInScanResultsRaw(requestParameters, initOverrides); return await response.value(); } @@ -558,14 +500,3 @@ export const DownloadScanResultsScanTypeEnum = { CloudComplianceScan: 'CloudComplianceScan' } as const; export type DownloadScanResultsScanTypeEnum = typeof DownloadScanResultsScanTypeEnum[keyof typeof DownloadScanResultsScanTypeEnum]; -/** - * @export - */ -export const GetAllNodesInScanResultScanTypeEnum = { - SecretScan: 'SecretScan', - VulnerabilityScan: 'VulnerabilityScan', - MalwareScan: 'MalwareScan', - ComplianceScan: 'ComplianceScan', - CloudComplianceScan: 'CloudComplianceScan' -} as const; -export type GetAllNodesInScanResultScanTypeEnum = typeof GetAllNodesInScanResultScanTypeEnum[keyof typeof GetAllNodesInScanResultScanTypeEnum]; diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelNodesInScanResultRequest.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelNodesInScanResultRequest.ts new file mode 100644 index 0000000000..bace9fb2ca --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelNodesInScanResultRequest.ts @@ -0,0 +1,89 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Deepfence ThreatMapper + * Deepfence Runtime API provides programmatic control over Deepfence microservice securing your container, kubernetes and cloud deployments. The API abstracts away underlying infrastructure details like cloud provider, container distros, container orchestrator and type of deployment. This is one uniform API to manage and control security alerts, policies and response to alerts for microservices running anywhere i.e. managed pure greenfield container deployments or a mix of containers, VMs and serverless paradigms like AWS Fargate. + * + * The version of the OpenAPI document: 2.0.0 + * Contact: community@deepfence.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelNodesInScanResultRequest + */ +export interface ModelNodesInScanResultRequest { + /** + * + * @type {Array} + * @memberof ModelNodesInScanResultRequest + */ + result_ids: Array | null; + /** + * + * @type {string} + * @memberof ModelNodesInScanResultRequest + */ + scan_type: ModelNodesInScanResultRequestScanTypeEnum; +} + + +/** + * @export + */ +export const ModelNodesInScanResultRequestScanTypeEnum = { + SecretScan: 'SecretScan', + VulnerabilityScan: 'VulnerabilityScan', + MalwareScan: 'MalwareScan', + ComplianceScan: 'ComplianceScan', + CloudComplianceScan: 'CloudComplianceScan' +} as const; +export type ModelNodesInScanResultRequestScanTypeEnum = typeof ModelNodesInScanResultRequestScanTypeEnum[keyof typeof ModelNodesInScanResultRequestScanTypeEnum]; + + +/** + * Check if a given object implements the ModelNodesInScanResultRequest interface. + */ +export function instanceOfModelNodesInScanResultRequest(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "result_ids" in value; + isInstance = isInstance && "scan_type" in value; + + return isInstance; +} + +export function ModelNodesInScanResultRequestFromJSON(json: any): ModelNodesInScanResultRequest { + return ModelNodesInScanResultRequestFromJSONTyped(json, false); +} + +export function ModelNodesInScanResultRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelNodesInScanResultRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'result_ids': json['result_ids'], + 'scan_type': json['scan_type'], + }; +} + +export function ModelNodesInScanResultRequestToJSON(value?: ModelNodesInScanResultRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'result_ids': value.result_ids, + 'scan_type': value.scan_type, + }; +} + diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts index 95d0733776..9eb4730d6f 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts @@ -63,6 +63,7 @@ export * from './ModelMalwareScanResult'; export * from './ModelMalwareScanTriggerReq'; export * from './ModelMessageResponse'; export * from './ModelNodeIdentifier'; +export * from './ModelNodesInScanResultRequest'; export * from './ModelPasswordResetRequest'; export * from './ModelPasswordResetVerifyRequest'; export * from './ModelPod'; diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx index ca6cf10d1d..e3f7f798d9 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx @@ -88,7 +88,7 @@ export const VulnerabilitiesCountsCard = ({

{title}

Details 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 ec376eddae..be7c56ee10 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -22,7 +22,8 @@ import { import { getScanResultsApiClient, getSearchApiClient } from '@/api/api'; import { ApiDocsBadRequestResponse, - GetAllNodesInScanResultScanTypeEnum, + ModelNodesInScanResultRequestScanTypeEnum, + ModelScanResultBasicNode, } from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; @@ -73,11 +74,6 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ const page = getPageFromSearchParams(searchParams); - let offsetSize = page * PAGE_SIZE; - if (offsetSize > 1000) { - offsetSize = (page - 1) * PAGE_SIZE; - } - const result = await makeRequest({ apiFunction: getSearchApiClient().searchVulnerabilities, apiArgs: [ @@ -104,7 +100,7 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: offsetSize, + offset: page * PAGE_SIZE, size: PAGE_SIZE, }, }, @@ -173,13 +169,14 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ if (ApiError.isApiError(countsResult)) { throw countsResult.value(); } - debugger; const allNodes = await makeRequest({ apiFunction: getScanResultsApiClient().getAllNodesInScanResults, apiArgs: [ { - resultId: result.map((res) => res.cve_id), - scanType: GetAllNodesInScanResultScanTypeEnum.VulnerabilityScan, + modelNodesInScanResultRequest: { + result_ids: result.map((res) => res.cve_id), + scan_type: ModelNodesInScanResultRequestScanTypeEnum.VulnerabilityScan, + }, }, ], errorHandler: async (r) => { @@ -194,14 +191,27 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ }, }); + if (ApiError.isApiError(allNodes)) { + throw allNodes.value(); + } + if (result === null) { return results; } - debugger; - const groupByNodes = groupBy(allNodes, 'name'); - console.log(groupByNodes); + const groupByNodes = allNodes.reduce((acc, data) => { + const { result_id, ...rest } = data; + acc[result_id] = rest as ModelScanResultBasicNode; + return acc; + }, {} as { [k: string]: ModelScanResultBasicNode }); + results.vulnerabilities = result .map((res) => { + const resources = groupByNodes[res.cve_id]; + const resourcesLen = resources?.basic_nodes?.length ?? 0; + let resourcesAsset = resources?.basic_nodes?.[0]?.name ?? ''; + if (resourcesAsset && resourcesLen > 1) { + resourcesAsset = `${resourcesAsset} + ${resourcesLen - 1} more`; + } return { cveId: res.cve_id, cveDescription: res.cve_description, @@ -210,7 +220,7 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ cveSeverity: res.cve_severity, cveCVSSScore: res.cve_cvss_score, cveAttackVector: res.parsed_attack_vector, - // cveAssetAffected: groupByNodes[res.cve_id][0]., + cveAssetAffected: resourcesAsset, active: res.has_live_connection, exploitPoc: res.exploit_poc, }; 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 b940f8529b..f589a08921 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { groupBy, toNumber } from 'lodash-es'; +import { toNumber } from 'lodash-es'; import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; @@ -22,7 +22,8 @@ import { import { getScanResultsApiClient, getSearchApiClient } from '@/api/api'; import { ApiDocsBadRequestResponse, - GetAllNodesInScanResultScanTypeEnum, + ModelNodesInScanResultRequestScanTypeEnum, + ModelScanResultBasicNode, } from '@/api/generated'; import { DFLink } from '@/components/DFLink'; import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability'; @@ -72,12 +73,6 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ }; const page = getPageFromSearchParams(searchParams); - - let offsetSize = page * PAGE_SIZE; - if (offsetSize > 1000) { - offsetSize = (page - 1) * PAGE_SIZE; - } - const result = await makeRequest({ apiFunction: getSearchApiClient().searchVulnerabilities, apiArgs: [ @@ -98,7 +93,7 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: offsetSize, + offset: page * PAGE_SIZE, size: PAGE_SIZE, }, }, @@ -128,16 +123,10 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ node_filter: { filters: { contains_filter: { - filter_in: { - exploitability_score: [1, 2, 3], - }, + filter_in: {}, }, order_filter: { - order_fields: [ - 'exploitability_score', - 'cve_severity', - 'vulnerability_score', - ], + order_fields: [], }, match_filter: { filter_in: {}, @@ -146,8 +135,8 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: 0, - size: 1000, + offset: page * PAGE_SIZE, + size: 10 * PAGE_SIZE, }, }, }, @@ -167,13 +156,15 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ if (ApiError.isApiError(countsResult)) { throw countsResult.value(); } - debugger; + const allNodes = await makeRequest({ apiFunction: getScanResultsApiClient().getAllNodesInScanResults, apiArgs: [ { - resultId: result.map((res) => res.cve_id), - scanType: GetAllNodesInScanResultScanTypeEnum.VulnerabilityScan, + modelNodesInScanResultRequest: { + result_ids: result.map((res) => res.cve_id), + scan_type: ModelNodesInScanResultRequestScanTypeEnum.VulnerabilityScan, + }, }, ], errorHandler: async (r) => { @@ -188,14 +179,27 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ }, }); + if (ApiError.isApiError(allNodes)) { + throw allNodes.value(); + } + if (result === null) { return results; } - debugger; - const groupByNodes = groupBy(allNodes, 'name'); - console.log(groupByNodes); + const groupByNodes = allNodes.reduce((acc, data) => { + const { result_id, ...rest } = data; + acc[result_id] = rest as ModelScanResultBasicNode; + return acc; + }, {} as { [k: string]: ModelScanResultBasicNode }); + results.vulnerabilities = result .map((res) => { + const resources = groupByNodes[res.cve_id]; + const resourcesLen = resources?.basic_nodes?.length ?? 0; + let resourcesAsset = resources?.basic_nodes?.[0]?.name ?? ''; + if (resourcesAsset && resourcesLen > 1) { + resourcesAsset = `${resourcesAsset} + ${resourcesLen - 1} more`; + } return { cveId: res.cve_id, cveDescription: res.cve_description, @@ -204,7 +208,7 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ cveSeverity: res.cve_severity, cveCVSSScore: res.cve_cvss_score, cveAttackVector: res.parsed_attack_vector, - // cveAssetAffected: groupByNodes[res.cve_id][0]., + cveAssetAffected: resourcesAsset, active: res.has_live_connection, exploitPoc: res.exploit_poc, }; @@ -377,7 +381,7 @@ const UniqueVulnerabilities = () => { - MOST EXPLOITABLE VULNERABILITIES + UNIQUE VULNERABILITIES {navigation.state === 'loading' ? : null} diff --git a/deepfence_frontend/apps/dashboard/src/routes/private.tsx b/deepfence_frontend/apps/dashboard/src/routes/private.tsx index c14082b594..b7245898d7 100644 --- a/deepfence_frontend/apps/dashboard/src/routes/private.tsx +++ b/deepfence_frontend/apps/dashboard/src/routes/private.tsx @@ -36,6 +36,7 @@ import { module as vulnerabilityScanSumary } from '@/features/onboard/pages/Vuln import { Registries } from '@/features/registries/pages/Registries'; import { vulnerabilityApiLoader } from '@/features/vulnerabilities/api/apiLoader'; import { module as mostExploitableVulnerabilities } from '@/features/vulnerabilities/pages/MostExploitableVulnerabilities'; +import { module as uniqueVulnerabilities } from '@/features/vulnerabilities/pages/UniqueVulnerabilities'; import { module as vulnerability } from '@/features/vulnerabilities/pages/Vulnerability'; import { module as vulnerabilityDetails } from '@/features/vulnerabilities/pages/VulnerabilityDetailModal'; import { module as vulnerabilityScanResults } from '@/features/vulnerabilities/pages/VulnerabilityScanResults'; @@ -223,6 +224,18 @@ export const privateRoutes: CustomRouteObject[] = [ }, ], }, + { + path: 'vulnerability/unique-vulnerabilities', + ...uniqueVulnerabilities, + meta: { title: 'Unique Vulnerabilities' }, + children: [ + { + path: ':cveId', + ...vulnerabilityDetails, + meta: { title: 'Unique Vulnerability Details' }, + }, + ], + }, ], }, { From aa725b3f712be259fd52559b1658451181368f29 Mon Sep 17 00:00:00 2001 From: Manan Vaghasiya Date: Wed, 1 Mar 2023 12:30:30 +0000 Subject: [PATCH 4/6] fix links --- .../components/landing/VulnerabilitiesCountsCard.tsx | 4 +++- .../src/features/vulnerabilities/pages/Vulnerability.tsx | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx index e3f7f798d9..bc7ca6f92f 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/components/landing/VulnerabilitiesCountsCard.tsx @@ -77,10 +77,12 @@ export const VulnerabilitiesCountsCard = ({ title, data, loading, + detailsLink, }: { title: string; data?: VulnerabilitiesCountsCardData; loading: boolean; + detailsLink: string; }) => { const { mode } = useTheme(); return ( @@ -88,7 +90,7 @@ export const VulnerabilitiesCountsCard = ({

{title}

Details diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx index 8e5149a3cc..822cd2420e 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/Vulnerability.tsx @@ -321,7 +321,11 @@ const Vulnerability = () => {
+ } > @@ -329,6 +333,7 @@ const Vulnerability = () => { return ( @@ -343,6 +348,7 @@ const Vulnerability = () => { fallback={ } @@ -352,6 +358,7 @@ const Vulnerability = () => { return ( From 10f3235a7d45c099f62d2b8e45e77ffa424fe66f Mon Sep 17 00:00:00 2001 From: Manan Vaghasiya Date: Wed, 1 Mar 2023 12:34:35 +0000 Subject: [PATCH 5/6] show max 1000 rows for most exploitable vulnerabilities --- .../pages/MostExploitableVulnerabilities.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 be7c56ee10..cd7e4e0ca7 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { groupBy, toNumber } from 'lodash-es'; +import { toNumber } from 'lodash-es'; import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; @@ -148,8 +148,8 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ in_field_filter: null, }, window: { - offset: 0, - size: 1000, + offset: page * PAGE_SIZE, + size: 10 * PAGE_SIZE, }, }, }, @@ -230,6 +230,10 @@ async function getVulnerability(searchParams: URLSearchParams): Promise<{ results.currentPage = page; results.totalRows = page * PAGE_SIZE + countsResult.count; + if (results.totalRows > 1000) { + results.totalRows = 1000; + } + return results; } const loader = async ({ From 1278046350868546c31745e540dd0ea7786431b2 Mon Sep 17 00:00:00 2001 From: Manan Vaghasiya Date: Wed, 1 Mar 2023 13:32:25 +0000 Subject: [PATCH 6/6] style fixes --- .../pages/MostExploitableVulnerabilities.tsx | 8 ++++---- .../vulnerabilities/pages/UniqueVulnerabilities.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) 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 cd7e4e0ca7..557bb99a96 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/MostExploitableVulnerabilities.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { toNumber } from 'lodash-es'; +import { capitalize, toNumber } from 'lodash-es'; import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; @@ -266,7 +266,7 @@ const MostExploitableVulnerabilities = () => { > <>
-
+
@@ -317,7 +317,7 @@ const MostExploitableVulnerabilities = () => { columnHelper.accessor('cveAttackVector', { enableSorting: false, enableResizing: false, - cell: (info) => info.getValue(), + cell: (info) => capitalize(info.getValue()), header: () => 'Attack Vector', minSize: 100, size: 120, @@ -399,7 +399,7 @@ const MostExploitableVulnerabilities = () => { MOST EXPLOITABLE VULNERABILITIES - + {navigation.state === 'loading' ? : null}
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 f589a08921..9d0b07cd5c 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/UniqueVulnerabilities.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { toNumber } from 'lodash-es'; +import { capitalize, toNumber } from 'lodash-es'; import { Suspense, useMemo } from 'react'; import { IconContext } from 'react-icons'; import { HiArrowSmLeft, HiExternalLink } from 'react-icons/hi'; @@ -250,7 +250,7 @@ const UniqueVulnerabilities = () => { > <>
-
+
@@ -301,7 +301,7 @@ const UniqueVulnerabilities = () => { columnHelper.accessor('cveAttackVector', { enableSorting: false, enableResizing: false, - cell: (info) => info.getValue(), + cell: (info) => capitalize(info.getValue()), header: () => 'Attack Vector', minSize: 100, size: 120, @@ -383,7 +383,7 @@ const UniqueVulnerabilities = () => { UNIQUE VULNERABILITIES - + {navigation.state === 'loading' ? : null}