Skip to content

Commit

Permalink
[8.x] [Cloud Security]Vulnerabilities table in Contextual flyout (ela…
Browse files Browse the repository at this point in the history
…stic#195143) (elastic#195597)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Cloud Security]Vulnerabilities table in Contextual flyout
(elastic#195143)](elastic#195143)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Rickyanto
Ang","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-09T13:25:28Z","message":"[Cloud
Security]Vulnerabilities table in Contextual flyout (elastic#195143)\n\n##
Summary\r\n\r\nThis PR is for Vulnerabilities data table in contextual
flyout\r\nIt also addresses the ticket to remove Empty State for Preview
Component\r\n[ticket](https://github.com/elastic/security-team/issues/10746)\r\n<img
width=\"1510\" alt=\"Screenshot 2024-10-07 at 2 14
52 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/3c4cdc86-68c6-439c-96a1-92cece88e42e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
Maxim Kholod
<[email protected]>","sha":"59f2f85b8a18cc23c7f0c168830fbc304a9346b6","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Cloud
Security","backport:prev-minor","ci:build-cloud-image","ci:project-deploy-security","v8.16.0"],"title":"[Cloud
Security]Vulnerabilities table in Contextual
flyout","number":195143,"url":"https://github.com/elastic/kibana/pull/195143","mergeCommit":{"message":"[Cloud
Security]Vulnerabilities table in Contextual flyout (elastic#195143)\n\n##
Summary\r\n\r\nThis PR is for Vulnerabilities data table in contextual
flyout\r\nIt also addresses the ticket to remove Empty State for Preview
Component\r\n[ticket](https://github.com/elastic/security-team/issues/10746)\r\n<img
width=\"1510\" alt=\"Screenshot 2024-10-07 at 2 14
52 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/3c4cdc86-68c6-439c-96a1-92cece88e42e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
Maxim Kholod
<[email protected]>","sha":"59f2f85b8a18cc23c7f0c168830fbc304a9346b6"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195143","number":195143,"mergeCommit":{"message":"[Cloud
Security]Vulnerabilities table in Contextual flyout (elastic#195143)\n\n##
Summary\r\n\r\nThis PR is for Vulnerabilities data table in contextual
flyout\r\nIt also addresses the ticket to remove Empty State for Preview
Component\r\n[ticket](https://github.com/elastic/security-team/issues/10746)\r\n<img
width=\"1510\" alt=\"Screenshot 2024-10-07 at 2 14
52 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/3c4cdc86-68c6-439c-96a1-92cece88e42e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
Maxim Kholod
<[email protected]>","sha":"59f2f85b8a18cc23c7f0c168830fbc304a9346b6"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Rickyanto Ang <[email protected]>
  • Loading branch information
kibanamachine and animehart authored Oct 9, 2024
1 parent 5798892 commit 0751a8e
Show file tree
Hide file tree
Showing 36 changed files with 1,007 additions and 330 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const VULNERABILITIES_FLYOUT_VISITS = 'vulnerabilities-flyout-visits';
export const OPEN_FINDINGS_FLYOUT = 'open-findings-flyout';
export const GROUP_BY_CLICK = 'group-by-click';
export const CHANGE_RULE_STATE = 'change-rule-state';
export const ENTITY_FLYOUT_VULNERABILITY_VIEW_VISITS = 'entity-flyout-vulnerability-view-visits';

type CloudSecurityUiCounters =
| typeof ENTITY_FLYOUT_MISCONFIGURATION_VIEW_VISITS
Expand All @@ -32,6 +33,7 @@ type CloudSecurityUiCounters =
| typeof CREATE_DETECTION_RULE_FROM_FLYOUT
| typeof CREATE_DETECTION_FROM_TABLE_ROW_ACTION
| typeof GROUP_BY_CLICK
| typeof ENTITY_FLYOUT_VULNERABILITY_VIEW_VISITS
| typeof CHANGE_RULE_STATE;

export class UiMetricService {
Expand Down
4 changes: 3 additions & 1 deletion x-pack/packages/kbn-cloud-security-posture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ export type { NavFilter } from './src/hooks/use_navigate_findings';
export { showErrorToast } from './src/utils/show_error_toast';
export { encodeQuery, decodeQuery } from './src/utils/query_utils';
export { CspEvaluationBadge } from './src/components/csp_evaluation_badge';
export { getSeverityStatusColor } from './src/utils/get_vulnerability_colors';
export { getSeverityStatusColor, getCvsScoreColor } from './src/utils/get_vulnerability_colors';
export { getSeverityText } from './src/utils/get_vulnerability_text';
export { getVulnerabilityStats, hasVulnerabilitiesData } from './src/utils/vulnerability_helpers';
export { CVSScoreBadge, SeverityStatusBadge } from './src/components/vulnerability_badges';
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import React from 'react';
import { css } from '@emotion/react';
import { float } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
import { getSeverityStatusColor } from '@kbn/cloud-security-posture';
import { getCvsScoreColor } from '../common/utils/get_vulnerability_colors';
import { VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ } from './test_subjects';
import { getCvsScoreColor, getSeverityStatusColor } from '../utils/get_vulnerability_colors';

const VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ = 'vulnerabilities_cvss_score_badge';

interface CVSScoreBadgeProps {
score?: float;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const useMisconfigurationPreview = (options: UseCspOptions) => {
params: buildMisconfigurationsFindingsQuery(options, rulesStates!),
})
);
if (!aggregations && !options.ignore_unavailable)
if (!aggregations && options.ignore_unavailable === false)
throw new Error('expected aggregations to be defined');
return {
count: getMisconfigurationAggregationCount(aggregations?.count?.buckets),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 { useQuery } from '@tanstack/react-query';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { lastValueFrom } from 'rxjs';
import type { IKibanaSearchResponse, IKibanaSearchRequest } from '@kbn/search-types';
import {
SearchRequest,
SearchResponse,
AggregationsMultiBucketAggregateBase,
AggregationsStringRareTermsBucketKeys,
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import type { CoreStart } from '@kbn/core/public';
import type { CspClientPluginStartDeps, UseCspOptions } from '../../type';
import { showErrorToast } from '../..';
import { getVulnerabilitiesAggregationCount, getVulnerabilitiesQuery } from '../utils/hooks_utils';

type LatestFindingsRequest = IKibanaSearchRequest<SearchRequest>;
type LatestFindingsResponse = IKibanaSearchResponse<
SearchResponse<CspVulnerabilityFinding, FindingsAggs>
>;

interface FindingsAggs {
count: AggregationsMultiBucketAggregateBase<AggregationsStringRareTermsBucketKeys>;
}

export const useVulnerabilitiesFindings = (options: UseCspOptions) => {
const {
data,
notifications: { toasts },
} = useKibana<CoreStart & CspClientPluginStartDeps>().services;
/**
* We're using useInfiniteQuery in this case to allow the user to fetch more data (if available and up to 10k)
* useInfiniteQuery differs from useQuery because it accumulates and caches a chunk of data from the previous fetches into an array
* it uses the getNextPageParam to know if there are more pages to load and retrieve the position of
* the last loaded record to be used as a from parameter to fetch the next chunk of data.
*/
return useQuery(
['csp_vulnerabilities_findings', { params: options }],
async ({ pageParam }) => {
const {
rawResponse: { aggregations, hits },
} = await lastValueFrom(
data.search.search<LatestFindingsRequest, LatestFindingsResponse>({
params: getVulnerabilitiesQuery(options, pageParam),
})
);

return {
count: getVulnerabilitiesAggregationCount(aggregations?.count?.buckets),
rows: hits.hits.map((finding) => ({
vulnerability: finding._source?.vulnerability,
resource: finding._source?.resource,
})) as Array<Pick<CspVulnerabilityFinding, 'vulnerability' | 'resource'>>,
};
},
{
keepPreviousData: true,
enabled: options.enabled,
onError: (err: Error) => showErrorToast(toasts, err),
}
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,11 @@ import {
AggregationsMultiBucketAggregateBase,
AggregationsStringRareTermsBucketKeys,
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import {
CDR_VULNERABILITIES_INDEX_PATTERN,
LATEST_VULNERABILITIES_RETENTION_POLICY,
} from '@kbn/cloud-security-posture-common';
import type { CspVulnerabilityFinding } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
import type { CoreStart } from '@kbn/core/public';
import type { CspClientPluginStartDeps, UseCspOptions } from '../../type';
import { showErrorToast } from '../..';
import {
getFindingsCountAggQueryVulnerabilities,
getVulnerabilitiesAggregationCount,
} from '../utils/hooks_utils';
import { getVulnerabilitiesAggregationCount, getVulnerabilitiesQuery } from '../utils/hooks_utils';

type LatestFindingsRequest = IKibanaSearchRequest<SearchRequest>;
type LatestFindingsResponse = IKibanaSearchResponse<
Expand All @@ -36,30 +29,6 @@ interface FindingsAggs {
count: AggregationsMultiBucketAggregateBase<AggregationsStringRareTermsBucketKeys>;
}

const getVulnerabilitiesQuery = ({ query }: UseCspOptions, isPreview = false) => ({
index: CDR_VULNERABILITIES_INDEX_PATTERN,
size: 0,
aggs: getFindingsCountAggQueryVulnerabilities(),
ignore_unavailable: true,
query: {
...query,
bool: {
...query?.bool,
filter: [
...(query?.bool?.filter ?? []),
{
range: {
'@timestamp': {
gte: `now-${LATEST_VULNERABILITIES_RETENTION_POLICY}`,
lte: 'now',
},
},
},
],
},
},
});

export const useVulnerabilitiesPreview = (options: UseCspOptions) => {
const {
data,
Expand All @@ -73,7 +42,7 @@ export const useVulnerabilitiesPreview = (options: UseCspOptions) => {
rawResponse: { aggregations },
} = await lastValueFrom(
data.search.search<LatestFindingsRequest, LatestFindingsResponse>({
params: getVulnerabilitiesQuery(options),
params: getVulnerabilitiesQuery(options, true),
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { euiThemeVars } from '@kbn/ui-theme';
import { getSeverityStatusColor } from './get_vulnerability_colors';
import { getCvsScoreColor, getSeverityStatusColor } from './get_vulnerability_colors';
describe('getSeverityStatusColor', () => {
it('should return the correct color for LOW severity', () => {
expect(getSeverityStatusColor('LOW')).toBe(euiThemeVars.euiColorVis0);
Expand All @@ -28,3 +28,25 @@ describe('getSeverityStatusColor', () => {
expect(getSeverityStatusColor('UNKNOWN')).toBe('#aaa');
});
});

describe('getCvsScoreColor', () => {
it('returns correct color for low severity score', () => {
expect(getCvsScoreColor(1.5)).toBe(euiThemeVars.euiColorVis0);
});

it('returns correct color for medium severity score', () => {
expect(getCvsScoreColor(5.5)).toBe(euiThemeVars.euiColorVis7);
});

it('returns correct color for high severity score', () => {
expect(getCvsScoreColor(7.9)).toBe(euiThemeVars.euiColorVis9);
});

it('returns correct color for critical severity score', () => {
expect(getCvsScoreColor(10.0)).toBe(euiThemeVars.euiColorDanger);
});

it('returns correct color for low severity score for undefined value', () => {
expect(getCvsScoreColor(-0.2)).toBe(euiThemeVars.euiColorVis0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ import { euiThemeVars } from '@kbn/ui-theme';
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
import { VULNERABILITIES_SEVERITY } from '@kbn/cloud-security-posture-common';

export const getCvsScoreColor = (score: number): string | undefined => {
if (score <= 4) {
return euiThemeVars.euiColorVis0; // low severity
} else if (score >= 4 && score <= 7) {
return euiThemeVars.euiColorVis7; // medium severity
} else if (score >= 7 && score <= 9) {
return euiThemeVars.euiColorVis9; // high severity
} else if (score >= 9) {
return euiThemeVars.euiColorDanger; // critical severity
}
};

export const getSeverityStatusColor = (severity: VulnSeverity): string => {
switch (severity) {
case VULNERABILITIES_SEVERITY.LOW:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import {
CDR_MISCONFIGURATIONS_INDEX_PATTERN,
CDR_VULNERABILITIES_INDEX_PATTERN,
LATEST_FINDINGS_RETENTION_POLICY,
LATEST_VULNERABILITIES_RETENTION_POLICY,
} from '@kbn/cloud-security-posture-common';
import type { CspBenchmarkRulesStates } from '@kbn/cloud-security-posture-common/schema/rules/latest';
import { buildMutedRulesFilter } from '@kbn/cloud-security-posture-common';
Expand Down Expand Up @@ -161,3 +163,31 @@ export const getFindingsCountAggQueryVulnerabilities = () => ({
},
},
});

export const getVulnerabilitiesQuery = ({ query }: UseCspOptions, isPreview = false) => ({
index: CDR_VULNERABILITIES_INDEX_PATTERN,
size: isPreview ? 0 : 500,
aggs: getFindingsCountAggQueryVulnerabilities(),
ignore_unavailable: true,
query: buildVulnerabilityFindingsQueryWithFilters(query),
});

const buildVulnerabilityFindingsQueryWithFilters = (query: UseCspOptions['query']) => {
return {
...query,
bool: {
...query?.bool,
filter: [
...(query?.bool?.filter ?? []),
{
range: {
'@timestamp': {
gte: `now-${LATEST_VULNERABILITIES_RETENTION_POLICY}`,
lte: 'now',
},
},
},
],
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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 { euiThemeVars } from '@kbn/ui-theme';
import { getVulnerabilityStats } from './vulnerability_helpers';
import { i18n } from '@kbn/i18n';

describe('getVulnerabilitiesAggregationCount', () => {
it('should return empty array when all severity count is 0', () => {
const result = getVulnerabilityStats({ critical: 0, high: 0, medium: 0, low: 0, none: 0 });
expect(result).toEqual([]);
});

it('should return stats for low, medium, high, and critical vulnerabilities', () => {
const result = getVulnerabilityStats({ critical: 1, high: 2, medium: 3, low: 4, none: 5 });

expect(result).toEqual([
{
key: i18n.translate(
'xpack.securitySolution.flyout.right.insights.vulnerabilities.noneVulnerabilitiesText',
{
defaultMessage: 'None',
}
),
count: 5,
color: '#aaa',
},
{
key: i18n.translate(
'xpack.securitySolution.flyout.right.insights.vulnerabilities.lowVulnerabilitiesText',
{
defaultMessage: 'Low',
}
),
count: 4,
color: euiThemeVars.euiColorVis0,
},
{
key: i18n.translate(
'xpack.securitySolution.flyout.right.insights.vulnerabilities.mediumVulnerabilitiesText',
{
defaultMessage: 'Medium',
}
),
count: 3,
color: euiThemeVars.euiColorVis5_behindText,
},
{
key: i18n.translate(
'xpack.securitySolution.flyout.right.insights.vulnerabilities.highVulnerabilitiesText',
{
defaultMessage: 'High',
}
),
count: 2,
color: euiThemeVars.euiColorVis9_behindText,
},
{
key: i18n.translate(
'xpack.securitySolution.flyout.right.insights.vulnerabilities.CriticalVulnerabilitiesText',
{
defaultMessage: 'Critical',
}
),
count: 1,
color: euiThemeVars.euiColorDanger,
},
]);
});
});
Loading

0 comments on commit 0751a8e

Please sign in to comment.