Skip to content

Commit

Permalink
[Security Solution][Entity details] - move osquery, response and inve…
Browse files Browse the repository at this point in the history
…stigation guide related hooks and components to flyout folder
  • Loading branch information
PhilippeOberti committed Aug 19, 2024
1 parent 29c5381 commit 4ef5bc0
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,10 @@

import { i18n } from '@kbn/i18n';

export const INVESTIGATION_GUIDE = i18n.translate(
'xpack.securitySolution.alertDetails.overview.investigationGuide',
{
defaultMessage: 'Investigation guide',
}
);

export const TABLE = i18n.translate('xpack.securitySolution.eventDetails.table', {
defaultMessage: 'Table',
});

export const OSQUERY_VIEW = i18n.translate('xpack.securitySolution.eventDetails.osqueryView', {
defaultMessage: 'Osquery Results',
});

export const RESPONSE_ACTIONS_VIEW = i18n.translate(
'xpack.securitySolution.eventDetails.responseActionsView',
{
defaultMessage: 'Response Results',
}
);

export const DESCRIPTION = i18n.translate('xpack.securitySolution.eventDetails.description', {
defaultMessage: 'Description',
});
Expand All @@ -48,20 +30,6 @@ export const RULE_TYPE = i18n.translate('xpack.securitySolution.detections.alert
defaultMessage: 'Rule type',
});

export const MULTI_FIELD_TOOLTIP = i18n.translate(
'xpack.securitySolution.eventDetails.multiFieldTooltipContent',
{
defaultMessage: 'Multi-fields can have multiple values per field',
}
);

export const MULTI_FIELD_BADGE = i18n.translate(
'xpack.securitySolution.eventDetails.multiFieldBadge',
{
defaultMessage: 'multi-field',
}
);

export const ACTIONS = i18n.translate('xpack.securitySolution.eventDetails.table.actions', {
defaultMessage: 'Actions',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { useAppToasts } from '../../../../hooks/use_app_toasts';
import { useKibana } from '../../../../lib/kibana';
import { useInsightQuery } from './use_insight_query';
import { useInsightDataProviders, type Provider } from './use_insight_data_providers';
import { BasicAlertDataContext } from '../../../event_details/investigation_guide_view';
import { BasicAlertDataContext } from '../../../../../flyout/document_details/left/components/investigation_guide_view';
import { InvestigateInTimelineButton } from '../../../event_details/table/investigate_in_timeline_button';
import {
getTimeRangeSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import styled from 'styled-components';
import { EuiButton, EuiToolTip } from '@elastic/eui';
import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
import { useUpsellingMessage } from '../../../../hooks/use_upselling';
import { BasicAlertDataContext } from '../../../event_details/investigation_guide_view';
import { BasicAlertDataContext } from '../../../../../flyout/document_details/left/components/investigation_guide_view';
import { expandDottedObject } from '../../../../../../common/utils/expand_dotted';
import OsqueryLogo from './osquery_icon/osquery.svg';
import { OsqueryFlyout } from '../../../../../detections/components/osquery/osquery_flyout';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
import { useDocumentDetailsContext } from '../../shared/context';
import { INVESTIGATION_GUIDE_TEST_ID, INVESTIGATION_GUIDE_LOADING_TEST_ID } from './test_ids';
import { InvestigationGuideView } from '../../../../common/components/event_details/investigation_guide_view';
import { InvestigationGuideView } from './investigation_guide_view';
import { FlyoutLoading } from '../../../shared/components/flyout_loading';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { InvestigationGuideView } from './investigation_guide_view';
import type { GetBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
import type { GetBasicDataFromDetailsData } from '../../../../timelines/components/side_panel/event_details/helpers';

const defaultProps = {
basicData: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@
import { EuiSpacer, EuiTitle, EuiText } from '@elastic/eui';
import React, { createContext } from 'react';
import styled from 'styled-components';
import type { GetBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
import * as i18n from './translations';
import { MarkdownRenderer } from '../markdown_editor';
import { LineClamp } from '../line_clamp';
import { i18n } from '@kbn/i18n';
import type { GetBasicDataFromDetailsData } from '../../../../timelines/components/side_panel/event_details/helpers';
import { LineClamp } from '../../../../common/components/line_clamp';
import { MarkdownRenderer } from '../../../../common/components/markdown_editor';

const INVESTIGATION_GUIDE = i18n.translate(
'xpack.securitySolution.flyout.left.investigationGuide',
{
defaultMessage: 'Investigation guide',
}
);

export const Indent = styled.div`
padding: 0 8px;
Expand Down Expand Up @@ -43,7 +50,6 @@ interface InvestigationGuideViewProps {
/**
* Investigation guide that shows the markdown text of rule.note
*/
// TODO: MOVE TO FLYOUT FOLDER - https://github.com/elastic/security-team/issues/7462
const InvestigationGuideViewComponent: React.FC<InvestigationGuideViewProps> = ({
basicData,
ruleNote,
Expand All @@ -56,7 +62,7 @@ const InvestigationGuideViewComponent: React.FC<InvestigationGuideViewProps> = (
<>
<EuiSpacer size="l" />
<EuiTitle size="xxxs" data-test-subj="summary-view-guide">
<h5>{i18n.INVESTIGATION_GUIDE}</h5>
<h5>{INVESTIGATION_GUIDE}</h5>
</EuiTitle>
<EuiSpacer size="s" />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n-react';
import { RESPONSE_DETAILS_TEST_ID } from './test_ids';
import { useDocumentDetailsContext } from '../../shared/context';
import { useResponseActionsView } from '../../../../common/components/event_details/response_actions_view';
import { useResponseActionsView } from '../hooks/use_response_actions_view';

const ExtendedFlyoutWrapper = styled.div`
figure {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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 { renderHook } from '@testing-library/react-hooks';
import { useOsqueryTab } from './use_osquery_tab';
import { mockSearchHit } from '../../shared/mocks/mock_search_hit';
import { mockDataAsNestedObject } from '../../shared/mocks/mock_data_as_nested_object';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useKibana } from '../../../../common/lib/kibana';
import type { ExperimentalFeatures } from '../../../../../common';
import { allowedExperimentalValues } from '../../../../../common';
import { ResponseActionTypesEnum } from '../../../../../common/api/detection_engine';

const ecsData = mockDataAsNestedObject;
const rawEventData = mockSearchHit;

jest.mock('../../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(true),
}));
jest.mock('../../../../management/hooks/response_actions/use_get_automated_action_list');
jest.mock('../../../../common/lib/kibana');

const rawEventDataWithResponseActions = {
...rawEventData,
fields: {
'kibana.alert.rule.parameters': [
{
response_actions: [
{
action_type_id: ResponseActionTypesEnum['.osquery'],
},
],
},
],
},
};

describe('useOsqueryTab', () => {
beforeEach(() => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
jest.fn((feature: keyof ExperimentalFeatures) => {
if (feature === 'responseActionsEnabled') return true;
if (feature === 'endpointResponseActionsEnabled') return false;
return allowedExperimentalValues[feature];
})
);
(useKibana as jest.Mock).mockReturnValue({
services: {
osquery: {
OsqueryResults: {},
fetchAllLiveQueries: jest.fn().mockReturnValue({ data: { data: {} } }),
},
},
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return the normal component', () => {
const { result } = renderHook(() =>
useOsqueryTab({
ecsData,
rawEventData: rawEventDataWithResponseActions,
})
);

expect(result.current?.id).toEqual('osquery-results-view');
expect(result.current?.name).toEqual('Osquery Results');
expect(result.current?.append).toBeDefined();
expect(result.current?.content).toBeDefined();
});

it('should return undefined if rawEventData is undefined', () => {
const { result } = renderHook(() =>
useOsqueryTab({
ecsData,
rawEventData: undefined,
})
);

expect(result.current).toEqual(undefined);
});

it('should return undefined if responseActionsEnabled feature flag is off', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
jest.fn((feature: keyof ExperimentalFeatures) => {
if (feature === 'responseActionsEnabled') return false;
return allowedExperimentalValues[feature];
})
);

const { result } = renderHook(() =>
useOsqueryTab({
ecsData,
rawEventData: rawEventDataWithResponseActions,
})
);

expect(result.current).toEqual(undefined);
});

it('should return undefined if endpointResponseActionsEnabled feature flag is on', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
jest.fn((feature: keyof ExperimentalFeatures) => {
if (feature === 'endpointResponseActionsEnabled') return true;
return allowedExperimentalValues[feature];
})
);

const { result } = renderHook(() =>
useOsqueryTab({
ecsData,
rawEventData: rawEventDataWithResponseActions,
})
);

expect(result.current).toEqual(undefined);
});

it('should return undefined if ecsData is undefined', () => {
const { result } = renderHook(() =>
useOsqueryTab({
ecsData: null,
rawEventData: rawEventDataWithResponseActions,
})
);

expect(result.current).toEqual(undefined);
});

it('should return undefined if there are no response actions', () => {
const rawEventDataWithNoResponseActions = {
...rawEventData,
fields: {
'kibana.alert.rule.parameters': [
{
response_actions: [],
},
],
},
};

const { result } = renderHook(() =>
useOsqueryTab({
ecsData,
rawEventData: rawEventDataWithNoResponseActions,
})
);

expect(result.current).toEqual(undefined);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 { renderHook } from '@testing-library/react-hooks';
import { useResponseActionsView } from './use_response_actions_view';
import { mockSearchHit } from '../../shared/mocks/mock_search_hit';
import { mockDataAsNestedObject } from '../../shared/mocks/mock_data_as_nested_object';
import { useGetAutomatedActionList } from '../../../../management/hooks/response_actions/use_get_automated_action_list';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';

const ecsData = mockDataAsNestedObject;
const rawEventData = mockSearchHit;

jest.mock('../../../../common/hooks/use_experimental_features');
jest.mock('../../../../management/hooks/response_actions/use_get_automated_action_list');

describe('useResponseActionsView', () => {
it('should return the normal component', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useGetAutomatedActionList as jest.Mock).mockReturnValue({
data: [],
isFetched: true,
});

const { result } = renderHook(() =>
useResponseActionsView({
ecsData,
rawEventData,
})
);

expect(result.current.id).toEqual('response-actions-results-view');
expect(result.current.name).toEqual('Response Results');
expect(result.current.append).toBeDefined();
expect(result.current.content).toBeDefined();
});

it('returns early return if rawEventData is undefined', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useGetAutomatedActionList as jest.Mock).mockReturnValue({
data: [],
isFetched: true,
});

const { result } = renderHook(() =>
useResponseActionsView({
ecsData,
rawEventData: undefined,
})
);

expect(result.current.id).toEqual('response-actions-results-view');
expect(result.current.name).toEqual('Response Results');
expect(result.current.append).not.toBeDefined();
expect(result.current.content).toBeDefined();
});

it('returns early return if endpointResponseActionsEnabled feature flag is off', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
(useGetAutomatedActionList as jest.Mock).mockReturnValue({
data: [],
isFetched: true,
});

const { result } = renderHook(() =>
useResponseActionsView({
ecsData,
rawEventData,
})
);

expect(result.current.id).toEqual('response-actions-results-view');
expect(result.current.name).toEqual('Response Results');
expect(result.current.append).not.toBeDefined();
expect(result.current.content).toBeDefined();
});
});
Loading

0 comments on commit 4ef5bc0

Please sign in to comment.