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

[CTI] adds Risky Host Overview Card #109553

Merged
merged 36 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fa8d99e
[CTI] adds Risky Host Overview Card
ecezalp Aug 20, 2021
7da420c
add unit tests
ecezalp Aug 23, 2021
d6fdd56
remove unused translation
ecezalp Aug 23, 2021
620ad4c
Merge branch 'master' into security-team-1308
kibanamachine Aug 23, 2021
2bb7903
fix types, i18n
ecezalp Aug 23, 2021
09d657b
adds integration test coverage
ecezalp Aug 23, 2021
a363b5b
Merge branch 'master' into security-team-1308
kibanamachine Aug 23, 2021
1f0b225
fix type
ecezalp Aug 24, 2021
5df51bf
adds defaultSort to risky hosts
ecezalp Aug 24, 2021
6e6a51b
hides links
ecezalp Aug 24, 2021
6f636eb
increate securitySolution bundle size limit
ecezalp Aug 24, 2021
75668fe
Merge branch 'master' into security-team-1308
kibanamachine Aug 24, 2021
588a287
Merge branch 'master' into security-team-1308
kibanamachine Aug 25, 2021
1ad0305
Merge branch 'master' into security-team-1308
ecezalp Sep 2, 2021
ee4ee86
Merge branch 'master' into security-team-1308
kibanamachine Sep 2, 2021
7da8199
updates tests
ecezalp Sep 2, 2021
71c05be
fix types
ecezalp Sep 3, 2021
5441214
Merge branch 'master' into security-team-1308
kibanamachine Sep 6, 2021
465e273
remove translation change
ecezalp Sep 13, 2021
d750217
Merge branch 'master' into security-team-1308
kibanamachine Sep 13, 2021
3e843a3
add translation change
ecezalp Sep 13, 2021
7bf93d3
fix race
ecezalp Sep 14, 2021
8899db4
adds feature flag
ecezalp Sep 14, 2021
d29e7d6
mocks experimental hook
ecezalp Sep 14, 2021
96fdf5e
Merge branch 'master' into security-team-1308
kibanamachine Sep 14, 2021
97e67d7
add flag to cypress config
ecezalp Sep 15, 2021
2572427
Merge branch 'master' into security-team-1308
kibanamachine Sep 20, 2021
a0e0b96
capitalizes link copy, fixes linting
ecezalp Sep 20, 2021
553ae3e
add host link to host name
ecezalp Sep 20, 2021
5c68f1f
remove unnecessary import
ecezalp Sep 20, 2021
a9dca41
remove nonexistent import
ecezalp Sep 20, 2021
e9ddd2a
fix dashboard items not displaying when there is no dashboardId
ecezalp Sep 22, 2021
29284b9
Merge branch 'master' into security-team-1308
kibanamachine Sep 22, 2021
2967919
Merge branch 'master' into security-team-1308
kibanamachine Sep 23, 2021
af99857
Merge branch 'master' into security-team-1308
kibanamachine Sep 23, 2021
ab14a4d
Merge branch 'master' into security-team-1308
kibanamachine Sep 27, 2021
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
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pageLoadAssetSize:
searchprofiler: 67080
security: 95864
securityOss: 30806
securitySolution: 217673
securitySolution: 224673
share: 99061
snapshotRestore: 79032
spaces: 57868
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './common';
export * from './details';
export * from './first_last_seen';
export * from './kpi';
export * from './risky_hosts';
export * from './overview';
export * from './uncommon_processes';

Expand All @@ -22,5 +23,6 @@ export enum HostsQueries {
hosts = 'hosts',
hostsEntities = 'hostsEntities',
overview = 'overviewHost',
riskyHosts = 'riskyHosts',
uncommonProcesses = 'uncommonProcesses',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { Inspect, Maybe, RequestBasicOptions } from '../../..';
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';

export type HostsRiskyHostsRequestOptions = RequestBasicOptions;

export interface HostsRiskyHostsStrategyResponse extends IEsSearchResponse {
inspect?: Maybe<Inspect>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
HostsKpiUniqueIpsStrategyResponse,
HostsKpiUniqueIpsRequestOptions,
HostFirstLastSeenRequestOptions,
HostsRiskyHostsStrategyResponse,
HostsRiskyHostsRequestOptions,
} from './hosts';
import {
NetworkQueries,
Expand Down Expand Up @@ -123,6 +125,8 @@ export type StrategyResponseType<T extends FactoryQueryTypes> = T extends HostsQ
: T extends HostsQueries.details
? HostDetailsStrategyResponse
: T extends UebaQueries.riskScore
? HostsRiskyHostsStrategyResponse
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a typo? It looks like the response type is associated to the wrong T here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed, thank you!

: T extends HostsQueries.riskyHosts
? RiskScoreStrategyResponse
: T extends UebaQueries.hostRules
? HostRulesStrategyResponse
Expand Down Expand Up @@ -180,6 +184,8 @@ export type StrategyRequestType<T extends FactoryQueryTypes> = T extends HostsQu
? HostsRequestOptions
: T extends HostsQueries.details
? HostDetailsRequestOptions
: T extends HostsQueries.riskyHosts
? HostsRiskyHostsRequestOptions
: T extends HostsQueries.overview
? HostOverviewRequestOptions
: T extends HostsQueries.authentications
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 {
OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON,
OVERVIEW_RISKY_HOSTS_LINKS,
OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL,
OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL,
OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT,
OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON,
} from '../../screens/overview';

import { loginAndWaitForPage } from '../../tasks/login';
import { OVERVIEW_URL } from '../../urls/navigation';
import { cleanKibana } from '../../tasks/common';
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';

describe('Risky Hosts Link Panel', () => {
before(() => {
cleanKibana();
});

it('renders disabled panel view as expected', () => {
loginAndWaitForPage(OVERVIEW_URL);
cy.get(`${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL}`).should(
'exist'
);
cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled');
cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts');
cy.get(`${OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON}`).should('exist');
cy.get(`${OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON}`)
.should('have.attr', 'href')
.and('match', /host-risk-score.md/);
});

describe('enabled module', () => {
before(() => {
esArchiverLoad('risky_hosts');
});

after(() => {
esArchiverUnload('risky_hosts');
});

it('renders disabled dashboard module as expected when there are no hosts in the selected time period', () => {
loginAndWaitForPage(
`${OVERVIEW_URL}?sourcerer=(timerange:(from:%272021-07-08T04:00:00.000Z%27,kind:absolute,to:%272021-07-09T03:59:59.999Z%27))`
);
cy.get(
`${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}`
).should('exist');
cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled');
cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts');
});

it('renders dashboard module as expected when there are hosts in the selected time period', () => {
loginAndWaitForPage(OVERVIEW_URL);
cy.get(
`${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}`
).should('not.exist');
cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled');
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add tests that cover cases when the dashboard button is enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there were some complexities around uploading dashboard saved objects from last time around, there is an issue to track that work here at elastic/security-team#1438. I will add that issue to our 7.16checklist as a backlog item

cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 host');
});
});
});
11 changes: 11 additions & 0 deletions x-pack/plugins/security_solution/cypress/screens/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,14 @@ export const OVERVIEW_CTI_LINKS_INFO_INNER_PANEL = '[data-test-subj="cti-inner-p
export const OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON = '[data-test-subj="cti-view-dashboard-button"]';
export const OVERVIEW_CTI_TOTAL_EVENT_COUNT = `${OVERVIEW_CTI_LINKS} [data-test-subj="header-panel-subtitle"]`;
export const OVERVIEW_CTI_ENABLE_MODULE_BUTTON = '[data-test-subj="cti-enable-module-button"]';

export const OVERVIEW_RISKY_HOSTS_LINKS = '[data-test-subj="risky-hosts-dashboard-links"]';
export const OVERVIEW_RISKY_HOSTS_LINKS_ERROR_INNER_PANEL =
'[data-test-subj="risky-hosts-inner-panel-danger"]';
export const OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL =
'[data-test-subj="risky-hosts-inner-panel-warning"]';
export const OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON =
'[data-test-subj="risky-hosts-view-dashboard-button"]';
export const OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT = `${OVERVIEW_RISKY_HOSTS_LINKS} [data-test-subj="header-panel-subtitle"]`;
export const OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON =
'[data-test-subj="risky-hosts-enable-module-button"]';
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { defaultControlColumn } from '../../../timelines/components/timeline/bod
import { EventsViewer } from './events_viewer';
import * as i18n from './translations';
import { GraphOverlay } from '../../../timelines/components/graph_overlay';

const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = [];
const leadingControlColumns: ControlColumnProps[] = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 { mount } from 'enzyme';
import { TestProviders } from '../../../common/mock';
import { DisabledLinkPanel } from './disabled_link_panel';
import { ThreatIntelPanelView as TestView } from '../overview_cti_links/threat_intel_panel_view';

jest.mock('../../../common/lib/kibana');

describe('DisabledLinkPanel', () => {
const defaultProps = {
docLink: '/doclink',
listItems: [],
titleCopy: 'title',
bodyCopy: 'body',
buttonCopy: 'button',
dataTestSubjPrefix: 'test-prefix',
LinkPanelViewComponent: TestView,
};

it('renders expected children', () => {
const wrapper = mount(
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you be able to rewrite this test using React Testing Library? We're trying to phase out enzyme in favor of it.

<TestProviders>
<DisabledLinkPanel {...defaultProps} />
</TestProviders>
);

expect(
wrapper.exists(
'[data-test-subj="test-prefix-inner-panel-danger"] [data-test-subj="test-prefix-enable-module-button"]'
)
).toEqual(true);
expect(
wrapper.find('[data-test-subj="test-prefix-enable-module-button"]').hostNodes().text()
).toEqual(defaultProps.buttonCopy);
expect(wrapper.find('a').hostNodes().props().href).toEqual(defaultProps.docLink);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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, { memo } from 'react';
import { EuiButton } from '@elastic/eui';

import { InnerLinkPanel } from './inner_link_panel';
import { LinkPanelListItem, LinkPanelViewProps } from './types';

interface DisabledLinkPanelProps {
docLink: string;
listItems: LinkPanelListItem[];
titleCopy: string;
bodyCopy: string;
buttonCopy: string;
dataTestSubjPrefix: string;
LinkPanelViewComponent: React.ComponentType<LinkPanelViewProps>;
}

const DisabledLinkPanelComponent: React.FC<DisabledLinkPanelProps> = ({
docLink,
listItems,
titleCopy,
bodyCopy,
buttonCopy,
dataTestSubjPrefix,
LinkPanelViewComponent,
}) => {
return (
<LinkPanelViewComponent
splitPanel={
<InnerLinkPanel
color="warning"
title={titleCopy}
body={bodyCopy}
button={
<EuiButton
href={docLink}
color="warning"
target="_blank"
data-test-subj={`${dataTestSubjPrefix}-enable-module-button`}
>
{buttonCopy}
</EuiButton>
}
dataTestSubj={`${dataTestSubjPrefix}-inner-panel-danger`}
/>
}
listItems={listItems}
/>
);
};

export const DisabledLinkPanel = memo(DisabledLinkPanelComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { LinkPanelListItem } from '.';

export const isLinkPanelListItem = (
item: LinkPanelListItem | Partial<LinkPanelListItem>
): item is LinkPanelListItem =>
typeof item.title === 'string' && typeof item.path === 'string' && typeof item.count === 'number';

export interface EventCounts {
[key: string]: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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.
*/

export { LinkPanel } from './link_panel';
export { InnerLinkPanel } from './inner_link_panel';
export { LinkPanelListItem } from './types';
export { isLinkPanelListItem } from './helpers';
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { mount } from 'enzyme';
import { TestProviders } from '../../../common/mock';
import { InnerLinkPanel } from './inner_link_panel';

describe('InnerLinkPanel', () => {
const defaultProps = {
title: 'test_title',
body: 'test_body',
button: <div className={'test-button'} />,
dataTestSubj: 'test-subj',
};

it('renders expected children', () => {
const wrapper = mount(
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto on using @testing-library here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

<TestProviders>
<InnerLinkPanel color="warning" {...defaultProps} />
</TestProviders>
);

expect(wrapper.exists('[data-test-subj="test-subj"]')).toEqual(true);
expect(wrapper.exists('.test-button')).toEqual(true);
expect(wrapper.find('[data-test-subj="inner-link-panel-title"]').hostNodes().text()).toEqual(
defaultProps.title
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Icon = styled(EuiIcon)`
transform: scale(${({ color }) => (color === 'primary' ? 1.4 : 1)});
`;

export const CtiInnerPanel = ({
export const InnerLinkPanel = ({
color,
title,
body,
Expand All @@ -46,12 +46,14 @@ export const CtiInnerPanel = ({
const iconType = color === 'primary' ? 'iInCircle' : 'help';
return (
<PanelContainer grow={false} color={color}>
<EuiFlexGroup direction={'column'} data-test-subj={dataTestSubj}>
<EuiFlexGroup direction="column" data-test-subj={dataTestSubj}>
<EuiFlexItem>
<EuiFlexGroup direction={'row'}>
<EuiFlexGroup direction="row">
<Icon type={iconType} size="m" color={color} />
<EuiFlexItem>
<Title textcolor={color}>{title}</Title>
<Title data-test-subj="inner-link-panel-title" textcolor={color}>
{title}
</Title>
</EuiFlexItem>
</EuiFlexGroup>
{body}
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 React from 'react';
import { mount } from 'enzyme';
import { Link } from './link';

describe('Link', () => {
it('renders <a> tag when there is a path', () => {
const wrapper = mount(<Link copy={'test'} path={'/path'} />);

expect(wrapper.exists('a')).toEqual(true);
expect(wrapper.find('a').hostNodes().props().href).toEqual('/path');
expect(wrapper.find('a').hostNodes().text()).toEqual('test(opens in a new tab or window)');
});

it('does not render <a> tag when there is no path', () => {
const wrapper = mount(<Link copy={'test'} />);

expect(wrapper.exists('a')).toEqual(false);
expect(wrapper.find('div').hostNodes().at(0).text()).toEqual('test');
});
});
Loading