Skip to content

Commit

Permalink
[Security Solution] Fix analyzer preview for ES|QL rule type and 7.X …
Browse files Browse the repository at this point in the history
…alerts (#178389)

## Summary

This PR adds sourcerer index patterns to analyzer preview (cherry picked
from
[PR](https://github.com/elastic/kibana/pull/176332/files#diff-eed5f590fe397c4ef11097a1f1f05ce4f1906f0066c39cf5e040e367b6680717R50)).
Previously we only pull indices from `kibana.alert.rule.indices` but
this field is not always present for non-index related rules (ES|QL) or
past kibana versions (7.x)

Address:
#174596
#169373


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
christineweng authored Mar 11, 2024
1 parent 8d2950c commit 4044067
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { render } from '@testing-library/react';
import React from 'react';
import { TestProviders } from '../../../../common/mock';
import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters';
import { mockContextValue } from '../mocks/mock_context';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { RightPanelContext } from '../context';
Expand All @@ -21,6 +22,18 @@ jest.mock('../../../../common/containers/alerts/use_alert_prevalence_from_proces
}));
const mockUseAlertPrevalenceFromProcessTree = useAlertPrevalenceFromProcessTree as jest.Mock;

jest.mock('../../../../timelines/containers/use_timeline_data_filters', () => ({
useTimelineDataFilters: jest.fn(),
}));
const mockUseTimelineDataFilters = useTimelineDataFilters as jest.Mock;

const mockTreeValues = {
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
};

const renderAnalyzerPreview = (contextValue: RightPanelContext) =>
render(
<TestProviders>
Expand All @@ -35,15 +48,11 @@ const NO_DATA_MESSAGE = 'An error is preventing this alert from being analyzed.'
describe('<AnalyzerPreview />', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseTimelineDataFilters.mockReturnValue({ selectedPatterns: ['index'] });
});

it('shows analyzer preview correctly when documentId and index are present', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
});
mockUseAlertPrevalenceFromProcessTree.mockReturnValue(mockTreeValues);
const contextValue = {
...mockContextValue,
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
Expand All @@ -59,13 +68,23 @@ describe('<AnalyzerPreview />', () => {
expect(wrapper.getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument();
});

it('should use ancestor id when in preview', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
error: false,
alertIds: ['alertid'],
statsNodes: mock.mockStatsNodes,
it('should use selected index patterns for non-alerts', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue(mockTreeValues);
const wrapper = renderAnalyzerPreview({
...mockContextValue,
dataFormattedForFieldBrowser: [],
});

expect(mockUseAlertPrevalenceFromProcessTree).toHaveBeenCalledWith({
isActiveTimeline: false,
documentId: 'eventId',
indices: ['index'],
});
expect(wrapper.getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument();
});

it('should use ancestor id as document id when in preview', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue(mockTreeValues);
const contextValue = {
...mockContextValue,
getFieldsData: () => 'ancestors-id',
Expand All @@ -83,36 +102,6 @@ describe('<AnalyzerPreview />', () => {
expect(wrapper.getByTestId(ANALYZER_PREVIEW_TEST_ID)).toBeInTheDocument();
});

it('shows error message when index is not present', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
error: false,
alertIds: undefined,
statsNodes: undefined,
});
const contextValue = {
...mockContextValue,
dataFormattedForFieldBrowser: [
{
category: 'kibana',
field: 'kibana.alert.rule.uuid',
values: ['rule-uuid'],
originalValue: ['rule-uuid'],
isObjectArray: false,
},
],
};
const { getByText } = renderAnalyzerPreview(contextValue);

expect(mockUseAlertPrevalenceFromProcessTree).toHaveBeenCalledWith({
isActiveTimeline: false,
documentId: 'eventId',
indices: [],
});

expect(getByText(NO_DATA_MESSAGE)).toBeInTheDocument();
});

it('shows error message when there is an error', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers
import type { StatsNode } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
import { isActiveTimeline } from '../../../../helpers';
import { getField } from '../../shared/utils';
import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters';

const CHILD_COUNT_LIMIT = 3;
const ANCESTOR_LEVEL = 3;
Expand Down Expand Up @@ -45,8 +46,9 @@ export const AnalyzerPreview: React.FC = () => {

const documentId = isPreview ? ancestorId : eventId; // use ancestor as fallback for alert preview

const { selectedPatterns } = useTimelineDataFilters(isActiveTimeline(scopeId));
const index = find({ category: 'kibana', field: RULE_INDICES }, data);
const indices = index?.values ?? [];
const indices = index?.values ?? selectedPatterns; // adding sourcerer indices for non-alert documents

const { statsNodes, loading, error } = useAlertPrevalenceFromProcessTree({
isActiveTimeline: isActiveTimeline(scopeId),
Expand All @@ -65,7 +67,7 @@ export const AnalyzerPreview: React.FC = () => {
[cache.statsNodes]
);

const showAnalyzerTree = eventId && index && items && items.length > 0 && !error;
const showAnalyzerTree = items && items.length > 0 && !error;

return loading ? (
<EuiSkeletonText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,27 @@ import { mockContextValue } from '../mocks/mock_context';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { RightPanelContext } from '../context';
import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters';
import { TestProvider } from '@kbn/expandable-flyout/src/test/provider';

jest.mock('../../../../common/containers/alerts/use_alert_prevalence_from_process_tree', () => ({
useAlertPrevalenceFromProcessTree: jest.fn(),
}));
const mockUseAlertPrevalenceFromProcessTree = useAlertPrevalenceFromProcessTree as jest.Mock;

jest.mock('../../../../timelines/containers/use_timeline_data_filters', () => ({
useTimelineDataFilters: jest.fn(),
}));
const mockUseTimelineDataFilters = useTimelineDataFilters as jest.Mock;

const contextValue = {
...mockContextValue,
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
};

describe('<VisualizationsSection />', () => {
beforeEach(() => {
mockUseTimelineDataFilters.mockReturnValue({ selectedPatterns: ['index'] });
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
error: false,
Expand Down

0 comments on commit 4044067

Please sign in to comment.