From 82cdf07145a6e473d3cd9babe9e760676029f453 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Thu, 9 Dec 2021 18:10:07 +0100 Subject: [PATCH 1/5] Create HoverVisibilityContainer component --- .../hover_visibility_container/index.test.tsx | 54 +++++++++++++++++++ .../hover_visibility_container/index.tsx | 54 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.test.tsx new file mode 100644 index 0000000000000..80b145791ba43 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.test.tsx @@ -0,0 +1,54 @@ +/* + * 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 { mount } from 'enzyme'; +import React from 'react'; + +import { TestProviders } from '../../mock'; + +import { HoverVisibilityContainer } from '.'; + +describe('HoverVisibilityContainer', () => { + const targetClass1 = 'Component1'; + const targetClass2 = 'Component2'; + const Component1 = () =>
; + const Component2 = () =>
; + + test('it renders a transparent inspect button by default', () => { + const wrapper = mount( + + + + + + + ); + expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '0', { + modifier: `.${targetClass1}`, + }); + expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', { + modifier: `:hover .${targetClass2}`, + }); + }); + + test('it renders an opaque inspect button when it has mouse focus', () => { + const wrapper = mount( + + + + + + + ); + expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', { + modifier: `:hover .${targetClass1}`, + }); + expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', { + modifier: `:hover .${targetClass2}`, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.tsx b/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.tsx new file mode 100644 index 0000000000000..563f6e322cb48 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/hover_visibility_container/index.tsx @@ -0,0 +1,54 @@ +/* + * 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 styled, { css } from 'styled-components'; +import { getOr } from 'lodash/fp'; + +const StyledDiv = styled.div<{ targetClassNames: string[] }>` + width: 100%; + display: flex; + flex-grow: 1; + + > * { + max-width: 100%; + } + + ${({ targetClassNames }) => + css` + ${targetClassNames.map((cn) => `.${cn}`).join(', ')} { + pointer-events: none; + opacity: 0; + transition: opacity ${(props) => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease; + } + + ${targetClassNames.map((cn) => `&:hover .${cn}`).join(', ')} { + pointer-events: auto; + opacity: 1; + } + `} +`; + +interface HoverVisibilityContainerProps { + show?: boolean; + children: React.ReactNode; + targetClassNames: string[]; +} + +export const HoverVisibilityContainer = React.memo( + ({ show = true, targetClassNames, children }) => { + if (!show) return <>{children}; + + return ( + + {children} + + ); + } +); + +HoverVisibilityContainer.displayName = 'HoverVisibilityContainer'; From 16d9406f54b0b58a374de24072f9c125340eb4e9 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Mon, 13 Dec 2021 12:14:59 +0100 Subject: [PATCH 2/5] Add Host risk information flyout to Host rik KPI panel --- .../integration/hosts/risky_hosts_kpi.spec.ts | 2 +- .../common/components/inspect/index.test.tsx | 32 +--- .../common/components/inspect/index.tsx | 46 ++--- .../common/host_risk_score.test.tsx | 10 ++ .../components/common/host_risk_score.tsx | 17 +- .../host_risk_information/index.test.tsx | 33 ++++ .../host_risk_information/index.tsx | 161 ++++++++++++++++++ .../host_risk_information/translations.ts | 29 ++++ .../kpi_hosts/risky_hosts/index.tsx | 23 ++- 9 files changed, 282 insertions(+), 71 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx create mode 100644 x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/hosts/risky_hosts_kpi.spec.ts b/x-pack/plugins/security_solution/cypress/integration/hosts/risky_hosts_kpi.spec.ts index 602a9118128b5..6661e6308971f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/hosts/risky_hosts_kpi.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/hosts/risky_hosts_kpi.spec.ts @@ -10,7 +10,7 @@ import { loginAndWaitForPage } from '../../tasks/login'; import { HOSTS_URL } from '../../urls/navigation'; describe('RiskyHosts KPI', () => { - it('it renders', () => { + it('renders', () => { loginAndWaitForPage(HOSTS_URL); cy.get('[data-test-subj="riskyHostsTotal"]').should('have.text', '0 Risky Hosts'); diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx index 6f3e28469a949..d73b4cb7d98d6 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx @@ -18,7 +18,7 @@ import { import { createStore, State } from '../../store'; import { UpdateQueryParams, upsertQuery } from '../../store/inputs/helpers'; -import { InspectButton, InspectButtonContainer, BUTTON_CLASS } from '.'; +import { InspectButton } from '.'; import { cloneDeep } from 'lodash/fp'; describe('Inspect Button', () => { @@ -103,36 +103,6 @@ describe('Inspect Button', () => { ); expect(wrapper.find('.euiButtonIcon').get(0).props.disabled).toBe(true); }); - - describe('InspectButtonContainer', () => { - test('it renders a transparent inspect button by default', async () => { - const wrapper = mount( - - - - - - ); - - expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '0', { - modifier: `.${BUTTON_CLASS}`, - }); - }); - - test('it renders an opaque inspect button when it has mouse focus', async () => { - const wrapper = mount( - - - - - - ); - - expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '1', { - modifier: `:hover .${BUTTON_CLASS}`, - }); - }); - }); }); describe('Modal Inspect - happy path', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx index a0e2ff266ad28..4f52703620b5f 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx @@ -6,50 +6,34 @@ */ import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; -import { getOr, omit } from 'lodash/fp'; +import { omit } from 'lodash/fp'; import React, { useCallback } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import styled, { css } from 'styled-components'; import { inputsSelectors, State } from '../../store'; import { InputsModelId } from '../../store/inputs/constants'; import { inputsActions } from '../../store/inputs'; +import { HoverVisibilityContainer } from '../hover_visibility_container'; + import { ModalInspectQuery } from './modal'; import * as i18n from './translations'; export const BUTTON_CLASS = 'inspectButtonComponent'; -export const InspectButtonContainer = styled.div<{ show?: boolean }>` - width: 100%; - display: flex; - flex-grow: 1; - - > * { - max-width: 100%; - } - - .${BUTTON_CLASS} { - pointer-events: none; - opacity: 0; - transition: opacity ${(props) => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease; - } - - ${({ show }) => - show && - css` - &:hover .${BUTTON_CLASS} { - pointer-events: auto; - opacity: 1; - } - `} -`; - -InspectButtonContainer.displayName = 'InspectButtonContainer'; +interface InspectButtonContainerProps { + show?: boolean; + children: React.ReactNode; +} -InspectButtonContainer.defaultProps = { - show: true, -}; +export const InspectButtonContainer: React.FC = ({ + children, + show = true, +}) => ( + + {children} + +); interface OwnProps { compact?: boolean; diff --git a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.test.tsx index 4f70dce3c1160..1badc0206d12e 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.test.tsx @@ -99,4 +99,14 @@ describe('HostRiskScore', () => { context ); }); + + it("doesn't render background-color when hideBackgroundColor is true", () => { + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('host-risk-score')).toHaveStyleRule('background-color', undefined); + }); }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx b/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx index 94f344b54036f..3f666cf396504 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/common/host_risk_score.tsx @@ -21,13 +21,14 @@ const HOST_RISK_SEVERITY_COLOUR = { Critical: euiLightVars.euiColorDanger, }; -const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity }>` - ${({ theme, $severity }) => css` +const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity; $hideBackgroundColor: boolean }>` + ${({ theme, $severity, $hideBackgroundColor }) => css` width: fit-content; padding-right: ${theme.eui.paddingSizes.s}; padding-left: ${theme.eui.paddingSizes.xs}; ${($severity === 'Critical' || $severity === 'High') && + !$hideBackgroundColor && css` background-color: ${transparentize(theme.eui.euiColorDanger, 0.2)}; border-radius: 999px; // pill shaped @@ -35,8 +36,16 @@ const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity }>` `} `; -export const HostRiskScore: React.FC<{ severity: HostRiskSeverity }> = ({ severity }) => ( - +export const HostRiskScore: React.FC<{ + severity: HostRiskSeverity; + hideBackgroundColor?: boolean; +}> = ({ severity, hideBackgroundColor = false }) => ( + {severity} diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.test.tsx new file mode 100644 index 0000000000000..09d0375cf7dff --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.test.tsx @@ -0,0 +1,33 @@ +/* + * 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 { render, fireEvent } from '@testing-library/react'; +import React from 'react'; +import { HostRiskInformation } from '.'; +import { TestProviders } from '../../../common/mock'; + +describe('Host Risk Flyout', () => { + it('renders', () => { + const { queryByTestId } = render(); + + expect(queryByTestId('open-risk-information-flyout')).toBeInTheDocument(); + }); + + it('opens and displays table with 5 rows', () => { + const NUMBER_OF_ROWS = 1 + 5; // 1 header row + 5 severity rows + const { getByTestId, queryByTestId, queryAllByRole } = render( + + + + ); + + fireEvent.click(getByTestId('open-risk-information-flyout')); + + expect(queryByTestId('risk-information-table')).toBeInTheDocument(); + expect(queryAllByRole('row')).toHaveLength(NUMBER_OF_ROWS); + }); +}); diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx new file mode 100644 index 0000000000000..2bb7fdd00a17d --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx @@ -0,0 +1,161 @@ +/* + * 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 { + useGeneratedHtmlId, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiText, + EuiTitle, + EuiLink, + EuiBasicTable, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutFooter, + EuiButton, + EuiSpacer, + EuiBasicTableColumn, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useState } from 'react'; +import { HostRiskSeverity } from '../../../../common/search_strategy'; +import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; +import { HostRiskScore } from '../common/host_risk_score'; +import * as i18n from './translations'; + +const tableColumns: Array> = [ + { + field: 'classification', + name: i18n.INFORMATION_CLASSIFICATION_HEADER, + render: (riskScore?: HostRiskSeverity) => { + if (riskScore != null) { + return ; + } + }, + }, + { + field: 'range', + name: i18n.INFORMATION_RISK_HEADER, + }, +]; + +interface TableItem { + range?: string; + classification: HostRiskSeverity; +} + +const tableItems: TableItem[] = [ + { classification: HostRiskSeverity.critical, range: '90 and above' }, + { classification: HostRiskSeverity.high, range: '70 - 90 ' }, + { classification: HostRiskSeverity.moderate, range: '40 - 70' }, + { classification: HostRiskSeverity.low, range: '20 - 40' }, + { classification: HostRiskSeverity.unknown, range: 'Less than 20' }, +]; + +export const HOST_RISK_INFO_BUTTON_CLASS = 'HostRiskInformation__button'; + +export const HostRiskInformation = () => { + const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); + const simpleFlyoutTitleId = useGeneratedHtmlId({ + prefix: 'HostRiskInformation', + }); + + let flyout; + + if (isFlyoutVisible) { + flyout = ( + setIsFlyoutVisible(false)} + aria-labelledby={simpleFlyoutTitleId} + size={450} + > + + +

+ +

+
+
+ + +

+ +

+

+ +

+
+ + + + + + + ), + }} + /> +
+ + + + + setIsFlyoutVisible(false)}> + + + + + +
+ ); + } + + return ( + <> + setIsFlyoutVisible(true)} + className={HOST_RISK_INFO_BUTTON_CLASS} + data-test-subj="open-risk-information-flyout" + /> + {flyout} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts new file mode 100644 index 0000000000000..8a8186525b604 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts @@ -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 { i18n } from '@kbn/i18n'; + +export const INFORMATION_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.informationAriaLabel', + { + defaultMessage: 'Information', + } +); + +export const INFORMATION_CLASSIFICATION_HEADER = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.classificationHeader', + { + defaultMessage: 'Classification', + } +); + +export const INFORMATION_RISK_HEADER = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.riskHeader', + { + defaultMessage: 'Host risk score range', + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx index d5f0b16fbb7b6..f882e12d211d3 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/risky_hosts/index.tsx @@ -17,7 +17,11 @@ import { import React from 'react'; import styled from 'styled-components'; import { euiLightVars } from '@kbn/ui-shared-deps-src/theme'; -import { InspectButtonContainer, InspectButton } from '../../../../common/components/inspect'; +import { + InspectButton, + BUTTON_CLASS as INPECT_BUTTON_CLASS, +} from '../../../../common/components/inspect'; + import { HostsKpiBaseComponentLoader } from '../common'; import * as i18n from './translations'; @@ -29,6 +33,8 @@ import { import { useInspectQuery } from '../../../../common/hooks/use_inspect_query'; import { useErrorToast } from '../../../../common/hooks/use_error_toast'; import { HostRiskScore } from '../../common/host_risk_score'; +import { HostRiskInformation, HOST_RISK_INFO_BUTTON_CLASS } from '../../host_risk_information'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; const QUERY_ID = 'hostsKpiRiskyHostsQuery'; @@ -63,7 +69,7 @@ const RiskyHostsComponent: React.FC<{ const totalCount = criticalRiskCount + hightlRiskCount; return ( - + @@ -72,7 +78,16 @@ const RiskyHostsComponent: React.FC<{ - {data?.inspect && } + + + + + {data?.inspect && ( + + + + )} + @@ -117,7 +132,7 @@ const RiskyHostsComponent: React.FC<{ - + ); }; From 9d368a75c6c899b5f435942f766988e41f0b903b Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Tue, 14 Dec 2021 10:22:25 +0100 Subject: [PATCH 3/5] Update snapshot test --- .../expandable_host.test.tsx.snap | 1863 +++++----- .../components/side_panel/index.test.tsx | 3002 +++++++++-------- 2 files changed, 2458 insertions(+), 2407 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap index 43efd8cd824e6..e00b89e8837bc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap @@ -123,999 +123,1016 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re narrowDateRange={[Function]} startDate="2020-07-07T08:20:18.966Z" > - -
+ - - -
- - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } - key="0" + - -
+ — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } + key="0" > - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } + - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } +
-
+ — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > - -
- Host ID -
-
- + — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > -
- - +
- — - - - - - -
- First seen -
- - -
- - + + +
- — - - -
-
- -
- Last seen -
-
- -
- - + + — + + +
+
+ +
- — - - - - -
- - -
-
- - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } - key="1" - > - -
+ + +
+ + + — + + +
+
+ +
+ Last seen +
+
+ +
+ + + — + + +
+
+ + + +
+
+
+ , + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } + key="1" > - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } + - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } +
-
, + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } > - -
- IP addresses -
-
- , + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } > -
- +
+ IP addresses +
+ + - - + - — - - - - - - -
- MAC addresses -
-
- -
- + + — + + + +
+
+ +
+ MAC addresses +
+
+ - - + - — - - - - - - -
- Platform -
-
- -
- + + — + + + +
+
+ +
+ Platform +
+
+ - - + - — - - - - - -
- - -
-
-
- , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } - key="2" - > - -
+ + — + + + + + + + + +
+
+
+ , + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } + key="2" > - , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } + - , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } +
-
, + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } > - -
- Operating system -
-
- , + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } > -
- - - + Operating system + + + +
+ - — - - - -
-
- -
- Family -
-
- -
- + + — + + + +
+
+ - - + Family + + + +
+ - — - - - -
-
- -
- Version -
-
- -
- + + — + + + +
+
+ +
+ Version +
+
+ - - + - — - - - - - - -
- Architecture -
-
- -
- + + — + + + +
+
+ +
+ Architecture +
+
+ - - + - — - - - - - -
- - -
-
-
- , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } - key="3" - > - -
+ + — + + + + + + + + +
+
+
+ , + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } + key="3" > - , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } + - , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } +
-
, + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } > - -
- Cloud provider -
-
- , + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } > -
- +
+ Cloud provider +
+ + - - + - — - - - - - - -
- Region -
-
- -
- + + — + + + +
+
+ - - + Region + + + +
+ - — - - - -
-
- -
- Instance ID -
-
- -
- + + — + + + +
+
+ - - + Instance ID + + + +
+ - — - - - -
-
- -
- Machine type -
-
- -
- + + — + + + +
+
+ +
+ Machine type +
+
+ - - + - — - - - - - -
- - -
-
-
- -
+
+
+ - - -
-
- - - +
+ + + +
+
-
-
-
-
- - -
-
-
-
-
+ + + + + +
+ + +
+ + diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx index 69b61c771390d..54ceb71132fd0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.test.tsx @@ -1153,999 +1153,1016 @@ describe('Details Panel Component', () => { narrowDateRange={[Function]} startDate="2020-07-07T08:20:18.966Z" > - -
+ - - -
- - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } - key="0" + - -
+ — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } + key="0" > - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } + - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } +
-
+ — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > - -
- Host ID -
-
- + — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > -
- - +
- — - - - - - -
- First seen -
- - -
- - + + +
- — - - -
-
- -
- Last seen -
-
- -
- - + + — + + +
+
+ +
- — - - - - -
- - -
-
- - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } - key="1" - > - -
+ + +
+ + + — + + +
+
+ +
+ Last seen +
+
+ +
+ + + — + + +
+
+ + + +
+
+
+ , + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } + key="1" > - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } + - , - "title": "IP addresses", - }, - Object { - "description": , - "title": "MAC addresses", - }, - Object { - "description": , - "title": "Platform", - }, - ] - } +
-
, + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } > - -
- IP addresses -
-
- , + "title": "IP addresses", + }, + Object { + "description": , + "title": "MAC addresses", + }, + Object { + "description": , + "title": "Platform", + }, + ] + } > -
- - - + IP addresses + + + +
+ - — - - - -
-
- -
- MAC addresses -
-
- -
- + + — + + + +
+
+ - - + MAC addresses + + + +
+ - — - - - -
-
- -
- Platform -
-
- -
- + + — + + + +
+
+ - - + Platform + + + +
+ - — - - - -
-
-
- - -
-
-
- , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } - key="2" - > - -
+ + — + + + + + + + + +
+
+
+ , + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } + key="2" > - , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } + - , - "title": "Operating system", - }, - Object { - "description": , - "title": "Family", - }, - Object { - "description": , - "title": "Version", - }, - Object { - "description": , - "title": "Architecture", - }, - ] - } +
-
, + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } > - -
- Operating system -
-
- , + "title": "Operating system", + }, + Object { + "description": , + "title": "Family", + }, + Object { + "description": , + "title": "Version", + }, + Object { + "description": , + "title": "Architecture", + }, + ] + } > -
- - - + Operating system + + + +
+ - — - - - -
-
- -
- Family -
-
- -
- + + — + + + +
+
+ - - + Family + + + +
+ - — - - - -
-
- -
- Version -
-
- -
- + + — + + + +
+
+ - - + Version + + + +
+ - — - - - -
-
- -
- Architecture -
-
- -
- + + — + + + +
+
+ - - + Architecture + + + +
+ - — - - - -
-
-
- - -
-
-
- , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } - key="3" - > - -
+ + — + + + + + + + + +
+
+
+ , + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } + key="3" > - , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } + - , - "title": "Cloud provider", - }, - Object { - "description": , - "title": "Region", - }, - Object { - "description": , - "title": "Instance ID", - }, - Object { - "description": , - "title": "Machine type", - }, - ] - } +
-
, + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } > - -
- Cloud provider -
-
- , + "title": "Cloud provider", + }, + Object { + "description": , + "title": "Region", + }, + Object { + "description": , + "title": "Instance ID", + }, + Object { + "description": , + "title": "Machine type", + }, + ] + } > -
- - - + Cloud provider + + + +
+ - — - - - -
-
- -
- Region -
-
- -
- + + — + + + +
+
+ - - + Region + + + +
+ - — - - - -
-
- -
- Instance ID -
-
- -
- + + — + + + +
+
+ - - + Instance ID + + + +
+ - — - - - -
-
- -
- Machine type -
-
- -
- + + — + + + +
+
+ - - + Machine type + + + +
+ - — - - - -
-
-
- - -
-
-
- -
+
+
+ - - -
-
- - - +
+ + + +
+
-
-
-
-
- - -
-
-
-
-
+ + + + + + + + + + +
@@ -2270,626 +2287,643 @@ describe('Details Panel Component', () => { startDate="2020-07-07T08:20:18.966Z" type="details" > - -
+ - - -
- - — - , - "title": "Location", - }, - Object { - "description": - — - , - "title": "Autonomous system", - }, - ] - } - key="0" + - -
+ — + , + "title": "Location", + }, + Object { + "description": + — + , + "title": "Autonomous system", + }, + ] + } + key="0" > - - — - , - "title": "Location", - }, - Object { - "description": - — - , - "title": "Autonomous system", - }, - ] - } + - - — - , - "title": "Location", - }, - Object { - "description": - — - , - "title": "Autonomous system", - }, - ] - } +
-
+ — + , + "title": "Location", + }, + Object { + "description": + — + , + "title": "Autonomous system", + }, + ] + } > - -
- Location -
-
- + — + , + "title": "Location", + }, + Object { + "description": + — + , + "title": "Autonomous system", + }, + ] + } > -
- - +
- — - - - - - -
- Autonomous system -
- - -
- - + + +
- — - - -
-
-
- - -
-
- - - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } - key="1" - > - -
+ + — + + + + + +
+ Autonomous system +
+
+ +
+ + + — + + +
+
+ + + +
+
+
+ + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } + key="1" > - - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } + - - — - , - "title": "First seen", - }, - Object { - "description": - — - , - "title": "Last seen", - }, - ] - } +
-
+ — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > - -
- First seen -
-
- + — + , + "title": "First seen", + }, + Object { + "description": + — + , + "title": "Last seen", + }, + ] + } > -
- - +
- — - - - - - -
- Last seen -
- - -
- - + + +
- — - - -
-
-
- - -
-
-
- - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "Host name", - }, - ] - } - key="2" - > - -
+ + — + + + + + +
+ Last seen +
+
+ +
+ + + — + + +
+
+ + + +
+
+
+ + — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "Host name", + }, + ] + } + key="2" > - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "Host name", - }, - ] - } + - - — - , - "title": "Host ID", - }, - Object { - "description": - — - , - "title": "Host name", - }, - ] - } +
-
+ — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "Host name", + }, + ] + } > - -
- Host ID -
-
- + — + , + "title": "Host ID", + }, + Object { + "description": + — + , + "title": "Host name", + }, + ] + } > -
- - +
- — - - - - - -
- Host name -
- - -
- - + + +
- — - - -
-
-
- - -
-
-
- - iana.org - , - "title": "WhoIs", - }, - Object { - "description": , - "title": "Reputation", - }, - ] - } - key="3" - > - -
+ + — + + + + + +
+ Host name +
+
+ +
+ + + — + + +
+
+ + + +
+
+
+ + iana.org + , + "title": "WhoIs", + }, + Object { + "description": , + "title": "Reputation", + }, + ] + } + key="3" > - - iana.org - , - "title": "WhoIs", - }, - Object { - "description": , - "title": "Reputation", - }, - ] - } + - - iana.org - , - "title": "WhoIs", - }, - Object { - "description": , - "title": "Reputation", - }, - ] - } +
-
+ iana.org + , + "title": "WhoIs", + }, + Object { + "description": , + "title": "Reputation", + }, + ] + } > - -
- WhoIs -
-
- + iana.org + , + "title": "WhoIs", + }, + Object { + "description": , + "title": "Reputation", + }, + ] + } > -
- +
+ WhoIs +
+ + - - - - - - iana.org - - - - - - - (opens in a new tab or window) - - - - - - - - - - - - -
- Reputation -
-
- -
- -
-
-
- - -
-
-
- -
+
+
+ - - -
-
- - - +
+ + + +
+
-
-
-
-
- - -
-
-
-
-
+ + + + + + + + + + +
From 3ef7933b35664af06b3a2b97e26619191d4b0280 Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Wed, 15 Dec 2021 12:45:07 +0100 Subject: [PATCH 4/5] Fix cypress test --- .../alerts_kpis/alerts_histogram_panel/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index 873b5d40184ef..51f8c0ac8dd30 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -263,8 +263,13 @@ export const AlertsHistogramPanel = memo( ); return ( - - + + Date: Mon, 20 Dec 2021 18:00:53 +0100 Subject: [PATCH 5/5] Code review improvements --- .../host_risk_information/index.tsx | 145 ++++++++---------- .../host_risk_information/translations.ts | 41 +++++ 2 files changed, 103 insertions(+), 83 deletions(-) diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx index 2bb7fdd00a17d..b4632466672e2 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/index.tsx @@ -24,7 +24,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { HostRiskSeverity } from '../../../../common/search_strategy'; import { RISKY_HOSTS_DOC_LINK } from '../../../overview/components/overview_risky_host_links/risky_hosts_disabled_module'; import { HostRiskScore } from '../common/host_risk_score'; @@ -52,97 +52,25 @@ interface TableItem { } const tableItems: TableItem[] = [ - { classification: HostRiskSeverity.critical, range: '90 and above' }, + { classification: HostRiskSeverity.critical, range: i18n.CRITICAL_RISK_DESCRIPTION }, { classification: HostRiskSeverity.high, range: '70 - 90 ' }, { classification: HostRiskSeverity.moderate, range: '40 - 70' }, { classification: HostRiskSeverity.low, range: '20 - 40' }, - { classification: HostRiskSeverity.unknown, range: 'Less than 20' }, + { classification: HostRiskSeverity.unknown, range: i18n.UNKNOWN_RISK_DESCRIPTION }, ]; export const HOST_RISK_INFO_BUTTON_CLASS = 'HostRiskInformation__button'; export const HostRiskInformation = () => { const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); - const simpleFlyoutTitleId = useGeneratedHtmlId({ - prefix: 'HostRiskInformation', - }); - let flyout; + const handleOnClose = useCallback(() => { + setIsFlyoutVisible(false); + }, []); - if (isFlyoutVisible) { - flyout = ( - setIsFlyoutVisible(false)} - aria-labelledby={simpleFlyoutTitleId} - size={450} - > - - -

- -

-
-
- - -

- -

-

- -

-
- - - - - - - ), - }} - /> -
- - - - - setIsFlyoutVisible(false)}> - - - - - -
- ); - } + const handleOnOpen = useCallback(() => { + setIsFlyoutVisible(true); + }, []); return ( <> @@ -151,11 +79,62 @@ export const HostRiskInformation = () => { iconSize="m" iconType="iInCircle" aria-label={i18n.INFORMATION_ARIA_LABEL} - onClick={() => setIsFlyoutVisible(true)} + onClick={handleOnOpen} className={HOST_RISK_INFO_BUTTON_CLASS} data-test-subj="open-risk-information-flyout" /> - {flyout} + {isFlyoutVisible && } ); }; + +const HostRiskInformationFlyout = ({ handleOnClose }: { handleOnClose: () => void }) => { + const simpleFlyoutTitleId = useGeneratedHtmlId({ + prefix: 'HostRiskInformation', + }); + + return ( + + + +

{i18n.TITLE}

+
+
+ + +

{i18n.INTRODUCTION}

+

{i18n.EXPLANATION_MESSAGE}

+
+ + + + + + + ), + }} + /> +
+ + + + + {i18n.CLOSE_BUTTON_LTEXT} + + + +
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts index 8a8186525b604..244c7b458b206 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/host_risk_information/translations.ts @@ -27,3 +27,44 @@ export const INFORMATION_RISK_HEADER = i18n.translate( defaultMessage: 'Host risk score range', } ); + +export const UNKNOWN_RISK_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.unknownRiskDescription', + { + defaultMessage: 'Less than 20', + } +); + +export const CRITICAL_RISK_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.criticalRiskDescription', + { + defaultMessage: '90 and above', + } +); + +export const TITLE = i18n.translate('xpack.securitySolution.hosts.hostRiskInformation.title', { + defaultMessage: 'How is host risk calculated?', +}); + +export const INTRODUCTION = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.introduction', + { + defaultMessage: + 'The Host Risk Score capability surfaces risky hosts from within your environment.', + } +); + +export const EXPLANATION_MESSAGE = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.explanation', + { + defaultMessage: + 'This feature utilizes a transform, with a scripted metric aggregation to calculate host risk scores based on detection rule alerts with an "open" status, within a 5 day time window. The transform runs hourly to keep the score updated as new detection rule alerts stream in.', + } +); + +export const CLOSE_BUTTON_LTEXT = i18n.translate( + 'xpack.securitySolution.hosts.hostRiskInformation.closeBtn', + { + defaultMessage: 'Close', + } +);