Skip to content

Commit

Permalink
[Cloud Security Posture][CNVM] UI Fixes/Enhancements (#155473)
Browse files Browse the repository at this point in the history
### Summary

This PR addresses Fixes and Enhancements for Cloud Native Vulnerability
Management, such as:
- Added Billing usage Callout
- Fixed Badges Inconsistency between the Table and the Flyout
- Added support for custom placeholder in the Search Bar
- Added Filter In/Out option in the Table
- Fixed Published Date was always showing today's date
- Added filter to the table to filter out inconsistent data
- Improved types in the `useLatestVulnerabilities` hook
- Cleaned the Fix version column when there were no fixes available
- Changed score to always display in decimals for consistency
- Reduced vulnerability column default width (still resizable)

### Screenshots

Billing usage Callout


![image](https://user-images.githubusercontent.com/19270322/233539949-558763f7-f71d-4bfd-b536-7f9f0eba61d9.png)

Filters


![image](https://user-images.githubusercontent.com/19270322/233540869-aacfd6d6-d849-41c5-840e-2b27c8d7fe7f.png)

Filter In


![image](https://user-images.githubusercontent.com/19270322/233540893-2a33cc12-7471-49ef-80aa-71981450923d.png)

Filter Out


![image](https://user-images.githubusercontent.com/19270322/233541035-eb428676-b2e6-41a4-9728-1ac60ce67924.png)

Flyout


![image](https://user-images.githubusercontent.com/19270322/233541145-88f9df1a-1f79-4ed8-aca8-bd9ef1158378.png)

Score always in decimals


![image](https://user-images.githubusercontent.com/19270322/233541375-7e06ca18-d129-44f6-bc0f-3c5383393db6.png)
  • Loading branch information
opauloh authored Apr 21, 2023
1 parent e9cc7a8 commit e0d40b7
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 33 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const CLOUDBEAT_VULN_MGMT_AZURE = 'cloudbeat/vuln_mgmt_azure';
export const KSPM_POLICY_TEMPLATE = 'kspm';
export const CSPM_POLICY_TEMPLATE = 'cspm';
export const VULN_MGMT_POLICY_TEMPLATE = 'vuln_mgmt';
export const CNVM_POLICY_TEMPLATE = 'cnvm';
export const SUPPORTED_POLICY_TEMPLATES = [
KSPM_POLICY_TEMPLATE,
CSPM_POLICY_TEMPLATE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
* 2.0.
*/
import React from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import { EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import type { NewPackagePolicy } from '@kbn/fleet-plugin/common';
import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants';
import {
CSPM_POLICY_TEMPLATE,
KSPM_POLICY_TEMPLATE,
VULN_MGMT_POLICY_TEMPLATE,
CNVM_POLICY_TEMPLATE,
} from '../../../common/constants';
import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types';
import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils';
import { RadioGroup } from './csp_boxed_radio_group';
Expand All @@ -21,27 +26,36 @@ interface PolicyTemplateSelectorProps {
disabled: boolean;
}

const getPolicyTemplateLabel = (policyTemplate: CloudSecurityPolicyTemplate) => {
if (policyTemplate === VULN_MGMT_POLICY_TEMPLATE) {
return CNVM_POLICY_TEMPLATE.toUpperCase();
}
return policyTemplate.toUpperCase();
};

export const PolicyTemplateSelector = ({
policy,
selectedTemplate,
setPolicyTemplate,
disabled,
}: PolicyTemplateSelectorProps) => {
const policyTemplates = new Set(policy.inputs.map((input) => input.policy_template!));
const policyTemplates = new Set(
policy.inputs.map((input) => input.policy_template as CloudSecurityPolicyTemplate)
);

return (
<div>
<EuiText color={'subdued'} size="s">
<EuiText color="subdued" size="s">
<FormattedMessage
id="xpack.csp.fleetIntegration.selectIntegrationTypeTitle"
defaultMessage="Select the type of security posture management integration you want to configure"
/>
</EuiText>
<EuiSpacer size="m" />
<RadioGroup
options={Array.from(policyTemplates, (v) => ({ id: v, label: v.toUpperCase() }))}
options={Array.from(policyTemplates, (v) => ({ id: v, label: getPolicyTemplateLabel(v) }))}
idSelected={selectedTemplate}
onChange={(id) => setPolicyTemplate(id as CloudSecurityPolicyTemplate)}
onChange={(id: CloudSecurityPolicyTemplate) => setPolicyTemplate(id)}
disabled={disabled}
/>
</div>
Expand Down Expand Up @@ -69,7 +83,7 @@ interface PolicyTemplateInfoProps {
}

export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) => (
<EuiText color={'subdued'} size="s">
<EuiText color="subdued" size="s">
{postureType === KSPM_POLICY_TEMPLATE && (
<FormattedMessage
id="xpack.csp.fleetIntegration.configureKspmIntegrationDescription"
Expand All @@ -82,6 +96,34 @@ export const PolicyTemplateInfo = ({ postureType }: PolicyTemplateInfoProps) =>
defaultMessage="Select the cloud service provider (CSP) you want to monitor and then fill in the name and description to help identify this integration"
/>
)}
{postureType === VULN_MGMT_POLICY_TEMPLATE && (
<>
<EuiCallOut
iconType="iInCircle"
color="primary"
title={
<FormattedMessage
id="xpack.csp.fleetIntegration.cnvm.additionalChargesCalloutTitle"
defaultMessage="Additional charges on cloud provider billing account."
/>
}
>
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.csp.fleetIntegration.cnvm.additionalChargesCalloutDescription"
defaultMessage="Please note that using this service may result in additional charges on your next cloud provider billing statement due to increased usage."
/>
</p>
</EuiText>
</EuiCallOut>
<EuiSpacer size="m" />
<FormattedMessage
id="xpack.csp.fleetIntegration.cnvm.configureIntegrationDescription"
defaultMessage="Select the cloud service provider (CSP) you want to monitor and then fill in the name and description to help identify this integration"
/>
</>
)}
</EuiText>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export const CVSScoreBadge = ({ score, version }: CVSScoreBadgeProps) => {
.euiBadge__text {
display: flex;
}
display: flex;
width: 62px;
`}
>
{versionDisplay && (
<>
<EuiTextColor color="ghost">{score}</EuiTextColor>
<EuiTextColor color="ghost">{score < 10 ? score.toFixed(1) : score}</EuiTextColor>
<hr
css={css`
width: 1px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ type SearchBarQueryProps = Pick<FindingsBaseURLQuery, 'query' | 'filters'>;
interface FindingsSearchBarProps {
setQuery(v: Partial<SearchBarQueryProps>): void;
loading: boolean;
placeholder?: string;
}

export const FindingsSearchBar = ({
dataView,
loading,
setQuery,
placeholder = i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', {
defaultMessage: 'Search findings (eg. rule.section : "API Server" )',
}),
}: FindingsSearchBarProps & { dataView: DataView }) => {
const { euiTheme } = useEuiTheme();
const {
Expand All @@ -49,9 +53,7 @@ export const FindingsSearchBar = ({
indexPatterns={[dataView]}
onQuerySubmit={setQuery}
onFiltersUpdated={(value: Filter[]) => setQuery({ filters: value })}
placeholder={i18n.translate('xpack.csp.findings.searchBar.searchPlaceholder', {
defaultMessage: 'Search findings (eg. rule.section : "API Server" )',
})}
placeholder={placeholder}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,43 @@ import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constan
import { useKibana } from '../../../common/hooks/use_kibana';
import { showErrorToast } from '../../../common/utils/show_error_toast';
import { MAX_FINDINGS_TO_LOAD } from '../../../common/constants';
import { FindingsBaseEsQuery } from '../../../common/types';
type LatestFindingsRequest = IKibanaSearchRequest<estypes.SearchRequest>;
type LatestFindingsResponse = IKibanaSearchResponse<estypes.SearchResponse<any, FindingsAggs>>;

interface FindingsAggs {
count: estypes.AggregationsMultiBucketAggregateBase<estypes.AggregationsStringRareTermsBucketKeys>;
}

export const getFindingsQuery = ({ query, sort }: any) => ({
interface VulnerabilitiesQuery extends FindingsBaseEsQuery {
sort: estypes.Sort;
enabled: boolean;
}

export const getFindingsQuery = ({ query, sort }: VulnerabilitiesQuery) => ({
index: LATEST_VULNERABILITIES_INDEX_PATTERN,
query,
query: {
...query,
bool: {
...query?.bool,
filter: [
...(query?.bool?.filter || []),
{ exists: { field: 'vulnerability.score.base' } },
{ exists: { field: 'vulnerability.score.version' } },
{ exists: { field: 'resource.name' } },
{ match_phrase: { 'vulnerability.enumeration': 'CVE' } },
],
must_not: [
...(query?.bool?.must_not || []),
{ match_phrase: { 'vulnerability.severity': 'UNKNOWN' } },
],
},
},
size: MAX_FINDINGS_TO_LOAD,
sort,
});

export const useLatestVulnerabilities = (options: any) => {
export const useLatestVulnerabilities = (options: VulnerabilitiesQuery) => {
const {
data,
notifications: { toasts },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';

export const FILTER_IN = i18n.translate('xpack.csp.vulnerabilities.table.filterIn', {
defaultMessage: 'Filter in',
});
export const FILTER_OUT = i18n.translate('xpack.csp.vulnerabilities.table.filterOut', {
defaultMessage: 'Filter out',
});
export const SEARCH_BAR_PLACEHOLDER = i18n.translate(
'xpack.csp.vulnerabilities.searchBar.placeholder',
{
defaultMessage: 'Search vulnerabilities (eg. vulnerability.severity : "CRITICAL" )',
}
);
export const VULNERABILITIES = i18n.translate('xpack.csp.vulnerabilities', {
defaultMessage: 'Vulnerabilities',
});
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ export interface VulnerabilityRecord {
}

export interface Vulnerability {
published_at: string;
published_date: string;
score: {
version: string;
impact: number;
base: number;
};
cwe: string[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
type Filter,
buildFilter,
FILTERS,
FilterStateStore,
compareFilters,
FilterCompareOptions,
} from '@kbn/es-query';
import type { Serializable } from '@kbn/utility-types';
import type { FindingsBaseProps } from '../../../common/types';

const compareOptions: FilterCompareOptions = {
negate: false,
};

/**
* adds a new filter to a new filters array
* removes existing filter if negated filter is added
*
* @returns {Filter[]} a new array of filters to be added back to filterManager
*/
export const getFilters = ({
filters: existingFilters,
dataView,
field,
value,
negate,
}: {
filters: Filter[];
dataView: FindingsBaseProps['dataView'];
field: string;
value: Serializable;
negate: boolean;
}): Filter[] => {
const dataViewField = dataView.fields.find((f) => f.spec.name === field);
if (!dataViewField) return existingFilters;

const phraseFilter = buildFilter(
dataView,
dataViewField,
FILTERS.PHRASE,
negate,
false,
value,
null,
FilterStateStore.APP_STATE
);

const nextFilters = [
...existingFilters.filter(
// Exclude existing filters that match the newly added 'phraseFilter'
(filter) => !compareFilters(filter, phraseFilter, compareOptions)
),
phraseFilter,
];

return nextFilters;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { VectorScoreBase, Vector } from './types';
import { VectorScoreBase, Vector } from '../types';

export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => {
const result: Vector[] = [];
Expand All @@ -24,7 +24,7 @@ export const getVectorScoreList = (vectorBaseScore: VectorScoreBase) => {

if (v3Vector) {
result.push({
version: '2.0',
version: '3.0',
vector: v3Vector,
score: v3Score,
});
Expand Down
Loading

0 comments on commit e0d40b7

Please sign in to comment.