mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Cloud Security] Vulnerabilities Preview & Refactor CSP Plugin PHASE 2 (#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 <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Maxim Kholod <maxim.kholod@elastic.co>
This commit is contained in:
parent
f582423239
commit
0b92c268f9
32 changed files with 830 additions and 86 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
@ -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',
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
@ -171,7 +171,7 @@ describe('test helper methods', () => {
|
|||
filter: [
|
||||
{
|
||||
bool: {
|
||||
should: [{ term: { 'host.name': { value: '' } } }],
|
||||
should: [{ term: { 'host.name': '' } }],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
CspClientPluginStartDeps,
|
||||
LatestFindingsRequest,
|
||||
LatestFindingsResponse,
|
||||
UseMisconfigurationOptions,
|
||||
UseCspOptions,
|
||||
} from '../../type';
|
||||
|
||||
import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
|
||||
|
@ -23,7 +23,7 @@ import {
|
|||
getMisconfigurationAggregationCount,
|
||||
} from '../utils/hooks_utils';
|
||||
|
||||
export const useMisconfigurationFindings = (options: UseMisconfigurationOptions) => {
|
||||
export const useMisconfigurationFindings = (options: UseCspOptions) => {
|
||||
const {
|
||||
data,
|
||||
notifications: { toasts },
|
||||
|
|
|
@ -13,7 +13,7 @@ import type {
|
|||
CspClientPluginStartDeps,
|
||||
LatestFindingsRequest,
|
||||
LatestFindingsResponse,
|
||||
UseMisconfigurationOptions,
|
||||
UseCspOptions,
|
||||
} from '../../type';
|
||||
import { useGetCspBenchmarkRulesStatesApi } from './use_get_benchmark_rules_state_api';
|
||||
import {
|
||||
|
@ -21,7 +21,7 @@ import {
|
|||
getMisconfigurationAggregationCount,
|
||||
} from '../utils/hooks_utils';
|
||||
|
||||
export const useMisconfigurationPreview = (options: UseMisconfigurationOptions) => {
|
||||
export const useMisconfigurationPreview = (options: UseCspOptions) => {
|
||||
const {
|
||||
data,
|
||||
notifications: { toasts },
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
);
|
||||
};
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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';
|
||||
}
|
||||
};
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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';
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -12,14 +12,14 @@ import {
|
|||
} 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';
|
||||
import type { UseMisconfigurationOptions } from '../../type';
|
||||
import type { UseCspOptions } from '../../type';
|
||||
|
||||
const MISCONFIGURATIONS_SOURCE_FIELDS = ['result.*', 'rule.*', 'resource.*'];
|
||||
interface AggregationBucket {
|
||||
doc_count?: number;
|
||||
}
|
||||
|
||||
type AggregationBuckets = Record<string, AggregationBucket>;
|
||||
export type AggregationBuckets = Record<string, AggregationBucket>;
|
||||
|
||||
const RESULT_EVALUATION = {
|
||||
PASSED: 'passed',
|
||||
|
@ -27,6 +27,14 @@ const RESULT_EVALUATION = {
|
|||
UNKNOWN: 'unknown',
|
||||
};
|
||||
|
||||
export const VULNERABILITIES_RESULT_EVALUATION = {
|
||||
LOW: 'LOW',
|
||||
MEDIUM: 'MEDIUM',
|
||||
HIGH: 'HIGH',
|
||||
CRITICAL: 'CRITICAL',
|
||||
NONE: 'NONE',
|
||||
};
|
||||
|
||||
export const getFindingsCountAggQueryMisconfiguration = () => ({
|
||||
count: {
|
||||
filters: {
|
||||
|
@ -64,7 +72,7 @@ export const getMisconfigurationAggregationCount = (
|
|||
};
|
||||
|
||||
export const buildMisconfigurationsFindingsQuery = (
|
||||
{ query }: UseMisconfigurationOptions,
|
||||
{ query }: UseCspOptions,
|
||||
rulesStates: CspBenchmarkRulesStates,
|
||||
isPreview = false
|
||||
) => {
|
||||
|
@ -81,7 +89,7 @@ export const buildMisconfigurationsFindingsQuery = (
|
|||
};
|
||||
|
||||
const buildMisconfigurationsFindingsQueryWithFilters = (
|
||||
query: UseMisconfigurationOptions['query'],
|
||||
query: UseCspOptions['query'],
|
||||
mutedRulesFilterQuery: estypes.QueryDslQueryContainer[]
|
||||
) => {
|
||||
return {
|
||||
|
@ -103,3 +111,53 @@ const buildMisconfigurationsFindingsQueryWithFilters = (
|
|||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getVulnerabilitiesAggregationCount = (
|
||||
buckets?: estypes.AggregationsBuckets<estypes.AggregationsStringRareTermsBucketKeys>
|
||||
) => {
|
||||
const defaultBuckets: AggregationBuckets = {
|
||||
[VULNERABILITIES_RESULT_EVALUATION.LOW]: { doc_count: 0 },
|
||||
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: { doc_count: 0 },
|
||||
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: { doc_count: 0 },
|
||||
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: { doc_count: 0 },
|
||||
[VULNERABILITIES_RESULT_EVALUATION.NONE]: { doc_count: 0 },
|
||||
};
|
||||
|
||||
// if buckets are undefined we will use default buckets
|
||||
const usedBuckets = buckets || defaultBuckets;
|
||||
return Object.entries(usedBuckets).reduce(
|
||||
(evaluation, [key, value]) => {
|
||||
evaluation[key] = (evaluation[key] || 0) + (value.doc_count || 0);
|
||||
return evaluation;
|
||||
},
|
||||
{
|
||||
[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,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const getFindingsCountAggQueryVulnerabilities = () => ({
|
||||
count: {
|
||||
filters: {
|
||||
other_bucket_key: VULNERABILITIES_RESULT_EVALUATION.NONE,
|
||||
filters: {
|
||||
[VULNERABILITIES_RESULT_EVALUATION.LOW]: {
|
||||
match: { 'vulnerability.severity': VULNERABILITIES_RESULT_EVALUATION.LOW },
|
||||
},
|
||||
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: {
|
||||
match: { 'vulnerability.severity': VULNERABILITIES_RESULT_EVALUATION.MEDIUM },
|
||||
},
|
||||
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: {
|
||||
match: { 'vulnerability.severity': VULNERABILITIES_RESULT_EVALUATION.HIGH },
|
||||
},
|
||||
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: {
|
||||
match: { 'vulnerability.severity': VULNERABILITIES_RESULT_EVALUATION.CRITICAL },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -55,7 +55,7 @@ export interface CspClientPluginStartDeps {
|
|||
usageCollection?: UsageCollectionStart;
|
||||
}
|
||||
|
||||
export interface MisconfigurationBaseEsQuery {
|
||||
export interface CspBaseEsQuery {
|
||||
query?: {
|
||||
bool: {
|
||||
filter: estypes.QueryDslQueryContainer[];
|
||||
|
@ -63,7 +63,7 @@ export interface MisconfigurationBaseEsQuery {
|
|||
};
|
||||
}
|
||||
|
||||
export interface UseMisconfigurationOptions extends MisconfigurationBaseEsQuery {
|
||||
export interface UseCspOptions extends CspBaseEsQuery {
|
||||
sort: string[][];
|
||||
enabled: boolean;
|
||||
pageSize: number;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { KSPM_POLICY_TEMPLATE, CSPM_POLICY_TEMPLATE } from '@kbn/cloud-security-posture-common';
|
||||
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
|
||||
|
||||
import { AwsCredentialsTypeFieldMap, GcpCredentialsTypeFieldMap, PostureTypes } from './types_old';
|
||||
|
||||
export const CLOUD_SECURITY_INTERTAL_PREFIX_ROUTE_PATH = '/internal/cloud_security_posture/';
|
||||
|
@ -125,14 +125,6 @@ export const POSTURE_TYPES: { [x: string]: PostureTypes } = {
|
|||
[POSTURE_TYPE_ALL]: POSTURE_TYPE_ALL,
|
||||
};
|
||||
|
||||
export const VULNERABILITIES_SEVERITY: Record<VulnSeverity, VulnSeverity> = {
|
||||
LOW: 'LOW',
|
||||
MEDIUM: 'MEDIUM',
|
||||
HIGH: 'HIGH',
|
||||
CRITICAL: 'CRITICAL',
|
||||
UNKNOWN: 'UNKNOWN',
|
||||
};
|
||||
|
||||
export const AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP: AwsCredentialsTypeFieldMap = {
|
||||
assume_role: ['role_arn'],
|
||||
direct_access_keys: ['access_key_id', 'secret_access_key'],
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
|
||||
import { VULNERABILITIES_SEVERITY } from '../../../common/constants';
|
||||
|
||||
export const getCvsScoreColor = (score: number): string | undefined => {
|
||||
if (score <= 4) {
|
||||
|
@ -20,18 +18,3 @@ export const getCvsScoreColor = (score: number): string | undefined => {
|
|||
return euiThemeVars.euiColorDanger; // critical severity
|
||||
}
|
||||
};
|
||||
|
||||
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';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { getCvsScoreColor, getSeverityStatusColor } from './get_vulnerability_colors';
|
||||
import { getCvsScoreColor } from './get_vulnerability_colors';
|
||||
|
||||
describe('getCvsScoreColor', () => {
|
||||
it('returns correct color for low severity score', () => {
|
||||
|
@ -29,25 +29,3 @@ describe('getCvsScoreColor', () => {
|
|||
expect(getCvsScoreColor(-0.2)).toBe(euiThemeVars.euiColorVis0);
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,8 @@ 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 { getCvsScoreColor, getSeverityStatusColor } from '../common/utils/get_vulnerability_colors';
|
||||
import { getSeverityStatusColor } from '@kbn/cloud-security-posture';
|
||||
import { getCvsScoreColor } from '../common/utils/get_vulnerability_colors';
|
||||
import { VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ } from './test_subjects';
|
||||
|
||||
interface CVSScoreBadgeProps {
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { PaletteColorStop } from '@elastic/eui/src/components/color_picker/color_palette_picker';
|
||||
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getSeverityStatusColor } from '../common/utils/get_vulnerability_colors';
|
||||
import { getSeverityStatusColor } from '@kbn/cloud-security-posture';
|
||||
import { SeverityStatusBadge } from './vulnerability_badges';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -14,7 +14,10 @@ import {
|
|||
parseGroupingQuery,
|
||||
} from '@kbn/grouping/src';
|
||||
import { useMemo } from 'react';
|
||||
import { LATEST_VULNERABILITIES_RETENTION_POLICY } from '@kbn/cloud-security-posture-common';
|
||||
import {
|
||||
LATEST_VULNERABILITIES_RETENTION_POLICY,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
} from '@kbn/cloud-security-posture-common';
|
||||
import { buildEsQuery, Filter } from '@kbn/es-query';
|
||||
import {
|
||||
LOCAL_STORAGE_VULNERABILITIES_GROUPING_KEY,
|
||||
|
@ -22,7 +25,6 @@ import {
|
|||
VULNERABILITY_FIELDS,
|
||||
} from '../../../common/constants';
|
||||
import { useDataViewContext } from '../../../common/contexts/data_view_context';
|
||||
import { VULNERABILITIES_SEVERITY } from '../../../../common/constants';
|
||||
import {
|
||||
VulnerabilitiesGroupingAggregation,
|
||||
VulnerabilitiesRootGroupingAggregation,
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { LATEST_VULNERABILITIES_RETENTION_POLICY } from '@kbn/cloud-security-posture-common';
|
||||
import type { Vulnerability } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
|
||||
import {
|
||||
VULNERABILITIES_INDEX_PATTERN,
|
||||
LATEST_VULNERABILITIES_RETENTION_POLICY,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
} from '../../../../common/constants';
|
||||
} from '@kbn/cloud-security-posture-common';
|
||||
import type { Vulnerability } from '@kbn/cloud-security-posture-common/schema/vulnerabilities/latest';
|
||||
import { VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants';
|
||||
import { createDetectionRule } from '../../../common/api/create_detection_rule';
|
||||
|
||||
const DEFAULT_RULE_RISK_SCORE = 0;
|
||||
|
|
|
@ -8,11 +8,11 @@ import React, { useMemo } from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiHealth } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useNavigateVulnerabilities } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
|
||||
import { VULNERABILITIES_SEVERITY } from '@kbn/cloud-security-posture-common';
|
||||
import { getSeverityStatusColor } from '@kbn/cloud-security-posture';
|
||||
import { VulnCounterCard, type VulnCounterCardProps } from '../../components/vuln_counter_card';
|
||||
import { VULNERABILITIES_SEVERITY } from '../../../common/constants';
|
||||
import { useVulnerabilityDashboardApi } from '../../common/api/use_vulnerability_dashboard_api';
|
||||
import { CompactFormattedNumber } from '../../components/compact_formatted_number';
|
||||
import { getSeverityStatusColor } from '../../common/utils/get_vulnerability_colors';
|
||||
|
||||
export const VulnerabilityStatistics = () => {
|
||||
const navToVulnerabilities = useNavigateVulnerabilities();
|
||||
|
|
|
@ -21,12 +21,12 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
import { useNavigateVulnerabilities } from '@kbn/cloud-security-posture/src/hooks/use_navigate_findings';
|
||||
import type { VulnSeverity } from '@kbn/cloud-security-posture-common';
|
||||
import { VULNERABILITIES_SEVERITY } from '@kbn/cloud-security-posture-common';
|
||||
import { getSeverityStatusColor } from '@kbn/cloud-security-posture';
|
||||
import { truthy } from '../../../common/utils/helpers';
|
||||
import { VulnStatsTrend } from '../../../common/types_old';
|
||||
import { useVulnerabilityDashboardApi } from '../../common/api/use_vulnerability_dashboard_api';
|
||||
import { getSeverityStatusColor } from '../../common/utils/get_vulnerability_colors';
|
||||
import { ChartPanel } from '../../components/chart_panel';
|
||||
import { VULNERABILITIES_SEVERITY } from '../../../common/constants';
|
||||
import { useKibana } from '../../common/hooks/use_kibana';
|
||||
|
||||
const stackAccessors: VulnSeverity[] = [
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
import { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN } from '@kbn/cloud-security-posture-common';
|
||||
import { VULNERABILITIES_SEVERITY } from '../../../common/constants';
|
||||
import {
|
||||
CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
} from '@kbn/cloud-security-posture-common';
|
||||
|
||||
export interface VulnerabilitiesStatisticsQueryResult {
|
||||
critical: {
|
||||
|
|
|
@ -14,7 +14,10 @@ import {
|
|||
import type { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types';
|
||||
import { CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN } from '@kbn/cloud-security-posture-common';
|
||||
import {
|
||||
CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
} from '@kbn/cloud-security-posture-common';
|
||||
import type { ISavedObjectsRepository, Logger } from '@kbn/core/server';
|
||||
import { getMutedRulesFilterQuery } from '../routes/benchmark_rules/get_states/v1';
|
||||
import { getSafePostureTypeRuntimeMapping } from '../../common/runtime_mappings/get_safe_posture_type_runtime_mapping';
|
||||
|
@ -25,7 +28,6 @@ import {
|
|||
CSPM_FINDINGS_STATS_INTERVAL,
|
||||
INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE,
|
||||
LATEST_FINDINGS_INDEX_DEFAULT_NS,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
VULN_MGMT_POLICY_TEMPLATE,
|
||||
} from '../../common/constants';
|
||||
import { scheduleTaskSafe, removeTaskSafe } from '../lib/task_manager_util';
|
||||
|
|
|
@ -12,6 +12,7 @@ import { css } from '@emotion/react';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useCspSetupStatusApi } from '@kbn/cloud-security-posture/src/hooks/use_csp_setup_status_api';
|
||||
import { MisconfigurationsPreview } from './misconfiguration/misconfiguration_preview';
|
||||
import { VulnerabilitiesPreview } from './vulnerabilities/vulnerabilities_preview';
|
||||
|
||||
export const EntityInsight = <T,>({
|
||||
name,
|
||||
|
@ -25,10 +26,27 @@ export const EntityInsight = <T,>({
|
|||
const { euiTheme } = useEuiTheme();
|
||||
const getSetupStatus = useCspSetupStatusApi();
|
||||
const hasMisconfigurationFindings = getSetupStatus.data?.hasMisconfigurationsFindings;
|
||||
const hasVulnerabilitiesFindings = getSetupStatus.data?.hasVulnerabilitiesFindings;
|
||||
const insightContent: React.ReactElement[] = [];
|
||||
const isVulnerabilitiesFindingForHost = hasVulnerabilitiesFindings && fieldName === 'host.name';
|
||||
|
||||
if (hasMisconfigurationFindings)
|
||||
insightContent.push(
|
||||
<>
|
||||
<MisconfigurationsPreview name={name} fieldName={fieldName} isPreviewMode={isPreviewMode} />
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
);
|
||||
if (isVulnerabilitiesFindingForHost)
|
||||
insightContent.push(
|
||||
<>
|
||||
<VulnerabilitiesPreview hostName={name} />
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{hasMisconfigurationFindings && (
|
||||
{(hasMisconfigurationFindings || isVulnerabilitiesFindingForHost) && (
|
||||
<>
|
||||
<EuiAccordion
|
||||
initialIsOpen={true}
|
||||
|
@ -52,12 +70,7 @@ export const EntityInsight = <T,>({
|
|||
}
|
||||
>
|
||||
<EuiSpacer size="m" />
|
||||
<MisconfigurationsPreview
|
||||
name={name}
|
||||
fieldName={fieldName}
|
||||
isPreviewMode={isPreviewMode}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
{insightContent}
|
||||
</EuiAccordion>
|
||||
<EuiHorizontalRule />
|
||||
</>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
// Add stuff here
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 { TestProviders } from '../../../common/mock';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { VulnerabilitiesPreview } from './vulnerabilities_preview';
|
||||
|
||||
const mockProps: { hostName: string } = {
|
||||
hostName: 'testContextID',
|
||||
};
|
||||
|
||||
describe('VulnerabilitiesPreview', () => {
|
||||
it('renders', () => {
|
||||
const { queryByTestId } = render(<VulnerabilitiesPreview {...mockProps} />, {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
expect(
|
||||
queryByTestId('securitySolutionFlyoutInsightsVulnerabilitiesContent')
|
||||
).toBeInTheDocument();
|
||||
expect(queryByTestId('noVulnerabilitiesDataTestSubj')).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import type { EuiThemeComputed } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, useEuiTheme, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DistributionBar } from '@kbn/security-solution-distribution-bar';
|
||||
import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpandablePanel } from '@kbn/security-solution-common';
|
||||
import {
|
||||
buildEntityFlyoutPreviewQuery,
|
||||
VULNERABILITIES_SEVERITY,
|
||||
getAbbreviatedNumber,
|
||||
} from '@kbn/cloud-security-posture-common';
|
||||
import { getSeverityStatusColor, getSeverityText } from '@kbn/cloud-security-posture';
|
||||
|
||||
interface VulnerabilitiesDistributionBarProps {
|
||||
key: string;
|
||||
count: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const getVulnerabilityStats = (
|
||||
critical: number,
|
||||
high: number,
|
||||
medium: number,
|
||||
low: number,
|
||||
none: number
|
||||
): VulnerabilitiesDistributionBarProps[] => {
|
||||
const vulnerabilityStats: VulnerabilitiesDistributionBarProps[] = [];
|
||||
if (critical === 0 && high === 0 && medium === 0 && low === 0 && none === 0)
|
||||
return vulnerabilityStats;
|
||||
|
||||
if (none > 0)
|
||||
vulnerabilityStats.push({
|
||||
key: i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.vulnerabilities.noneVulnerabilitiesText',
|
||||
{
|
||||
defaultMessage: getSeverityText(VULNERABILITIES_SEVERITY.UNKNOWN),
|
||||
}
|
||||
),
|
||||
count: none,
|
||||
color: getSeverityStatusColor(VULNERABILITIES_SEVERITY.UNKNOWN),
|
||||
});
|
||||
if (low > 0)
|
||||
vulnerabilityStats.push({
|
||||
key: i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.vulnerabilities.lowVulnerabilitiesText',
|
||||
{
|
||||
defaultMessage: getSeverityText(VULNERABILITIES_SEVERITY.LOW),
|
||||
}
|
||||
),
|
||||
count: low,
|
||||
color: getSeverityStatusColor(VULNERABILITIES_SEVERITY.LOW),
|
||||
});
|
||||
|
||||
if (medium > 0)
|
||||
vulnerabilityStats.push({
|
||||
key: i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.vulnerabilities.mediumVulnerabilitiesText',
|
||||
{
|
||||
defaultMessage: getSeverityText(VULNERABILITIES_SEVERITY.MEDIUM),
|
||||
}
|
||||
),
|
||||
count: medium,
|
||||
color: getSeverityStatusColor(VULNERABILITIES_SEVERITY.MEDIUM),
|
||||
});
|
||||
if (high > 0)
|
||||
vulnerabilityStats.push({
|
||||
key: i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.vulnerabilities.highVulnerabilitiesText',
|
||||
{
|
||||
defaultMessage: getSeverityText(VULNERABILITIES_SEVERITY.HIGH),
|
||||
}
|
||||
),
|
||||
count: high,
|
||||
color: getSeverityStatusColor(VULNERABILITIES_SEVERITY.HIGH),
|
||||
});
|
||||
if (critical > 0)
|
||||
vulnerabilityStats.push({
|
||||
key: i18n.translate(
|
||||
'xpack.securitySolution.flyout.right.insights.vulnerabilities.CriticalVulnerabilitiesText',
|
||||
{
|
||||
defaultMessage: getSeverityText(VULNERABILITIES_SEVERITY.CRITICAL),
|
||||
}
|
||||
),
|
||||
count: critical,
|
||||
color: getSeverityStatusColor(VULNERABILITIES_SEVERITY.CRITICAL),
|
||||
});
|
||||
|
||||
return vulnerabilityStats;
|
||||
};
|
||||
|
||||
const VulnerabilitiesEmptyState = ({ euiTheme }: { euiTheme: EuiThemeComputed<{}> }) => {
|
||||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="m">
|
||||
<h1>{'-'}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText
|
||||
size="m"
|
||||
css={css`
|
||||
font-weight: ${euiTheme.font.weight.semiBold};
|
||||
`}
|
||||
data-test-subj="noVulnerabilitiesDataTestSubj"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.vulnerabilities.noVulnerabilitiesDescription"
|
||||
defaultMessage="No vulnerabilities"
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
};
|
||||
|
||||
const VulnerabilitiesCount = ({
|
||||
vulnerabilitiesTotal,
|
||||
euiTheme,
|
||||
}: {
|
||||
vulnerabilitiesTotal: string | number;
|
||||
euiTheme: EuiThemeComputed<{}>;
|
||||
}) => {
|
||||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s">
|
||||
<h1>{vulnerabilitiesTotal}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText
|
||||
size="m"
|
||||
css={css`
|
||||
font-weight: ${euiTheme.font.weight.semiBold};
|
||||
`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.vulnerabilities.vulnerabilitiesCountDescription"
|
||||
defaultMessage="Vulnerabilities"
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const VulnerabilitiesPreview = ({ hostName }: { hostName: string }) => {
|
||||
const { data } = useVulnerabilitiesPreview({
|
||||
query: buildEntityFlyoutPreviewQuery('host.name', hostName),
|
||||
sort: [],
|
||||
enabled: true,
|
||||
pageSize: 1,
|
||||
});
|
||||
|
||||
const { CRITICAL = 0, HIGH = 0, MEDIUM = 0, LOW = 0, NONE = 0 } = data?.count || {};
|
||||
|
||||
const totalVulnerabilities = CRITICAL + HIGH + MEDIUM + LOW + NONE;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const hasVulnerabilities = totalVulnerabilities > 0;
|
||||
return (
|
||||
<ExpandablePanel
|
||||
header={{
|
||||
title: (
|
||||
<EuiText
|
||||
size="xs"
|
||||
css={css`
|
||||
font-weight: ${euiTheme.font.weight.semiBold};
|
||||
`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.flyout.right.insights.vulnerabilities.vulnerabilitiesTitle"
|
||||
defaultMessage="Vulnerabilities"
|
||||
/>
|
||||
</EuiText>
|
||||
),
|
||||
}}
|
||||
data-test-subj={'securitySolutionFlyoutInsightsVulnerabilities'}
|
||||
>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
{hasVulnerabilities ? (
|
||||
<VulnerabilitiesCount
|
||||
vulnerabilitiesTotal={getAbbreviatedNumber(totalVulnerabilities)}
|
||||
euiTheme={euiTheme}
|
||||
/>
|
||||
) : (
|
||||
<VulnerabilitiesEmptyState euiTheme={euiTheme} />
|
||||
)}
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem />
|
||||
<EuiFlexItem>
|
||||
<EuiSpacer />
|
||||
<DistributionBar stats={getVulnerabilityStats(CRITICAL, HIGH, MEDIUM, LOW, NONE)} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</ExpandablePanel>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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 { CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN } from '@kbn/cloud-security-posture-common';
|
||||
import { createRule } from '../../../../tasks/api_calls/rules';
|
||||
import { getNewRule } from '../../../../objects/rule';
|
||||
import { getDataTestSubjectSelector } from '../../../../helpers/common';
|
||||
|
||||
import { rootRequest, deleteAlertsAndRules } from '../../../../tasks/api_calls/common';
|
||||
import { expandFirstAlertHostFlyout } from '../../../../tasks/asset_criticality/common';
|
||||
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
|
||||
import { login } from '../../../../tasks/login';
|
||||
import { ALERTS_URL } from '../../../../urls/navigation';
|
||||
import { visit } from '../../../../tasks/navigation';
|
||||
|
||||
const CSP_INSIGHT_VULNERABILITIES_TITLE = getDataTestSubjectSelector(
|
||||
'securitySolutionFlyoutInsightsVulnerabilitiesTitleText'
|
||||
);
|
||||
|
||||
const NO_VULNERABILITIES_TEXT = getDataTestSubjectSelector('noVulnerabilitiesDataTestSubj');
|
||||
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Create a Date object using the timestamp
|
||||
const date = new Date(timestamp);
|
||||
|
||||
// Convert the Date object to ISO 8601 format
|
||||
const iso8601String = date.toISOString();
|
||||
|
||||
const getMockVulnerability = (isNameMatchesAlert: boolean) => {
|
||||
return {
|
||||
'@timestamp': iso8601String,
|
||||
resource: { name: '634yfsdg2.dkr.ecr.eu-central-1.amazon.stage', id: 'ami_12328' },
|
||||
agent: {
|
||||
name: 'ip-172-31-33-74',
|
||||
type: 'cloudbeat',
|
||||
version: '8.8.0',
|
||||
ephemeral_id: '49f19e6a-94e9-4f2b-81e3-2f3794a74068',
|
||||
id: 'd0313a94-c168-4d95-b1f0-97a388dac29a',
|
||||
},
|
||||
cloud: {
|
||||
availability_zone: 'eu-west-1c',
|
||||
service: { name: 'EC2' },
|
||||
account: { id: '704479110758' },
|
||||
image: { id: 'ami-02dc8dbcc971f2c74' },
|
||||
provider: 'aws',
|
||||
instance: { id: 'i-0fb7759c6e5d400cf' },
|
||||
machine: { type: 'c6g.medium' },
|
||||
region: 'eu-west-1',
|
||||
},
|
||||
package: { fixed_version: '0.4.0', version: 'v0.2.0', name: 'golang.org/x/net' },
|
||||
vulnerability: {
|
||||
published_date: '2022-08-10T00:00:00.000Z',
|
||||
data_source: {
|
||||
ID: 'go-vulndb',
|
||||
Name: 'The Go Vulnerability Database',
|
||||
URL: 'https://github.com/golang/vulndb',
|
||||
},
|
||||
enumeration: 'CVE',
|
||||
description:
|
||||
'An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests. HTTP/2 server connections contain a cache of HTTP header keys sent by the client. While the total number of entries in this cache is capped, an attacker sending very large keys can cause the server to allocate approximately 64 MiB per open connection.',
|
||||
title:
|
||||
'golang: net/http: An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests',
|
||||
reference: 'https://avd.aquasec.com/nvd/cve-2022-41717',
|
||||
severity: 'MEDIUM',
|
||||
cvss: {
|
||||
nvd: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 },
|
||||
redhat: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 },
|
||||
ghsa: { V3Vector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L', V3Score: 5.3 },
|
||||
},
|
||||
scanner: { vendor: 'Trivy' },
|
||||
score: { base: 5.3, version: '3.0' },
|
||||
cwe: ['CWE-770'],
|
||||
id: 'CVE-2022-41717',
|
||||
classification: 'CVSS',
|
||||
},
|
||||
cloudbeat: {
|
||||
commit_sha: 'b5c4b728f0a9268e7f2d195c00dad0320c8a74e6',
|
||||
commit_time: '2023-03-30T07:47:06Z',
|
||||
version: '8.8.0',
|
||||
},
|
||||
event: {
|
||||
category: ['vulnerability'],
|
||||
created: '2023-03-30T10:27:35.013537768Z',
|
||||
id: '5cfbcbe5-7f90-47b8-b1d4-7f79313b2a6d',
|
||||
kind: 'state',
|
||||
sequence: 1680172055,
|
||||
outcome: 'success',
|
||||
type: ['info'],
|
||||
},
|
||||
ecs: { version: '8.0.0' },
|
||||
host: {
|
||||
os: {
|
||||
kernel: '5.15.0-1028-aws',
|
||||
codename: 'jammy',
|
||||
type: 'linux',
|
||||
platform: 'ubuntu',
|
||||
version: '22.04.1 LTS (Jammy Jellyfish)',
|
||||
family: 'debian',
|
||||
name: 'Ubuntu',
|
||||
},
|
||||
id: 'ec2644f440799ed0cf8aa595a9a105cc',
|
||||
containerized: false,
|
||||
name: isNameMatchesAlert ? 'siem-kibana' : 'not-siem-kibana',
|
||||
ip: ['172.31.33.74', 'fe80::85d:f0ff:fe91:c01b'],
|
||||
mac: ['0A-5D-F0-91-C0-1B'],
|
||||
hostname: 'ip-172-31-33-74',
|
||||
architecture: 'aarch64',
|
||||
},
|
||||
data_stream: {
|
||||
dataset: 'cloud_security_posture.vulnerabilities',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createMockVulnerability = (isNameMatchesAlert: boolean) => {
|
||||
return rootRequest({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env(
|
||||
'ELASTICSEARCH_URL'
|
||||
)}/${CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN}/_doc`,
|
||||
body: getMockVulnerability(isNameMatchesAlert),
|
||||
});
|
||||
};
|
||||
|
||||
const deleteDataStream = () => {
|
||||
return rootRequest({
|
||||
method: 'DELETE',
|
||||
url: `${Cypress.env(
|
||||
'ELASTICSEARCH_URL'
|
||||
)}/_data_stream/${CDR_LATEST_NATIVE_VULNERABILITIES_INDEX_PATTERN}`,
|
||||
});
|
||||
};
|
||||
|
||||
describe('Alert Host details expandable flyout', { tags: ['@ess', '@serverless'] }, () => {
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
login();
|
||||
createRule(getNewRule());
|
||||
visit(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
context('No Vulnerabilities Findings', () => {
|
||||
it('should not display Vulnerabilities preview under Insights Entities when it does not have Vulnerabilities Findings', () => {
|
||||
expandFirstAlertHostFlyout();
|
||||
|
||||
cy.log('check if Vulnerabilities preview title is not shown');
|
||||
cy.get(CSP_INSIGHT_VULNERABILITIES_TITLE).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
context('Host name - Has Vulnerabilities findings', () => {
|
||||
beforeEach(() => {
|
||||
createMockVulnerability(true);
|
||||
cy.reload();
|
||||
expandFirstAlertHostFlyout();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
deleteDataStream();
|
||||
});
|
||||
|
||||
it('should display Vulnerabilities preview under Insights Entities when it has Vulnerabilities Findings', () => {
|
||||
cy.log('check if Vulnerabilities preview title shown');
|
||||
cy.get(CSP_INSIGHT_VULNERABILITIES_TITLE).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
context(
|
||||
'Host name - Has Vulnerabilities findings but host name is not the same as alert host name',
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
createMockVulnerability(false);
|
||||
cy.reload();
|
||||
expandFirstAlertHostFlyout();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
deleteDataStream();
|
||||
});
|
||||
|
||||
it('should display Vulnerabilities preview under Insights Entities when it has Vulnerabilities Findings but it should show no vulnerabilities title', () => {
|
||||
cy.log('check if Vulnerabilities preview title shown');
|
||||
cy.get(CSP_INSIGHT_VULNERABILITIES_TITLE).should('be.visible');
|
||||
cy.log('check if no vulnerabilities text is shown');
|
||||
cy.get(NO_VULNERABILITIES_TEXT).should('be.visible');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
|
@ -44,5 +44,6 @@
|
|||
"@kbn/securitysolution-endpoint-exceptions-common",
|
||||
"@kbn/repo-info",
|
||||
"@kbn/elastic-assistant-common",
|
||||
"@kbn/cloud-security-posture-common",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue