Skip to content

Commit

Permalink
[Cloud Security][Vulnerabilities] Resource page (#159873)
Browse files Browse the repository at this point in the history
  • Loading branch information
opauloh authored Jun 19, 2023
1 parent 3849d14 commit 0163bf3
Show file tree
Hide file tree
Showing 18 changed files with 1,033 additions and 343 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,18 @@ export const useFilteredDataView = (indexPattern: string) => {
throw new Error('Error fetching fields for the index pattern');
}

// Filter out fields that are not present in the index pattern passed as a parameter
dataView.fields = dataView.fields.filter((field) =>
indexPatternFields.some((indexPatternField) => indexPatternField.name === field.name)
) as DataView['fields'];

// Insert fields that are present in the index pattern but not in the data view
indexPatternFields.forEach((indexPatternField) => {
if (!dataView.fields.some((field) => field.name === indexPatternField.name)) {
dataView.fields.push(indexPatternField as DataView['fields'][0]);
}
});

return dataView;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ export const NO_VULNERABILITIES_STATUS_TEST_SUBJ = {
};

export const VULNERABILITIES_CONTAINER_TEST_SUBJ = 'vulnerabilities_container';

export const VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ = 'vuknerabilities_cvss_score_badge';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { css } from '@emotion/react';
import { float } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { getCvsScoreColor, getSeverityStatusColor } from '../common/utils/get_vulnerability_colors';
import { VulnSeverity } from '../../common/types';
import { VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ } from './test_subjects';

interface CVSScoreBadgeProps {
score: float;
Expand All @@ -32,9 +33,9 @@ export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => {
.euiBadge__text {
display: flex;
}
display: flex;
width: 62px;
`}
data-test-subj={VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ}
>
{versionDisplay && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@
* 2.0.
*/
import React, { useCallback } from 'react';
import {
EuiSpacer,
EuiButtonEmpty,
type EuiDescriptionListProps,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import { EuiSpacer, EuiButtonEmpty, type EuiDescriptionListProps } from '@elastic/eui';
import { Link, useParams } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { generatePath } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { CspInlineDescriptionList } from '../../../../components/csp_inline_description_list';
import type { Evaluation } from '../../../../../common/types';
import { CspFinding } from '../../../../../common/schemas/csp_finding';
Expand Down Expand Up @@ -50,7 +45,14 @@ const getDefaultQuery = ({

const BackToResourcesButton = () => (
<Link to={generatePath(findingsNavigation.findings_by_resource.path)}>
<EuiButtonEmpty iconType={'arrowLeft'}>
<EuiButtonEmpty
iconType="arrowLeft"
css={css`
& .euiButtonEmpty__content {
padding: 0;
}
`}
>
<FormattedMessage
id="xpack.csp.findings.resourceFindings.backToResourcesPageButtonLabel"
defaultMessage="Back to resources"
Expand Down Expand Up @@ -196,32 +198,26 @@ export const ResourceFindings = ({ dataView }: FindingsBaseProps) => {
}}
loading={resourceFindings.isFetching}
/>
<EuiSpacer size="m" />
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<PageTitle>
<PageTitleText
title={
<CloudPosturePageTitle
title={i18n.translate(
'xpack.csp.findings.resourceFindings.resourceFindingsPageTitle',
{
defaultMessage: '{resourceName} {hyphen} Findings',
values: {
resourceName: resourceFindings.data?.resourceName,
hyphen: resourceFindings.data?.resourceName ? '-' : '',
},
}
)}
/>
}
<BackToResourcesButton />
<EuiSpacer size="xs" />
<PageTitle>
<PageTitleText
title={
<CloudPosturePageTitle
title={i18n.translate(
'xpack.csp.findings.resourceFindings.resourceFindingsPageTitle',
{
defaultMessage: '{resourceName} {hyphen} Findings',
values: {
resourceName: resourceFindings.data?.resourceName,
hyphen: resourceFindings.data?.resourceName ? '-' : '',
},
}
)}
/>
</PageTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<BackToResourcesButton />
</EuiFlexItem>
</EuiFlexGroup>
}
/>
</PageTitle>
<EuiSpacer />
{resourceFindings.data && (
<CspInlineDescriptionList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export const PageTitle: React.FC = ({ children }) => (
</EuiTitle>
);

export const PageTitleText = ({ title }: { title: React.ReactNode }) => <h2>{title}</h2>;
export const PageTitleText = ({ title }: { title: React.ReactNode }) => (
<EuiText grow={false} size="s">
<h1>{title}</h1>
</EuiText>
);

export const getExpandColumn = <T extends unknown>({
onClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { Redirect, Switch, useHistory, useLocation } from 'react-router-dom';
import { Redirect, Switch, useHistory, useLocation, matchPath } from 'react-router-dom';
import { Route } from '@kbn/shared-ux-router';
import { Configurations } from '../configurations';
import { cloudPosturePages, findingsNavigation } from '../../common/navigation/constants';
Expand All @@ -33,63 +33,76 @@ export const Findings = () => {
history.push({ pathname: findingsNavigation.findings_default.path });
};

const isResourcesVulnerabilitiesPage = matchPath(location.pathname, {
path: findingsNavigation.resource_vulnerabilities.path,
})?.isExact;

const isResourcesFindingsPage = matchPath(location.pathname, {
path: findingsNavigation.resource_findings.path,
})?.isExact;

const showHeader = !isResourcesVulnerabilitiesPage && !isResourcesFindingsPage;

const isVulnerabilitiesTabSelected = (pathname: string) => {
return (
pathname === findingsNavigation.vulnerabilities.path ||
pathname === findingsNavigation.vulnerabilities_by_resource.path ||
pathname === findingsNavigation.resource_vulnerabilities.path
pathname === findingsNavigation.vulnerabilities_by_resource.path
);
};

return (
<>
<EuiTitle size="l">
<h1>
<FormattedMessage id="xpack.csp.findings.title" defaultMessage="Findings" />
</h1>
</EuiTitle>
<EuiSpacer />
<EuiTabs size="l">
<EuiTab
key="vuln_mgmt"
onClick={navigateToVulnerabilitiesTab}
isSelected={isVulnerabilitiesTabSelected(location.pathname)}
>
<EuiFlexGroup responsive={false} alignItems="center" direction="row" gutterSize="s">
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.csp.findings.tabs.vulnerabilities"
defaultMessage="Vulnerabilities"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
css={css`
display: block;
`}
label="Beta"
tooltipContent={
{showHeader && (
<>
<EuiTitle size="l">
<h1>
<FormattedMessage id="xpack.csp.findings.title" defaultMessage="Findings" />
</h1>
</EuiTitle>
<EuiSpacer />
<EuiTabs size="l">
<EuiTab
key="vuln_mgmt"
onClick={navigateToVulnerabilitiesTab}
isSelected={isVulnerabilitiesTabSelected(location.pathname)}
>
<EuiFlexGroup responsive={false} alignItems="center" direction="row" gutterSize="s">
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.csp.findings.betaLabel"
defaultMessage="This functionality is in beta and is subject to change. The design and code is less mature than official generally available features and is being provided as-is with no warranties. Beta features are not subject to the support service level agreement of official generally available features."
id="xpack.csp.findings.tabs.vulnerabilities"
defaultMessage="Vulnerabilities"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiBetaBadge
css={css`
display: block;
`}
label="Beta"
tooltipContent={
<FormattedMessage
id="xpack.csp.findings.betaLabel"
defaultMessage="This functionality is in beta and is subject to change. The design and code is less mature than official generally available features and is being provided as-is with no warranties. Beta features are not subject to the support service level agreement of official generally available features."
/>
}
tooltipPosition="bottom"
/>
}
tooltipPosition="bottom"
</EuiFlexItem>
</EuiFlexGroup>
</EuiTab>
<EuiTab
key="configurations"
onClick={navigateToConfigurationsTab}
isSelected={!isVulnerabilitiesTabSelected(location.pathname)}
>
<FormattedMessage
id="xpack.csp.findings.tabs.misconfigurations"
defaultMessage="Misconfigurations"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTab>
<EuiTab
key="configurations"
onClick={navigateToConfigurationsTab}
isSelected={!isVulnerabilitiesTabSelected(location.pathname)}
>
<FormattedMessage
id="xpack.csp.findings.tabs.misconfigurations"
defaultMessage="Misconfigurations"
/>
</EuiTab>
</EuiTabs>
</EuiTab>
</EuiTabs>
</>
)}
<Switch>
<Route
exact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getSafeVulnerabilitiesQueryFilter } from '../../../../common/utils/get_
import { useKibana } from '../../../common/hooks/use_kibana';
import { showErrorToast } from '../../../common/utils/show_error_toast';
import { FindingsBaseEsQuery } from '../../../common/types';
import { VulnerabilityRecord } from '../types';
type LatestFindingsRequest = IKibanaSearchRequest<SearchRequest>;
type LatestFindingsResponse = IKibanaSearchResponse<SearchResponse<any, FindingsAggs>>;

Expand Down Expand Up @@ -59,7 +60,7 @@ export const useLatestVulnerabilities = (options: VulnerabilitiesQuery) => {
);

return {
page: hits.hits.map((hit) => hit._source!),
page: hits.hits.map((hit) => hit._source!) as VulnerabilityRecord[],
total: number.is(hits.total) ? hits.total : 0,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,17 @@ export const getQuery = ({
});
const getFirstKey = (
buckets: AggregationsMultiBucketAggregateBase<AggregationsStringTermsBucketKeys>['buckets']
): undefined | string => {
if (!!Array.isArray(buckets) && !!buckets.length) return buckets[0].key;
) => {
return !!Array.isArray(buckets) && !!buckets.length ? (buckets[0].key as string) : '';
};
const createVulnerabilitiesByResource = (resource: FindingsAggBucket) => ({
'resource.id': resource.key,
'resource.name': getFirstKey(resource.name.buckets),
'cloud.region': getFirstKey(resource.region.buckets),
resource: {
id: resource.key,
name: getFirstKey(resource.name.buckets),
},
cloud: {
region: getFirstKey(resource.region.buckets),
},
vulnerabilities_count: resource.doc_count,
severity_map: {
critical: resource.critical.doc_count,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export const useStyles = () => {
& .euiDataGridRowCell__expandActions > [data-test-subj='euiDataGridCellExpandButton'] {
display: none;
}
& .euiDataGridRowCell__contentByHeight + .euiDataGridRowCell__expandActions {
padding: 0;
}
& .euiDataGridRowCell__expandFlex {
align-items: center;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ export interface VulnerabilityRecord {
version: string;
};
cloud: {
image: {
image?: {
id: string;
};
provider: string;
instance: {
provider?: string;
instance?: {
id: string;
};
machine: {
machine?: {
type: string;
};
region: string;
availability_zone: string;
service: {
availability_zone?: string;
service?: {
name: string;
};
account: {
account?: {
id: string;
};
};
Expand Down
Loading

0 comments on commit 0163bf3

Please sign in to comment.