diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 89e4a2c008727..684f2ffd3b548 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1283,7 +1283,11 @@ x-pack/plugins/security_solution/server/lib/telemetry/ @elastic/security-data-an
## Security Solution sub teams - security-engineering-productivity
x-pack/test/security_solution_cypress/cypress/README.md @elastic/security-engineering-productivity
-x-pack/test/security_solution_cypress @elastic/security-engineering-productivity
+x-pack/test/security_solution_cypress/es_archives @elastic/security-engineering-productivity
+x-pack/test/security_solution_cypress/cli_config.ts @elastic/security-engineering-productivity
+x-pack/test/security_solution_cypress/config.ts @elastic/security-engineering-productivity
+x-pack/test/security_solution_cypress/runner.ts @elastic/security-engineering-productivity
+x-pack/test/security_solution_cypress/serverless_config.ts @elastic/security-engineering-productivity
## Security Solution sub teams - adaptive-workload-protection
x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/sec-cloudnative-integrations
diff --git a/x-pack/plugins/cases/public/components/description/index.test.tsx b/x-pack/plugins/cases/public/components/description/index.test.tsx
index f519c7af3eb19..e077c0896d67d 100644
--- a/x-pack/plugins/cases/public/components/description/index.test.tsx
+++ b/x-pack/plugins/cases/public/components/description/index.test.tsx
@@ -27,7 +27,13 @@ const defaultProps = {
isLoadingDescription: false,
};
-describe('Description', () => {
+// FLAKY: https://github.com/elastic/kibana/issues/164049
+// FLAKY: https://github.com/elastic/kibana/issues/164048
+// FLAKY: https://github.com/elastic/kibana/issues/164047
+// FLAKY: https://github.com/elastic/kibana/issues/164046
+// FLAKY: https://github.com/elastic/kibana/issues/164045
+// FLAKY: https://github.com/elastic/kibana/issues/164044
+describe.skip('Description', () => {
const onUpdateField = jest.fn();
let appMockRender: AppMockRenderer;
@@ -198,7 +204,8 @@ describe('Description', () => {
expect(screen.queryByTestId('description-edit-icon')).not.toBeInTheDocument();
});
- describe('draft message', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/164050
+ describe.skip('draft message', () => {
const draftStorageKey = `cases.testAppId.basic-case-id.description.markdownEditor`;
beforeEach(() => {
diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details_alerts_table.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details_alerts_table.tsx
index 79ce5e8f4bf0d..28d05d3a08d70 100644
--- a/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details_alerts_table.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details_alerts_table.tsx
@@ -10,6 +10,7 @@ import { type Criteria, EuiBasicTable, formatDate, EuiEmptyPrompt } from '@elast
import { Severity } from '@kbn/securitysolution-io-ts-alerting-types';
import { isRight } from 'fp-ts/lib/Either';
+import { ALERT_REASON, ALERT_RULE_NAME } from '@kbn/rule-data-utils';
import { SeverityBadge } from '../../../detections/components/rules/severity_badge';
import { usePaginatedAlerts } from '../hooks/use_paginated_alerts';
import { ERROR_MESSAGE, ERROR_TITLE } from '../../shared/translations';
@@ -26,12 +27,12 @@ export const columns = [
render: (value: string) => formatDate(value, TIMESTAMP_DATE_FORMAT),
},
{
- field: 'kibana.alert.rule.name',
+ field: ALERT_RULE_NAME,
name: i18n.CORRELATIONS_RULE_COLUMN_TITLE,
truncateText: true,
},
{
- field: 'kibana.alert.reason',
+ field: ALERT_REASON,
name: i18n.CORRELATIONS_REASON_COLUMN_TITLE,
truncateText: true,
},
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.test.tsx b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.test.tsx
new file mode 100644
index 0000000000000..30076fd3ca1d2
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.test.tsx
@@ -0,0 +1,47 @@
+/*
+ * 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 { render } from '@testing-library/react';
+import { PreviewPanelContext } from '../context';
+import { mockContextValue } from '../mocks/mock_preview_panel_context';
+import { ALERT_REASON_PREVIEW_BODY_TEST_ID } from './test_ids';
+import { AlertReasonPreview } from './alert_reason_preview';
+import { ThemeProvider } from 'styled-components';
+import { getMockTheme } from '../../../common/lib/kibana/kibana_react.mock';
+
+const mockTheme = getMockTheme({ eui: { euiFontSizeXS: '' } });
+
+const panelContextValue = {
+ ...mockContextValue,
+};
+
+describe('', () => {
+ it('should render alert reason preview', () => {
+ const { getByTestId } = render(
+
+
+
+
+
+ );
+ expect(getByTestId(ALERT_REASON_PREVIEW_BODY_TEST_ID)).toBeInTheDocument();
+ });
+
+ it('should render null is dataAsNestedObject is null', () => {
+ const contextValue = {
+ ...mockContextValue,
+ dataAsNestedObject: null,
+ };
+ const { queryByTestId } = render(
+
+
+
+ );
+ expect(queryByTestId(ALERT_REASON_PREVIEW_BODY_TEST_ID)).not.toBeInTheDocument();
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx
new file mode 100644
index 0000000000000..4fd912cbfbeec
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/preview/components/alert_reason_preview.tsx
@@ -0,0 +1,50 @@
+/*
+ * 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, { useMemo } from 'react';
+import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { ALERT_REASON_TITLE } from './translations';
+import { ALERT_REASON_PREVIEW_BODY_TEST_ID } from './test_ids';
+import { usePreviewPanelContext } from '../context';
+import { getRowRenderer } from '../../../timelines/components/timeline/body/renderers/get_row_renderer';
+import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
+
+/**
+ * Alert reason renderer on a preview panel on top of the right section of expandable flyout
+ */
+export const AlertReasonPreview: React.FC = () => {
+ const { dataAsNestedObject } = usePreviewPanelContext();
+
+ const renderer = useMemo(
+ () =>
+ dataAsNestedObject != null
+ ? getRowRenderer({ data: dataAsNestedObject, rowRenderers: defaultRowRenderers })
+ : null,
+ [dataAsNestedObject]
+ );
+
+ if (!dataAsNestedObject || !renderer) {
+ return null;
+ }
+
+ return (
+
+
+ {ALERT_REASON_TITLE}
+
+
+ {renderer.renderRow({
+ contextId: 'event-details',
+ data: dataAsNestedObject,
+ isDraggable: false,
+ scopeId: 'global',
+ })}
+
+ );
+};
+
+AlertReasonPreview.displayName = 'AlertReasonPreview';
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts
index 1c27fd7472fca..764ec90fd9bdf 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts
+++ b/x-pack/plugins/security_solution/public/flyout/preview/components/test_ids.ts
@@ -38,3 +38,5 @@ export const RULE_PREVIEW_LOADING_TEST_ID =
'securitySolutionDocumentDetailsFlyoutRulePreviewLoadingSpinner';
export const RULE_PREVIEW_FOOTER_TEST_ID = 'securitySolutionDocumentDetailsFlyoutRulePreviewFooter';
export const RULE_PREVIEW_NAVIGATE_TO_RULE_TEST_ID = 'goToRuleDetails';
+export const ALERT_REASON_PREVIEW_BODY_TEST_ID =
+ 'securitySolutionDocumentDetailsFlyoutAlertReasonPreviewBody';
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/preview/components/translations.ts
index e3f3b1fd095fb..36bfdd33ea2ca 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/components/translations.ts
+++ b/x-pack/plugins/security_solution/public/flyout/preview/components/translations.ts
@@ -31,3 +31,8 @@ export const RULE_PREVIEW_ACTIONS_TEXT = i18n.translate(
'xpack.securitySolution.flyout.documentDetails.rulePreviewActionsSectionText',
{ defaultMessage: 'Actions' }
);
+
+export const ALERT_REASON_TITLE = i18n.translate(
+ 'xpack.securitySolution.flyout.documentDetails.alertReasonTitle',
+ { defaultMessage: 'Alert reason' }
+);
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/context.tsx b/x-pack/plugins/security_solution/public/flyout/preview/context.tsx
index 521303635c25d..005ef1dcdb258 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/context.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/preview/context.tsx
@@ -7,11 +7,15 @@
import React, { createContext, useContext, useMemo } from 'react';
import type { DataViewBase } from '@kbn/es-query';
+import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
+import { SecurityPageName } from '@kbn/security-solution-navigation';
import type { PreviewPanelProps } from '.';
-import { useRouteSpy } from '../../common/utils/route/use_route_spy';
-import { SecurityPageName } from '../../../common/constants';
import { SourcererScopeName } from '../../common/store/sourcerer/model';
import { useSourcererDataView } from '../../common/containers/sourcerer';
+import { useTimelineEventsDetails } from '../../timelines/containers/details';
+import { getAlertIndexAlias } from '../../timelines/components/side_panel/event_details/helpers';
+import { useSpaceId } from '../../common/hooks/use_space_id';
+import { useRouteSpy } from '../../common/utils/route/use_route_spy';
export interface PreviewPanelContext {
/**
@@ -34,6 +38,10 @@ export interface PreviewPanelContext {
* Index pattern for rule details
*/
indexPattern: DataViewBase;
+ /**
+ * An object with top level fields from the ECS object
+ */
+ dataAsNestedObject: Ecs | null;
}
export const PreviewPanelContext = createContext(undefined);
@@ -52,12 +60,21 @@ export const PreviewPanelProvider = ({
ruleId,
children,
}: PreviewPanelProviderProps) => {
+ const currentSpaceId = useSpaceId();
+ const eventIndex = indexName ? getAlertIndexAlias(indexName, currentSpaceId) ?? indexName : '';
const [{ pageName }] = useRouteSpy();
const sourcererScope =
pageName === SecurityPageName.detections
? SourcererScopeName.detections
: SourcererScopeName.default;
const sourcererDataView = useSourcererDataView(sourcererScope);
+ const [_, __, ___, dataAsNestedObject] = useTimelineEventsDetails({
+ indexName: eventIndex,
+ eventId: id ?? '',
+ runtimeMappings: sourcererDataView.runtimeMappings,
+ skip: !id,
+ });
+
const contextValue = useMemo(
() =>
id && indexName && scopeId
@@ -67,9 +84,10 @@ export const PreviewPanelProvider = ({
scopeId,
ruleId: ruleId ?? '',
indexPattern: sourcererDataView.indexPattern,
+ dataAsNestedObject,
}
: undefined,
- [id, indexName, scopeId, ruleId, sourcererDataView.indexPattern]
+ [id, indexName, scopeId, ruleId, sourcererDataView.indexPattern, dataAsNestedObject]
);
return (
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/index.tsx b/x-pack/plugins/security_solution/public/flyout/preview/index.tsx
index 9bfefb8f257fd..db9f7bb5ba58a 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/index.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/preview/index.tsx
@@ -10,8 +10,9 @@ import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { panels } from './panels';
-export type PreviewPanelPaths = 'rule-preview';
+export type PreviewPanelPaths = 'rule-preview' | 'alert-reason-preview';
export const RulePreviewPanel: PreviewPanelPaths = 'rule-preview';
+export const AlertReasonPreviewPanel: PreviewPanelPaths = 'alert-reason-preview';
export const PreviewPanelKey: PreviewPanelProps['key'] = 'document-details-preview';
export interface PreviewPanelProps extends FlyoutPanelProps {
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/mocks/mock_preview_panel_context.ts b/x-pack/plugins/security_solution/public/flyout/preview/mocks/mock_preview_panel_context.ts
index 4e9f9cc43d8ba..cdfe8ab5307ba 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/mocks/mock_preview_panel_context.ts
+++ b/x-pack/plugins/security_solution/public/flyout/preview/mocks/mock_preview_panel_context.ts
@@ -5,6 +5,8 @@
* 2.0.
*/
+import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
+import { mockDataAsNestedObject } from '../../shared/mocks/mock_context';
import type { PreviewPanelContext } from '../context';
/**
@@ -16,4 +18,5 @@ export const mockContextValue: PreviewPanelContext = {
scopeId: 'scopeId',
ruleId: '',
indexPattern: { fields: [], title: 'test index' },
+ dataAsNestedObject: mockDataAsNestedObject as unknown as Ecs,
};
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/panels.tsx b/x-pack/plugins/security_solution/public/flyout/preview/panels.tsx
index b9aee26bdd577..e585c58945d9c 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/panels.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/preview/panels.tsx
@@ -6,8 +6,9 @@
*/
import React from 'react';
+import { AlertReasonPreview } from './components/alert_reason_preview';
import type { PreviewPanelPaths } from '.';
-import { RULE_PREVIEW } from './translations';
+import { ALERT_REASON_PREVIEW, RULE_PREVIEW } from './translations';
import { RulePreview } from './components/rule_preview';
import { RulePreviewFooter } from './components/rule_preview_footer';
@@ -40,4 +41,9 @@ export const panels: PreviewPanelType = [
content: ,
footer: ,
},
+ {
+ id: 'alert-reason-preview',
+ name: ALERT_REASON_PREVIEW,
+ content: ,
+ },
];
diff --git a/x-pack/plugins/security_solution/public/flyout/preview/translations.ts b/x-pack/plugins/security_solution/public/flyout/preview/translations.ts
index 1db37fbb49bb8..cf359e7900cea 100644
--- a/x-pack/plugins/security_solution/public/flyout/preview/translations.ts
+++ b/x-pack/plugins/security_solution/public/flyout/preview/translations.ts
@@ -11,3 +11,8 @@ export const RULE_PREVIEW = i18n.translate(
'xpack.securitySolution.flyout.documentDetails.rulePreviewPanel',
{ defaultMessage: 'Rule preview' }
);
+
+export const ALERT_REASON_PREVIEW = i18n.translate(
+ 'xpack.securitySolution.flyout.documentDetails.alertReasonPreviewPanel',
+ { defaultMessage: 'Alert reason preview' }
+);
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx
deleted file mode 100644
index 13fa592c4f7d6..0000000000000
--- a/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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 { Story } from '@storybook/react';
-import { Description } from './description';
-import { RightPanelContext } from '../context';
-
-const ruleUuid = {
- category: 'kibana',
- field: 'kibana.alert.rule.uuid',
- values: ['123'],
- originalValue: ['123'],
- isObjectArray: false,
-};
-const ruleDescription = {
- category: 'kibana',
- field: 'kibana.alert.rule.description',
- values: [
- `This is a very long description of the rule. In theory. this description is long enough that it should be cut off when displayed in collapsed mode. If it isn't then there is a problem`,
- ],
- originalValue: ['description'],
- isObjectArray: false,
-};
-
-export default {
- component: Description,
- title: 'Flyout/Description',
-};
-
-const wrapper = (children: React.ReactNode, panelContextValue: RightPanelContext) => (
-
-
- {children}
-
-
-);
-export const Rule: Story = () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [ruleUuid, ruleDescription],
- } as unknown as RightPanelContext;
-
- return wrapper(, panelContextValue);
-};
-
-export const Document: Story = () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [
- {
- category: 'kibana',
- field: 'kibana.alert.rule.description',
- values: ['This is a description for the document.'],
- originalValue: ['description'],
- isObjectArray: false,
- },
- ],
- } as unknown as RightPanelContext;
-
- return wrapper(, panelContextValue);
-};
-
-export const EmptyDescription: Story = () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [
- ruleUuid,
- {
- category: 'kibana',
- field: 'kibana.alert.rule.description',
- values: [''],
- originalValue: ['description'],
- isObjectArray: false,
- },
- ],
- } as unknown as RightPanelContext;
-
- return wrapper(, panelContextValue);
-};
-
-export const Empty: Story = () => {
- const panelContextValue = {} as unknown as RightPanelContext;
-
- return wrapper(, panelContextValue);
-};
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/description.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/description.test.tsx
index 1cf4823d79358..f80d7c1939661 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/components/description.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/right/components/description.test.tsx
@@ -8,14 +8,17 @@
import React from 'react';
import { render } from '@testing-library/react';
import { DESCRIPTION_TITLE_TEST_ID, RULE_SUMMARY_BUTTON_TEST_ID } from './test_ids';
-import { DOCUMENT_DESCRIPTION_TITLE, RULE_DESCRIPTION_TITLE } from './translations';
+import {
+ DOCUMENT_DESCRIPTION_TITLE,
+ PREVIEW_RULE_DETAILS,
+ RULE_DESCRIPTION_TITLE,
+} from './translations';
import { Description } from './description';
-import { TestProviders } from '../../../common/mock';
import { RightPanelContext } from '../context';
-import { ThemeProvider } from 'styled-components';
-import { getMockTheme } from '../../../common/lib/kibana/kibana_react.mock';
-
-const mockTheme = getMockTheme({ eui: { euiColorMediumShade: '#ece' } });
+import { mockGetFieldsData } from '../mocks/mock_context';
+import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
+import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
+import { PreviewPanelKey } from '../../preview';
const ruleUuid = {
category: 'kibana',
@@ -41,23 +44,32 @@ const ruleName = {
isObjectArray: false,
};
-jest.mock('../../../common/lib/kibana');
-jest.mock('../../../common/components/link_to');
+const flyoutContextValue = {
+ openPreviewPanel: jest.fn(),
+} as unknown as ExpandableFlyoutContext;
+
+const panelContextValue = (dataFormattedForFieldBrowser: TimelineEventsDetailsItem[] | null) =>
+ ({
+ eventId: 'event id',
+ indexName: 'indexName',
+ scopeId: 'scopeId',
+ dataFormattedForFieldBrowser,
+ getFieldsData: mockGetFieldsData,
+ } as unknown as RightPanelContext);
+
+const renderDescription = (panelContext: RightPanelContext) =>
+ render(
+
+
+
+
+
+ );
describe('', () => {
it('should render the component', () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [ruleUuid, ruleDescription, ruleName],
- } as unknown as RightPanelContext;
-
- const { getByTestId } = render(
-
-
-
-
-
-
-
+ const { getByTestId } = renderDescription(
+ panelContextValue([ruleUuid, ruleDescription, ruleName])
);
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toBeInTheDocument();
@@ -66,18 +78,8 @@ describe('', () => {
});
it('should not render rule preview button if rule name is not available', () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [ruleUuid, ruleDescription],
- } as unknown as RightPanelContext;
-
- const { getByTestId, queryByTestId } = render(
-
-
-
-
-
-
-
+ const { getByTestId, queryByTestId } = renderDescription(
+ panelContextValue([ruleUuid, ruleDescription])
);
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toBeInTheDocument();
@@ -86,21 +88,44 @@ describe('', () => {
});
it('should render document title if document is not an alert', () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [ruleDescription],
- } as unknown as RightPanelContext;
-
- const { getByTestId } = render(
-
-
-
-
-
-
-
- );
+ const { getByTestId } = renderDescription(panelContextValue([ruleDescription]));
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(DESCRIPTION_TITLE_TEST_ID)).toHaveTextContent(DOCUMENT_DESCRIPTION_TITLE);
});
+
+ it('should render null if dataFormattedForFieldBrowser is null', () => {
+ const panelContext = {
+ ...panelContextValue([ruleUuid, ruleDescription, ruleName]),
+ dataFormattedForFieldBrowser: null,
+ } as unknown as RightPanelContext;
+
+ const { container } = renderDescription(panelContext);
+
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it('should open preview panel when clicking on button', () => {
+ const panelContext = panelContextValue([ruleUuid, ruleDescription, ruleName]);
+
+ const { getByTestId } = renderDescription(panelContext);
+
+ getByTestId(RULE_SUMMARY_BUTTON_TEST_ID).click();
+
+ expect(flyoutContextValue.openPreviewPanel).toHaveBeenCalledWith({
+ id: PreviewPanelKey,
+ path: { tab: 'rule-preview' },
+ params: {
+ id: panelContext.eventId,
+ indexName: panelContext.indexName,
+ scopeId: panelContext.scopeId,
+ banner: {
+ title: PREVIEW_RULE_DETAILS,
+ backgroundColor: 'warning',
+ textColor: 'warning',
+ },
+ ruleId: ruleUuid.values[0],
+ },
+ });
+ });
});
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.stories.tsx
deleted file mode 100644
index 68cb0b3a35e31..0000000000000
--- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.stories.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 type { Story } from '@storybook/react';
-import { StorybookProviders } from '../../../common/mock/storybook_providers';
-import { Reason } from './reason';
-import { RightPanelContext } from '../context';
-import { mockDataAsNestedObject, mockDataFormattedForFieldBrowser } from '../mocks/mock_context';
-
-export default {
- component: Reason,
- title: 'Flyout/Reason',
-};
-
-export const Default: Story = () => {
- const panelContextValue = {
- dataAsNestedObject: mockDataAsNestedObject,
- dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
- } as unknown as RightPanelContext;
-
- return (
-
-
-
-
-
- );
-};
-
-export const Empty: Story = () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: {},
- } as unknown as RightPanelContext;
-
- return (
-
-
-
- );
-};
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx
index b7050d1df0fa0..3ec854cfbd815 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx
@@ -7,70 +7,85 @@
import React from 'react';
import { render } from '@testing-library/react';
-import { REASON_TITLE_TEST_ID } from './test_ids';
+import {
+ REASON_DETAILS_PREVIEW_BUTTON_TEST_ID,
+ REASON_DETAILS_TEST_ID,
+ REASON_TITLE_TEST_ID,
+} from './test_ids';
import { Reason } from './reason';
import { RightPanelContext } from '../context';
-import { mockDataAsNestedObject, mockDataFormattedForFieldBrowser } from '../mocks/mock_context';
-import { euiDarkVars } from '@kbn/ui-theme';
-import { ThemeProvider } from 'styled-components';
+import { mockDataFormattedForFieldBrowser, mockGetFieldsData } from '../mocks/mock_context';
+import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
+import { PreviewPanelKey } from '../../preview';
+import { PREVIEW_ALERT_REASON_DETAILS } from './translations';
-describe('', () => {
- it('should render the component', () => {
- const panelContextValue = {
- dataAsNestedObject: mockDataAsNestedObject,
- dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
- } as unknown as RightPanelContext;
+const flyoutContextValue = {
+ openPreviewPanel: jest.fn(),
+} as unknown as ExpandableFlyoutContext;
- const { getByTestId } = render(
- ({ eui: euiDarkVars, darkMode: true })}>
-
-
-
-
- );
+const panelContextValue = {
+ eventId: 'event id',
+ indexName: 'indexName',
+ scopeId: 'scopeId',
+ dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
+ getFieldsData: mockGetFieldsData,
+} as unknown as RightPanelContext;
+const renderReason = (panelContext: RightPanelContext = panelContextValue) =>
+ render(
+
+
+
+
+
+ );
+
+describe('', () => {
+ it('should render the component', () => {
+ const { getByTestId } = renderReason();
expect(getByTestId(REASON_TITLE_TEST_ID)).toBeInTheDocument();
});
it('should render null if dataFormattedForFieldBrowser is null', () => {
- const panelContextValue = {
- dataAsNestedObject: {},
+ const panelContext = {
+ ...panelContextValue,
+ dataFormattedForFieldBrowser: null,
} as unknown as RightPanelContext;
- const { container } = render(
-
-
-
- );
+ const { container } = renderReason(panelContext);
expect(container).toBeEmptyDOMElement();
});
- it('should render null if dataAsNestedObject is null', () => {
- const panelContextValue = {
- dataFormattedForFieldBrowser: [],
+ it('should render no reason if the field is null', () => {
+ const panelContext = {
+ ...panelContextValue,
+ getFieldsData: () => {},
} as unknown as RightPanelContext;
- const { container } = render(
-
-
-
- );
+ const { getByTestId } = renderReason(panelContext);
- expect(container).toBeEmptyDOMElement();
+ expect(getByTestId(REASON_DETAILS_TEST_ID)).toBeEmptyDOMElement();
});
- it('should render null if renderer is null', () => {
- const panelContextValue = {
- dataAsNestedObject: {},
- dataFormattedForFieldBrowser: [],
- } as unknown as RightPanelContext;
- const { container } = render(
-
-
-
- );
+ it('should open preview panel when clicking on button', () => {
+ const { getByTestId } = renderReason();
- expect(container).toBeEmptyDOMElement();
+ getByTestId(REASON_DETAILS_PREVIEW_BUTTON_TEST_ID).click();
+
+ expect(flyoutContextValue.openPreviewPanel).toHaveBeenCalledWith({
+ id: PreviewPanelKey,
+ path: { tab: 'alert-reason-preview' },
+ params: {
+ id: panelContextValue.eventId,
+ indexName: panelContextValue.indexName,
+ scopeId: panelContextValue.scopeId,
+ banner: {
+ title: PREVIEW_ALERT_REASON_DETAILS,
+ backgroundColor: 'warning',
+ textColor: 'warning',
+ },
+ },
+ });
});
});
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.tsx
index b6633ac42c46b..b356809917973 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/right/components/reason.tsx
@@ -6,31 +6,71 @@
*/
import type { FC } from 'react';
-import React, { useMemo } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
-import { REASON_DETAILS_TEST_ID, REASON_TITLE_TEST_ID } from './test_ids';
-import { ALERT_REASON_TITLE, DOCUMENT_REASON_TITLE } from './translations';
+import React, { useCallback, useMemo } from 'react';
+import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
+import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
+import { ALERT_REASON } from '@kbn/rule-data-utils';
+import { getField } from '../../shared/utils';
+import { AlertReasonPreviewPanel, PreviewPanelKey } from '../../preview';
+import {
+ REASON_DETAILS_PREVIEW_BUTTON_TEST_ID,
+ REASON_DETAILS_TEST_ID,
+ REASON_TITLE_TEST_ID,
+} from './test_ids';
+import {
+ ALERT_REASON_DETAILS_TEXT,
+ ALERT_REASON_TITLE,
+ DOCUMENT_REASON_TITLE,
+ PREVIEW_ALERT_REASON_DETAILS,
+} from './translations';
import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
-import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
-import { getRowRenderer } from '../../../timelines/components/timeline/body/renderers/get_row_renderer';
import { useRightPanelContext } from '../context';
/**
* Displays the information provided by the rowRenderer. Supports multiple types of documents.
*/
export const Reason: FC = () => {
- const { dataAsNestedObject, dataFormattedForFieldBrowser } = useRightPanelContext();
+ const { eventId, indexName, scopeId, dataFormattedForFieldBrowser, getFieldsData } =
+ useRightPanelContext();
const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
+ const alertReason = getField(getFieldsData(ALERT_REASON));
- const renderer = useMemo(
- () =>
- dataAsNestedObject != null
- ? getRowRenderer({ data: dataAsNestedObject, rowRenderers: defaultRowRenderers })
- : null,
- [dataAsNestedObject]
+ const { openPreviewPanel } = useExpandableFlyoutContext();
+ const openRulePreview = useCallback(() => {
+ openPreviewPanel({
+ id: PreviewPanelKey,
+ path: { tab: AlertReasonPreviewPanel },
+ params: {
+ id: eventId,
+ indexName,
+ scopeId,
+ banner: {
+ title: PREVIEW_ALERT_REASON_DETAILS,
+ backgroundColor: 'warning',
+ textColor: 'warning',
+ },
+ },
+ });
+ }, [eventId, openPreviewPanel, indexName, scopeId]);
+
+ const viewPreview = useMemo(
+ () => (
+
+
+ {ALERT_REASON_DETAILS_TEXT}
+
+
+ ),
+ [openRulePreview]
);
- if (!dataFormattedForFieldBrowser || !dataAsNestedObject || !renderer) {
+ if (!dataFormattedForFieldBrowser) {
return null;
}
@@ -38,17 +78,21 @@ export const Reason: FC = () => {
- {isAlert ? ALERT_REASON_TITLE : DOCUMENT_REASON_TITLE}
+
+ {isAlert ? (
+
+
+ {ALERT_REASON_TITLE}
+
+ {viewPreview}
+
+ ) : (
+ DOCUMENT_REASON_TITLE
+ )}
+
-
- {renderer.renderRow({
- contextId: 'event-details',
- data: dataAsNestedObject,
- isDraggable: false,
- scopeId: 'global',
- })}
-
+ {alertReason}
);
};
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts
index 9e8b112851be6..7d41fe13f9fca 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts
+++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts
@@ -46,6 +46,8 @@ export const DESCRIPTION_DETAILS_TEST_ID =
'securitySolutionDocumentDetailsFlyoutDescriptionDetails';
export const REASON_TITLE_TEST_ID = 'securitySolutionDocumentDetailsFlyoutReasonTitle';
export const REASON_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutReasonDetails';
+export const REASON_DETAILS_PREVIEW_BUTTON_TEST_ID =
+ 'securitySolutionDocumentDetailsFlyoutReasonDetailsPreviewButton';
export const MITRE_ATTACK_TITLE_TEST_ID = 'securitySolutionAlertDetailsFlyoutMitreAttackTitle';
export const MITRE_ATTACK_DETAILS_TEST_ID = 'securitySolutionAlertDetailsFlyoutMitreAttackDetails';
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts
index f32e9abb1d48f..a411b0f44054e 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts
+++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts
@@ -45,6 +45,13 @@ export const RULE_SUMMARY_TEXT = i18n.translate(
}
);
+export const ALERT_REASON_DETAILS_TEXT = i18n.translate(
+ 'xpack.securitySolution.flyout.documentDetails.alertReasonDetailsText',
+ {
+ defaultMessage: 'Show full reason',
+ }
+);
+
/* About section */
export const ABOUT_TITLE = i18n.translate(
@@ -66,6 +73,11 @@ export const PREVIEW_RULE_DETAILS = i18n.translate(
{ defaultMessage: 'Preview rule details' }
);
+export const PREVIEW_ALERT_REASON_DETAILS = i18n.translate(
+ 'xpack.securitySolution.flyout.documentDetails.previewAlertReasonDetailsText',
+ { defaultMessage: 'Preview alert reason' }
+);
+
export const DOCUMENT_DESCRIPTION_TITLE = i18n.translate(
'xpack.securitySolution.flyout.documentDetails.documentDescriptionTitle',
{
diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_process_data.ts b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_process_data.ts
index 72ca71badaf07..ac98ddd1df2b2 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_process_data.ts
+++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_process_data.ts
@@ -6,6 +6,7 @@
*/
import { useMemo } from 'react';
+import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils';
import type { GetFieldsData } from '../../../common/hooks/use_get_fields_data';
import { getField } from '../../shared/utils';
import { useRightPanelContext } from '../context';
@@ -14,8 +15,6 @@ const FIELD_USER_NAME = 'process.entry_leader.user.name' as const;
const FIELD_USER_ID = 'process.entry_leader.user.id' as const;
const FIELD_PROCESS_NAME = 'process.entry_leader.name' as const;
const FIELD_START_AT = 'process.entry_leader.start' as const;
-const FIELD_RULE_NAME = 'kibana.alert.rule.name' as const;
-const FIELD_RULE_ID = 'kibana.alert.rule.uuid' as const;
const FIELD_WORKING_DIRECTORY = 'process.group_leader.working_directory' as const;
const FIELD_COMMAND = 'process.command_line' as const;
@@ -48,8 +47,8 @@ export const useProcessData = () => {
userName: getUserDisplayName(getFieldsData),
processName: getField(getFieldsData(FIELD_PROCESS_NAME)),
startAt: getField(getFieldsData(FIELD_START_AT)),
- ruleName: getField(getFieldsData(FIELD_RULE_NAME)),
- ruleId: getField(getFieldsData(FIELD_RULE_ID)),
+ ruleName: getField(getFieldsData(ALERT_RULE_NAME)),
+ ruleId: getField(getFieldsData(ALERT_RULE_UUID)),
workdir: getField(getFieldsData(FIELD_WORKING_DIRECTORY)),
command: getField(getFieldsData(FIELD_COMMAND)),
}),
diff --git a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts
index 6ae872acd45ac..cdc058569d9d3 100644
--- a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts
+++ b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { ALERT_RISK_SCORE, ALERT_SEVERITY } from '@kbn/rule-data-utils';
+import { ALERT_REASON, ALERT_RISK_SCORE, ALERT_SEVERITY } from '@kbn/rule-data-utils';
/**
* Returns mocked data for field (mock this method: x-pack/plugins/security_solution/public/common/hooks/use_get_fields_data.ts)
@@ -22,6 +22,8 @@ export const mockGetFieldsData = (field: string): string[] => {
return ['host1'];
case 'user.name':
return ['user1'];
+ case ALERT_REASON:
+ return ['reason'];
default:
return [];
}
diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts
index 79fa4d0eea06e..7fedb18db416c 100644
--- a/x-pack/test/functional/apps/infra/home_page.ts
+++ b/x-pack/test/functional/apps/infra/home_page.ts
@@ -199,8 +199,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- // FLAKY: https://github.com/elastic/kibana/issues/157711
- describe.skip('alerts flyouts', () => {
+ describe('alerts flyouts', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await pageObjects.common.navigateToApp('infraOps');
@@ -217,7 +216,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.infraHome.closeAlertFlyout();
});
- it('should open and close inventory alert flyout', async () => {
+ it('should open and close metrics threshold alert flyout', async () => {
await pageObjects.infraHome.openMetricsThresholdAlertFlyout();
await pageObjects.infraHome.closeAlertFlyout();
});
diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts
index f9f088b106375..aed3558cfdcb4 100644
--- a/x-pack/test/functional/page_objects/infra_home_page.ts
+++ b/x-pack/test/functional/page_objects/infra_home_page.ts
@@ -335,20 +335,40 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide
await testSubjects.missingOrFail('metrics-alert-menu');
},
+ async dismissDatePickerTooltip() {
+ const isTooltipOpen = await testSubjects.exists(`waffleDatePickerIntervalTooltip`, {
+ timeout: 1000,
+ });
+
+ if (isTooltipOpen) {
+ await testSubjects.click(`waffleDatePickerIntervalTooltip`);
+ }
+ },
+
async openInventoryAlertFlyout() {
+ await this.dismissDatePickerTooltip();
await testSubjects.click('infrastructure-alerts-and-rules');
await testSubjects.click('inventory-alerts-menu-option');
- await testSubjects.click('inventory-alerts-create-rule');
+
+ // forces date picker tooltip to close in case it pops up after Alerts and rules opens
+ await testSubjects.moveMouseTo('contextMenuPanelTitleButton');
+
+ await retry.tryForTime(1000, () => testSubjects.click('inventory-alerts-create-rule'));
await testSubjects.missingOrFail('inventory-alerts-create-rule', { timeout: 30000 });
- await testSubjects.find('euiFlyoutCloseButton');
},
async openMetricsThresholdAlertFlyout() {
+ await this.dismissDatePickerTooltip();
await testSubjects.click('infrastructure-alerts-and-rules');
await testSubjects.click('metrics-threshold-alerts-menu-option');
- await testSubjects.click('metrics-threshold-alerts-create-rule');
+
+ // forces date picker tooltip to close in case it pops up after Alerts and rules opens
+ await testSubjects.moveMouseTo('contextMenuPanelTitleButton');
+
+ await retry.tryForTime(1000, () =>
+ testSubjects.click('metrics-threshold-alerts-create-rule')
+ );
await testSubjects.missingOrFail('metrics-threshold-alerts-create-rule', { timeout: 30000 });
- await testSubjects.find('euiFlyoutCloseButton');
},
async closeAlertFlyout() {
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts
index 59aa3a5abe793..1a1b47e925c91 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/alerts_detection_callouts_index_outdated.cy.ts
@@ -15,7 +15,11 @@ import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
-import { getCallOut, waitForCallOutToBeShown } from '../../tasks/common/callouts';
+import {
+ getCallOut,
+ NEED_ADMIN_FOR_UPDATE_CALLOUT,
+ waitForCallOutToBeShown,
+} from '../../tasks/common/callouts';
const loadPageAsPlatformEngineerUser = (url: string) => {
login(ROLES.soc_manager);
@@ -31,8 +35,6 @@ describe(
'Detections > Need Admin Callouts indicating an admin is needed to migrate the alert data set',
{ tags: tag.ESS },
() => {
- const NEED_ADMIN_FOR_UPDATE_CALLOUT = 'need-admin-for-update-rules';
-
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.
// Otherwise the app will be disabled and show a "welcome"-like page.
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts
index 4ca60eebad297..767b2ecbdd5c2 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_alerts/missing_privileges_callout.cy.ts
@@ -15,7 +15,12 @@ import { PAGE_TITLE } from '../../screens/common/page';
import { login, visitWithoutDateRange, waitForPageWithoutDateRange } from '../../tasks/login';
import { goToRuleDetails } from '../../tasks/alerts_detection_rules';
import { createRule, deleteCustomRule } from '../../tasks/api_calls/rules';
-import { getCallOut, waitForCallOutToBeShown, dismissCallOut } from '../../tasks/common/callouts';
+import {
+ getCallOut,
+ waitForCallOutToBeShown,
+ dismissCallOut,
+ MISSING_PRIVILEGES_CALLOUT,
+} from '../../tasks/common/callouts';
const loadPageAsReadOnlyUser = (url: string) => {
login(ROLES.reader);
@@ -39,8 +44,6 @@ const waitForPageTitleToBeShown = () => {
};
describe('Detections > Callouts', { tags: tag.ESS }, () => {
- const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
-
before(() => {
// First, we have to open the app on behalf of a privileged user in order to initialize it.
// Otherwise the app will be disabled and show a "welcome"-like page.
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts
index 62f484b69427a..bd8c5743d37b2 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/authorization/all_rules_read_only.cy.ts
@@ -22,12 +22,11 @@ import {
dismissCallOut,
getCallOut,
waitForCallOutToBeShown,
+ MISSING_PRIVILEGES_CALLOUT,
} from '../../../../tasks/common/callouts';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { SECURITY_DETECTIONS_RULES_URL } from '../../../../urls/navigation';
-const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
-
describe('All rules - read only', { tags: tag.ESS }, () => {
before(() => {
cleanKibana();
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts
index d6000c671fb86..3b3755dc66d3f 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts
@@ -7,6 +7,11 @@
import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types';
import { ROLES } from '@kbn/security-solution-plugin/common/test';
+import {
+ MISSING_PRIVILEGES_CALLOUT,
+ waitForCallOutToBeShown,
+} from '../../../../../tasks/common/callouts';
+import { createRuleAssetSavedObject } from '../../../../../helpers/rules';
import { tag } from '../../../../../tags';
import {
@@ -34,6 +39,7 @@ import {
waitForRulesTableToBeLoaded,
selectNumberOfRules,
goToEditRuleActionsSettingsOf,
+ disableAutoRefresh,
} from '../../../../../tasks/alerts_detection_rules';
import {
waitForBulkEditActionToFinish,
@@ -57,28 +63,28 @@ import {
getMachineLearningRule,
getNewTermsRule,
} from '../../../../../objects/rule';
-import { excessivelyInstallAllPrebuiltRules } from '../../../../../tasks/api_calls/prebuilt_rules';
+import {
+ createAndInstallMockedPrebuiltRules,
+ excessivelyInstallAllPrebuiltRules,
+ preventPrebuiltRulesPackageInstallation,
+} from '../../../../../tasks/api_calls/prebuilt_rules';
const ruleNameToAssert = 'Custom rule name with actions';
const expectedNumberOfCustomRulesToBeEdited = 7;
-// 7 custom rules of different types + 3 prebuilt.
+// 7 custom rules of different types + 2 prebuilt.
// number of selected rules doesn't matter, we only want to make sure they will be edited an no modal window displayed as for other actions
-const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 3;
+const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 2;
const expectedExistingSlackMessage = 'Existing slack action';
const expectedSlackMessage = 'Slack action test message';
-// TODO: Fix flakiness and unskip https://github.com/elastic/kibana/issues/154721
-describe.skip(
+describe(
'Detection rules, bulk edit of rule actions',
{ tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
() => {
- before(() => {
+ beforeEach(() => {
cleanKibana();
login();
- });
-
- beforeEach(() => {
deleteAlertsAndRules();
deleteConnectors();
cy.task('esArchiverResetKibana');
@@ -111,12 +117,27 @@ describe.skip(
createRule(getNewRule({ saved_id: 'mocked', rule_id: '7' }));
createSlackConnector();
+
+ // Prevent prebuilt rules package installation and mock two prebuilt rules
+ preventPrebuiltRulesPackageInstallation();
+
+ const RULE_1 = createRuleAssetSavedObject({
+ name: 'Test rule 1',
+ rule_id: 'rule_1',
+ });
+ const RULE_2 = createRuleAssetSavedObject({
+ name: 'Test rule 2',
+ rule_id: 'rule_2',
+ });
+
+ createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2] });
});
context('Restricted action privileges', () => {
it("User with no privileges can't add rule actions", () => {
login(ROLES.hunter_no_actions);
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.hunter_no_actions);
+ waitForCallOutToBeShown(MISSING_PRIVILEGES_CALLOUT, 'primary');
waitForRulesTableToBeLoaded();
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
@@ -129,8 +150,10 @@ describe.skip(
context('All actions privileges', () => {
beforeEach(() => {
+ login();
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
waitForRulesTableToBeLoaded();
+ disableAutoRefresh();
});
it('Add a rule action to rules (existing connector)', () => {
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts
index 25b9d2e34fe2e..b11d3de105b83 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts
@@ -17,12 +17,11 @@ import {
dismissCallOut,
getCallOut,
waitForCallOutToBeShown,
+ MISSING_PRIVILEGES_CALLOUT,
} from '../../../../tasks/common/callouts';
import { login, visitWithoutDateRange } from '../../../../tasks/login';
import { EXCEPTIONS_URL } from '../../../../urls/navigation';
-const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
-
describe('Shared exception lists - read only', { tags: tag.ESS }, () => {
before(() => {
cy.task('esArchiverResetKibana');
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_preview_panel_alert_reason_preview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_preview_panel_alert_reason_preview.cy.ts
new file mode 100644
index 0000000000000..83d2dbee62212
--- /dev/null
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_preview_panel_alert_reason_preview.cy.ts
@@ -0,0 +1,42 @@
+/*
+ * 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 { DOCUMENT_DETAILS_FLYOUT_ALERT_REASON_PREVIEW_CONTAINER } from '../../../../screens/expandable_flyout/alert_details_preview_panel_alert_reason_preview';
+import { expandFirstAlertExpandableFlyout } from '../../../../tasks/expandable_flyout/common';
+import { clickAlertReasonButton } from '../../../../tasks/expandable_flyout/alert_details_right_panel_overview_tab';
+import { cleanKibana } from '../../../../tasks/common';
+import { login, visit } from '../../../../tasks/login';
+import { createRule } from '../../../../tasks/api_calls/rules';
+import { getNewRule } from '../../../../objects/rule';
+import { ALERTS_URL } from '../../../../urls/navigation';
+import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';
+import { tag } from '../../../../tags';
+
+describe(
+ 'Alert details expandable flyout rule preview panel',
+ { tags: [tag.ESS, tag.BROKEN_IN_SERVERLESS] },
+ () => {
+ const rule = getNewRule();
+
+ beforeEach(() => {
+ cleanKibana();
+ login();
+ createRule(rule);
+ visit(ALERTS_URL);
+ waitForAlertsToPopulate();
+ expandFirstAlertExpandableFlyout();
+ clickAlertReasonButton();
+ });
+
+ describe('alert reason preview', () => {
+ it('should display alert reason preview', () => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_ALERT_REASON_PREVIEW_CONTAINER).scrollIntoView();
+ cy.get(DOCUMENT_DETAILS_FLYOUT_ALERT_REASON_PREVIEW_CONTAINER).should('be.visible');
+ });
+ });
+ }
+);
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts
index 2cdf95746dcfa..050463b70ae50 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts
@@ -111,7 +111,8 @@ describe(
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE)
.should('be.visible')
- .and('have.text', 'Alert reason');
+ .and('contain.text', 'Alert reason')
+ .and('contain.text', 'Show full reason');
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_DETAILS)
.should('be.visible')
.and('contain.text', rule.name);
diff --git a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_preview_panel_alert_reason_preview.ts b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_preview_panel_alert_reason_preview.ts
new file mode 100644
index 0000000000000..37db919da75ab
--- /dev/null
+++ b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_preview_panel_alert_reason_preview.ts
@@ -0,0 +1,13 @@
+/*
+ * 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 { ALERT_REASON_PREVIEW_BODY_TEST_ID } from '@kbn/security-solution-plugin/public/flyout/preview/components/test_ids';
+import { getDataTestSubjectSelector } from '../../helpers/common';
+
+export const DOCUMENT_DETAILS_FLYOUT_ALERT_REASON_PREVIEW_CONTAINER = getDataTestSubjectSelector(
+ ALERT_REASON_PREVIEW_BODY_TEST_ID
+);
diff --git a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel_overview_tab.ts b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel_overview_tab.ts
index dc7e3fdd1020e..ebfb40a3091f5 100644
--- a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel_overview_tab.ts
+++ b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel_overview_tab.ts
@@ -38,6 +38,7 @@ import {
ANALYZER_PREVIEW_CONTENT_TEST_ID,
SESSION_PREVIEW_CONTENT_TEST_ID,
INSIGHTS_PREVALENCE_VALUE_TEST_ID,
+ REASON_DETAILS_PREVIEW_BUTTON_TEST_ID,
} from '@kbn/security-solution-plugin/public/flyout/right/components/test_ids';
import { getDataTestSubjectSelector } from '../../helpers/common';
@@ -59,6 +60,8 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE =
getDataTestSubjectSelector(REASON_TITLE_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_DETAILS =
getDataTestSubjectSelector(REASON_DETAILS_TEST_ID);
+export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_ALERT_REASON_PREVIEW_BUTTON =
+ getDataTestSubjectSelector(REASON_DETAILS_PREVIEW_BUTTON_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_MITRE_ATTACK_TITLE = getDataTestSubjectSelector(
MITRE_ATTACK_TITLE_TEST_ID
);
diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common/callouts.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common/callouts.ts
index c65a29b8aa750..802faf821f8da 100644
--- a/x-pack/test/security_solution_cypress/cypress/tasks/common/callouts.ts
+++ b/x-pack/test/security_solution_cypress/cypress/tasks/common/callouts.ts
@@ -7,6 +7,9 @@
import { callOutWithId, CALLOUT_DISMISS_BTN } from '../../screens/common/callouts';
+export const NEED_ADMIN_FOR_UPDATE_CALLOUT = 'need-admin-for-update-rules';
+export const MISSING_PRIVILEGES_CALLOUT = 'missing-user-privileges';
+
export const getCallOut = (id: string, options?: Cypress.Timeoutable) => {
return cy.get(callOutWithId(id), options);
};
diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout/alert_details_right_panel_overview_tab.ts b/x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout/alert_details_right_panel_overview_tab.ts
index c87a3c2afb9fe..b94a8be090fa4 100644
--- a/x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout/alert_details_right_panel_overview_tab.ts
+++ b/x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout/alert_details_right_panel_overview_tab.ts
@@ -20,6 +20,8 @@ import {
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_RESPONSE_SECTION_HEADER,
+ DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE,
+ DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_ALERT_REASON_PREVIEW_BUTTON,
} from '../../screens/expandable_flyout/alert_details_right_panel_overview_tab';
/* About section */
@@ -129,3 +131,17 @@ export const clickRuleSummaryButton = () => {
.click();
});
};
+
+/**
+ * Click `Show full reason` button to open alert reason preview panel
+ */
+export const clickAlertReasonButton = () => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE).scrollIntoView();
+ cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE)
+ .should('be.visible')
+ .within(() => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_ALERT_REASON_PREVIEW_BUTTON)
+ .should('be.visible')
+ .click();
+ });
+};