Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cloud Security] Vulnerabilities Preview & Refactor CSP Plugin PHASE 2 #193638

Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4a2caae
initial commit: updated imports for constants and vuln types, TODO: U…
animehart Sep 19, 2024
2539143
update vulnerability schema imports
animehart Sep 19, 2024
e9ee30a
initial push
animehart Sep 20, 2024
1b6761f
move get_abbreviated_number function to shared package + updated impo…
animehart Sep 20, 2024
465b6a3
Merge branch 'main' into vulnerabilities-preview-phase1
animehart Sep 20, 2024
543cf52
pr comments
animehart Sep 20, 2024
656bce6
Merge branch 'vulnerabilities-preview-phase1' of github.com:animehart…
animehart Sep 20, 2024
0af9662
Merge branch 'vulnerabilities-preview-phase1' into vulnerabilities-pr…
animehart Sep 20, 2024
48f0bff
Merge branch 'main' into vulnerabilities-preview-phase2-branchedfromp…
animehart Sep 20, 2024
611e654
update Entity Insight component to only show Vuln if Vuln Findings ex…
animehart Sep 20, 2024
b53f2e8
Merge branch 'vulnerabilities-preview-phase2-branchedfromphase1' of g…
animehart Sep 20, 2024
4c94880
clean up and reversing Vuln severity bar color
animehart Sep 21, 2024
06765c6
Merge branch 'main' into vulnerabilities-preview-phase2-branchedfromp…
animehart Sep 23, 2024
cfd6f35
fixed quick check translation
animehart Sep 23, 2024
b3e5cab
added cypress for Vulnerabilities preview
animehart Sep 24, 2024
18c6d78
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Sep 24, 2024
01f717e
added code owners for cypress
animehart Sep 24, 2024
cd0f573
Merge branch 'vulnerabilities-preview-phase2-branchedfromphase1' of g…
animehart Sep 25, 2024
f5dac9b
clean up + added jest
animehart Sep 30, 2024
fa4d467
pr comments
animehart Sep 30, 2024
a7a20ca
more pr comments
animehart Sep 30, 2024
8c9c384
update unknown severity color
animehart Oct 1, 2024
ba589da
Merge branch 'main' into vulnerabilities-preview-phase2-branchedfromp…
animehart Oct 1, 2024
ccf37c1
updated interface name to be more inclusive + fix issue with query th…
animehart Oct 2, 2024
d56fa1f
Merge branch 'vulnerabilities-preview-phase2-branchedfromphase1' of g…
animehart Oct 2, 2024
7c7aa63
fix failed jest
animehart Oct 2, 2024
cbd708e
moved cypress to another folder
animehart Oct 2, 2024
0172edf
improvements, pr comments
animehart Oct 3, 2024
389592a
Merge branch 'main' into vulnerabilities-preview-phase2-branchedfromp…
animehart Oct 3, 2024
c9ca88c
change severity unknown to severity none to match what we have on vul…
animehart Oct 3, 2024
f549e22
Merge branch 'vulnerabilities-preview-phase2-branchedfromphase1' of g…
animehart Oct 3, 2024
b117e44
moved getSeverityStatusColor to shared package, refactor a bit, PR co…
animehart Oct 4, 2024
71e2a5c
fix quick check fail
animehart Oct 4, 2024
da540f1
Merge branch 'main' into vulnerabilities-preview-phase2-branchedfromp…
maxcold Oct 4, 2024
5d34c0f
more pr comments
animehart Oct 4, 2024
c1f29d4
Merge branch 'vulnerabilities-preview-phase2-branchedfromphase1' of g…
animehart Oct 4, 2024
b69cff8
forgot to update test
animehart Oct 4, 2024
6e0df80
fix linting
animehart Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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,
MAX_FINDINGS_TO_LOAD,
} 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, UseMisconfigurationOptions } 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>;
}

export const getVulnerabilitiesQuery = (
{ query }: UseMisconfigurationOptions,
isPreview = false
) => ({
index: CDR_VULNERABILITIES_INDEX_PATTERN,
size: MAX_FINDINGS_TO_LOAD,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need any actual documents for the preview, only aggs, correct? In that case should be size: 0
We already have similar logic in the misconfigs, let's follow the same here

aggs: getFindingsCountAggQueryVulnerabilities(),
query: {
...query,
bool: {
...query?.bool,
filter: [
...(query?.bool?.filter ?? []),
{
range: {
'@timestamp': {
gte: `now-${LATEST_VULNERABILITIES_RETENTION_POLICY}`,
lte: 'now',
},
},
},
],
},
},
});

export const useVulnerabilitiesPreview = (options: UseMisconfigurationOptions) => {
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.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment should be removed, as it is not true

return useQuery(
['csp_vulnerabilities_preview', { params: options }],
async ({ pageParam }) => {
const {
rawResponse: { aggregations },
} = await lastValueFrom(
data.search.search<LatestFindingsRequest, LatestFindingsResponse>({
params: getVulnerabilitiesQuery(options, pageParam),
})
);
return {
count: getVulnerabilitiesAggregationCount(aggregations?.count?.buckets),
};
},
{
staleTime: 5000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this staleTime from here, I originally introduced it on the useLatestVulnerabilities to help with performance issues of the vulnerabilities data grid, but that should not be required here

keepPreviousData: true,
enabled: options.enabled,
onError: (err: Error) => showErrorToast(toasts, err),
}
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ const RESULT_EVALUATION = {
UNKNOWN: 'unknown',
};

const VULNERABILITIES_RESULT_EVALUATION = {
LOW: 'LOW',
MEDIUM: 'MEDIUM',
HIGH: 'HIGH',
CRITICAL: 'CRITICAL',
UNKNOWN: 'UNKNOWN',
};

export const getFindingsCountAggQueryMisconfiguration = () => ({
count: {
filters: {
Expand All @@ -39,6 +47,8 @@ export const getFindingsCountAggQueryMisconfiguration = () => ({
},
});

// export type VulnSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' | 'UNKNOWN';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be removed


export const getMisconfigurationAggregationCount = (
buckets?: estypes.AggregationsBuckets<estypes.AggregationsStringRareTermsBucketKeys>
) => {
Expand Down Expand Up @@ -103,3 +113,53 @@ const buildMisconfigurationsFindingsQueryWithFilters = (
},
};
};

export const getVulnerabilitiesAggregationCount = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add some unit tests for the utils here

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.UNKNOWN]: { 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.UNKNOWN]: 0,
}
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
[VULNERABILITIES_RESULT_EVALUATION.LOW]: 0,
[VULNERABILITIES_RESULT_EVALUATION.MEDIUM]: 0,
[VULNERABILITIES_RESULT_EVALUATION.HIGH]: 0,
[VULNERABILITIES_RESULT_EVALUATION.CRITICAL]: 0,
[VULNERABILITIES_RESULT_EVALUATION.UNKNOWN]: 0,
}
);
...defaultBuckets
);

};

export const getFindingsCountAggQueryVulnerabilities = () => ({
count: {
filters: {
other_bucket_key: VULNERABILITIES_RESULT_EVALUATION.UNKNOWN,
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 },
},
},
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -25,10 +26,26 @@ export const EntityInsight = <T,>({
const { euiTheme } = useEuiTheme();
const getSetupStatus = useCspSetupStatusApi();
const hasMisconfigurationFindings = getSetupStatus.data?.hasMisconfigurationsFindings;

const hasVulnerabilitiesFindings = getSetupStatus.data?.hasVulnerabilitiesFindings;
const insightContent: React.ReactElement[] = [];
if (hasMisconfigurationFindings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:nit can we add a space after between the const and if

insightContent.push(
<>
<MisconfigurationsPreview name={name} fieldName={fieldName} isPreviewMode={isPreviewMode} />
<EuiSpacer size="m" />
</>
);
if (hasVulnerabilitiesFindings && fieldName === 'host.name')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the condition is duplicated on line 39 and 49, I would move it to a single constant to avoid them keeping out of sync

insightContent.push(
<>
<VulnerabilitiesPreview hostName={name} />
<EuiSpacer size="m" />
</>
);
return (
<>
{hasMisconfigurationFindings && (
{(hasMisconfigurationFindings ||
(hasVulnerabilitiesFindings && fieldName === 'host.name')) && (
<>
<EuiAccordion
initialIsOpen={true}
Expand All @@ -52,12 +69,7 @@ export const EntityInsight = <T,>({
}
>
<EuiSpacer size="m" />
<MisconfigurationsPreview
name={name}
fieldName={fieldName}
isPreviewMode={isPreviewMode}
/>
<EuiSpacer size="m" />
{insightContent}
</EuiAccordion>
<EuiHorizontalRule />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* 2.0.
*/

// Add stuff here
import { TestProviders } from '../../../common/mock';
import { render } from '@testing-library/react';
import React from 'react';
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
});
});
Loading