import React from "react"; import { Column } from "react-table"; import { InjectedRouter } from "react-router"; import ReactTooltip from "react-tooltip"; import { formatSoftwareType, ISoftware } from "interfaces/software"; import { IVulnerability } from "interfaces/vulnerability"; import PATHS from "router/paths"; import { formatFloatAsPercentage } from "utilities/helpers"; import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants"; import Button from "components/buttons/Button"; import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; import TextCell from "components/TableContainer/DataTable/TextCell"; import TooltipWrapper from "components/TooltipWrapper"; import ViewAllHostsLink from "components/ViewAllHostsLink"; import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip"; // NOTE: cellProps come from react-table // more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties interface ICellProps { cell: { value: number | string | IVulnerability[]; }; row: { original: ISoftware; }; } interface IStringCellProps extends ICellProps { cell: { value: string; }; } interface INumberCellProps extends ICellProps { cell: { value: number; }; } interface IVulnCellProps extends ICellProps { cell: { value: IVulnerability[]; }; } interface IHeaderProps { column: { title: string; isSortedDesc: boolean; }; } const condenseVulnerabilities = ( vulnerabilities: IVulnerability[] ): string[] => { const condensed = (vulnerabilities?.length && vulnerabilities .slice(-3) .map((v) => v.cve) .reverse()) || []; return vulnerabilities.length > 3 ? condensed.concat(`+${vulnerabilities.length - 3} more`) : condensed; }; const renderBundleTooltip = (name: string, bundle: string) => ( Bundle identifier:
${bundle}
`} > {name} ); const getMaxProbability = (vulns: IVulnerability[]) => vulns.reduce( (max, { epss_probability }) => Math.max(max, epss_probability || 0), 0 ); const generateEPSSColumnHeader = (isSandboxMode = false) => { return { Header: (headerProps: IHeaderProps): JSX.Element => { const titleWithToolTip = ( in the next 30 days (EPSS probability). This data is
reported by FIRST.org. `} > Probability of exploit
); return ( <> {isSandboxMode && } ); }, disableSortBy: false, accessor: "vulnerabilities", Cell: (cellProps: IVulnCellProps): JSX.Element => { const vulns = cellProps.cell.value || []; const maxProbability = (!!vulns.length && getMaxProbability(vulns)) || 0; const displayValue = (maxProbability && formatFloatAsPercentage(maxProbability)) || DEFAULT_EMPTY_CELL_VALUE; return ( {displayValue} ); }, }; }; const generateVulnColumnHeader = () => { return { title: "Vulnerabilities", Header: "Vulnerabilities", disableSortBy: true, accessor: "vulnerabilities", Cell: (cellProps: IVulnCellProps): JSX.Element => { const vulnerabilities = cellProps.cell.value || []; const tooltipText = condenseVulnerabilities(vulnerabilities)?.map( (value) => { return ( {value}
); } ); if (!vulnerabilities?.length) { return ---; } return ( <> 1 ? "text-muted tooltip" : "" }`} data-tip data-for={`vulnerabilities__${cellProps.row.original.id}`} data-tip-disable={vulnerabilities.length <= 1} > {vulnerabilities.length === 1 ? vulnerabilities[0].cve : `${vulnerabilities.length} vulnerabilities`} {tooltipText} ); }, }; }; const generateTableHeaders = ( router: InjectedRouter, isPremiumTier?: boolean, isSandboxMode?: boolean, teamId?: number ): Column[] => { const softwareTableHeaders = [ { title: "Name", Header: (cellProps: IHeaderProps): JSX.Element => ( ), disableSortBy: false, accessor: "name", Cell: (cellProps: IStringCellProps): JSX.Element => { const { id, name, bundle_identifier: bundle } = cellProps.row.original; const onClickSoftware = (e: React.MouseEvent) => { // Allows for button to be clickable in a clickable row e.stopPropagation(); router?.push(PATHS.SOFTWARE_DETAILS(id.toString())); }; return ( ); }, sortType: "caseInsensitive", }, { title: "Version", Header: "Version", disableSortBy: true, accessor: "version", Cell: (cellProps: IStringCellProps): JSX.Element => ( ), }, { title: "Type", Header: "Type", disableSortBy: true, accessor: "source", Cell: (cellProps: IStringCellProps): JSX.Element => ( ), }, isPremiumTier ? generateEPSSColumnHeader(isSandboxMode) : generateVulnColumnHeader(), { title: "Hosts", Header: (cellProps: IHeaderProps): JSX.Element => ( ), disableSortBy: false, accessor: "hosts_count", Cell: (cellProps: INumberCellProps): JSX.Element => ( ), }, ]; return softwareTableHeaders; }; export default generateTableHeaders;