Skip to content

Commit

Permalink
[Cloud Security] Vulnerabilities Preview & Refactor CSP Plugin PHASE 2 (
Browse files Browse the repository at this point in the history
elastic#193638)

In an attempt to make Reviewing easier and more accurate, the
implementation of Vulnerabilities on Host.name flyout in Alerts Page
will be split into 2 Phases

Phase 1: Move Functions, Utils or Helpers, Hooks, constants to Package
Phase 2: Implementing the feature

This is Phase 2
<img width="1465" alt="Screenshot 2024-09-20 at 5 33 01 PM"
src="https://github.com/user-attachments/assets/cabe2f3a-d35a-4825-9fe5-61fe2d570328">

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Maxim Kholod <[email protected]>
  • Loading branch information
3 people authored Oct 4, 2024
1 parent f582423 commit 0b92c26
Show file tree
Hide file tree
Showing 32 changed files with 830 additions and 86 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,7 @@ x-pack/plugins/osquery @elastic/security-defend-workflows
/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture
/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.* @elastic/fleet @elastic/kibana-cloud-security-posture
/x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture
/x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/vulnerabilities_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture

# Security Solution onboarding tour
/x-pack/plugins/security_solution/public/common/components/guided_onboarding @elastic/security-threat-hunting-explore
Expand Down
10 changes: 10 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture-common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { VulnSeverity } from './types/vulnerabilities';

export const KSPM_POLICY_TEMPLATE = 'kspm';
export const CSPM_POLICY_TEMPLATE = 'cspm';
export const CDR_LATEST_NATIVE_MISCONFIGURATIONS_INDEX_PATTERN =
Expand Down Expand Up @@ -33,3 +35,11 @@ export const CDR_LATEST_THIRD_PARTY_VULNERABILITIES_INDEX_PATTERN =
'security_solution-*.vulnerability_latest';
export const CDR_VULNERABILITIES_INDEX_PATTERN = `${CDR_LATEST_THIRD_PARTY_VULNERABILITIES_INDEX_PATTERN},${CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN}`;
export const LATEST_VULNERABILITIES_RETENTION_POLICY = '3d';

export const VULNERABILITIES_SEVERITY: Record<VulnSeverity, VulnSeverity> = {
LOW: 'LOW',
MEDIUM: 'MEDIUM',
HIGH: 'HIGH',
CRITICAL: 'CRITICAL',
UNKNOWN: 'UNKNOWN',
};
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe('test helper methods', () => {
filter: [
{
bool: {
should: [{ term: { 'host.name': { value: 'exampleHost' } } }],
should: [{ term: { 'host.name': 'exampleHost' } }],
minimum_should_match: 1,
},
},
Expand All @@ -171,7 +171,7 @@ describe('test helper methods', () => {
filter: [
{
bool: {
should: [{ term: { 'host.name': { value: '' } } }],
should: [{ term: { 'host.name': '' } }],
minimum_should_match: 1,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ export const buildEntityFlyoutPreviewQuery = (field: string, queryValue?: string
filter: [
{
bool: {
should: [{ term: { [field]: { value: `${queryValue || ''}` } } }],
should: [
{
term: {
[field]: `${queryValue || ''}`,
},
},
],
minimum_should_match: 1,
},
},
Expand Down
2 changes: 2 additions & 0 deletions x-pack/packages/kbn-cloud-security-posture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ 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 { getSeverityText } from './src/utils/get_vulnerability_text';
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
CspClientPluginStartDeps,
LatestFindingsRequest,
LatestFindingsResponse,
UseMisconfigurationOptions,
UseCspOptions,
} from '../../type';

import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
Expand All @@ -23,7 +23,7 @@ import {
getMisconfigurationAggregationCount,
} from '../utils/hooks_utils';

export const useMisconfigurationFindings = (options: UseMisconfigurationOptions) => {
export const useMisconfigurationFindings = (options: UseCspOptions) => {
const {
data,
notifications: { toasts },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import type {
CspClientPluginStartDeps,
LatestFindingsRequest,
LatestFindingsResponse,
UseMisconfigurationOptions,
UseCspOptions,
} from '../../type';
import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
import {
buildMisconfigurationsFindingsQuery,
getMisconfigurationAggregationCount,
} from '../utils/hooks_utils';

export const useMisconfigurationPreview = (options: UseMisconfigurationOptions) => {
export const useMisconfigurationPreview = (options: UseCspOptions) => {
const {
data,
notifications: { toasts },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 {
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';

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

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,
notifications: { toasts },
} = useKibana<CoreStart & CspClientPluginStartDeps>().services;

return useQuery(
['csp_vulnerabilities_preview', { params: options }],
async () => {
const {
rawResponse: { aggregations },
} = await lastValueFrom(
data.search.search<LatestFindingsRequest, LatestFindingsResponse>({
params: getVulnerabilitiesQuery(options),
})
);

return {
count: getVulnerabilitiesAggregationCount(aggregations?.count?.buckets),
};
},
{
keepPreviousData: true,
enabled: options.enabled,
onError: (err: Error) => showErrorToast(toasts, err),
}
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { getSeverityStatusColor } from './get_vulnerability_colors';
describe('getSeverityStatusColor', () => {
it('should return the correct color for LOW severity', () => {
expect(getSeverityStatusColor('LOW')).toBe(euiThemeVars.euiColorVis0);
});

it('should return the correct color for MEDIUM severity', () => {
expect(getSeverityStatusColor('MEDIUM')).toBe(euiThemeVars.euiColorVis5_behindText);
});

it('should return the correct color for HIGH severity', () => {
expect(getSeverityStatusColor('HIGH')).toBe(euiThemeVars.euiColorVis9_behindText);
});

it('should return the correct color for CRITICAL severity', () => {
expect(getSeverityStatusColor('CRITICAL')).toBe(euiThemeVars.euiColorDanger);
});

it('should return #aaa for an unknown severity', () => {
expect(getSeverityStatusColor('UNKNOWN')).toBe('#aaa');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 type { VulnSeverity } from '@kbn/cloud-security-posture-common';
import { VULNERABILITIES_SEVERITY } from '@kbn/cloud-security-posture-common';

export const getSeverityStatusColor = (severity: VulnSeverity): string => {
switch (severity) {
case VULNERABILITIES_SEVERITY.LOW:
return euiThemeVars.euiColorVis0;
case VULNERABILITIES_SEVERITY.MEDIUM:
return euiThemeVars.euiColorVis5_behindText;
case VULNERABILITIES_SEVERITY.HIGH:
return euiThemeVars.euiColorVis9_behindText;
case VULNERABILITIES_SEVERITY.CRITICAL:
return euiThemeVars.euiColorDanger;
default:
return '#aaa';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { getSeverityText } from './get_vulnerability_text';
describe('getSeverityStatusColor', () => {
it('should return the correct color for LOW severity', () => {
expect(getSeverityText('LOW')).toBe('Low');
});

it('should return the correct color for MEDIUM severity', () => {
expect(getSeverityText('MEDIUM')).toBe('Medium');
});

it('should return the correct color for HIGH severity', () => {
expect(getSeverityText('HIGH')).toBe('High');
});

it('should return the correct color for CRITICAL severity', () => {
expect(getSeverityText('CRITICAL')).toBe('Critical');
});

it('should return #aaa for an unknown severity', () => {
expect(getSeverityText('UNKNOWN')).toBe('None');
});
});
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 type { VulnSeverity } from '@kbn/cloud-security-posture-common';
import { VULNERABILITIES_SEVERITY } from '@kbn/cloud-security-posture-common';

export const getSeverityText = (severity: VulnSeverity): string => {
switch (severity) {
case VULNERABILITIES_SEVERITY.LOW:
return 'Low';
case VULNERABILITIES_SEVERITY.MEDIUM:
return 'Medium';
case VULNERABILITIES_SEVERITY.HIGH:
return 'High';
case VULNERABILITIES_SEVERITY.CRITICAL:
return 'Critical';
default:
return 'None';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import {
AggregationBuckets,
getVulnerabilitiesAggregationCount,
VULNERABILITIES_RESULT_EVALUATION,
} from './hooks_utils';

describe('getVulnerabilitiesAggregationCount', () => {
it('should return default counts when nothing is provided', () => {
const result = {
[VULNERABILITIES_RESULT_EVALUATION.LOW]: 0,
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: 0,
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: 0,
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: 0,
[VULNERABILITIES_RESULT_EVALUATION.NONE]: 0,
};
expect(getVulnerabilitiesAggregationCount()).toEqual(result);
});

it('should return default counts when empty bucket is provided', () => {
const result = {
[VULNERABILITIES_RESULT_EVALUATION.LOW]: 0,
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: 0,
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: 0,
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: 0,
[VULNERABILITIES_RESULT_EVALUATION.NONE]: 0,
};
expect(getVulnerabilitiesAggregationCount({})).toEqual(result);
});

it('should return counts when provided with non empty buckets', () => {
const buckets: AggregationBuckets = {
[VULNERABILITIES_RESULT_EVALUATION.LOW]: { doc_count: 1 },
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: { doc_count: 2 },
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: { doc_count: 3 },
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: { doc_count: 4 },
[VULNERABILITIES_RESULT_EVALUATION.NONE]: { doc_count: 5 },
};

const vulnerabilitiesAggregrationCount = getVulnerabilitiesAggregationCount(
buckets as estypes.AggregationsBuckets<estypes.AggregationsStringRareTermsBucketKeys>
);
const result = {
[VULNERABILITIES_RESULT_EVALUATION.LOW]: 1,
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: 2,
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: 3,
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: 4,
[VULNERABILITIES_RESULT_EVALUATION.NONE]: 5,
};
expect(vulnerabilitiesAggregrationCount).toEqual(result);
});
});
Loading

0 comments on commit 0b92c26

Please sign in to comment.