Skip to content

Commit

Permalink
[Security Solution] Alert flyout - update document id in analyzer pre…
Browse files Browse the repository at this point in the history
…view and same ancestry (elastic#174651)

## Summary

Address: elastic#169373

This PR updates the use of `kibana.alert.ancestor.id` to `_id`
(available in flyout context as `eventId`) in analyzer preview and
alerts by ancestry. This change allows upgrade from 7.x kibana to 8.10+
to utilize analyzer preview.

No UI change introduced.

**How to test**
- Analyzer preview should match that of prior to the change
- Alert by ancestry in correlations overview (right section) and
correlations tab (left section -> Insights) should match that of prior
to the change
- Analyzer preview should match the analyzer viewer in alerts table


### 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 Jan 16, 2024
1 parent 4f4857c commit f288919
Show file tree
Hide file tree
Showing 12 changed files with 29 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('CorrelationsDetails', () => {
it('renders all sections', () => {
jest
.mocked(useShowRelatedAlertsByAncestry)
.mockReturnValue({ show: true, documentId: 'documentId', indices: ['index1'] });
.mockReturnValue({ show: true, indices: ['index1'] });
jest
.mocked(useShowRelatedAlertsBySameSourceEvent)
.mockReturnValue({ show: true, originalEventId: 'originalEventId' });
Expand Down Expand Up @@ -115,7 +115,7 @@ describe('CorrelationsDetails', () => {
it('should render no section and show error message if show values are false', () => {
jest
.mocked(useShowRelatedAlertsByAncestry)
.mockReturnValue({ show: false, documentId: 'documentId', indices: ['index1'] });
.mockReturnValue({ show: false, indices: ['index1'] });
jest
.mocked(useShowRelatedAlertsBySameSourceEvent)
.mockReturnValue({ show: false, originalEventId: 'originalEventId' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ export const CorrelationsDetails: React.FC = () => {
const { dataAsNestedObject, dataFormattedForFieldBrowser, eventId, getFieldsData, scopeId } =
useLeftPanelContext();

const {
show: showAlertsByAncestry,
documentId,
indices,
} = useShowRelatedAlertsByAncestry({
const { show: showAlertsByAncestry, indices } = useShowRelatedAlertsByAncestry({
getFieldsData,
dataAsNestedObject,
dataFormattedForFieldBrowser,
Expand Down Expand Up @@ -86,14 +82,9 @@ export const CorrelationsDetails: React.FC = () => {
<RelatedAlertsBySession entityId={entityId} scopeId={scopeId} eventId={eventId} />
</EuiFlexItem>
)}
{showAlertsByAncestry && documentId && indices && (
{showAlertsByAncestry && indices && (
<EuiFlexItem>
<RelatedAlertsByAncestry
documentId={documentId}
indices={indices}
scopeId={scopeId}
eventId={eventId}
/>
<RelatedAlertsByAncestry indices={indices} scopeId={scopeId} documentId={eventId} />
</EuiFlexItem>
)}
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jest.mock('../hooks/use_paginated_alerts');
const documentId = 'documentId';
const indices = ['index1'];
const scopeId = 'scopeId';
const eventId = 'eventId';

const TOGGLE_ICON = EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID(
CORRELATIONS_DETAILS_BY_ANCESTRY_SECTION_TEST_ID
Expand All @@ -42,12 +41,7 @@ const TITLE_TEXT = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(
const renderRelatedAlertsByAncestry = () =>
render(
<TestProviders>
<RelatedAlertsByAncestry
documentId={documentId}
indices={indices}
scopeId={scopeId}
eventId={eventId}
/>
<RelatedAlertsByAncestry documentId={documentId} indices={indices} scopeId={scopeId} />
</TestProviders>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { CORRELATIONS_DETAILS_BY_ANCESTRY_SECTION_TEST_ID } from './test_ids';

export interface RelatedAlertsByAncestryProps {
/**
* Value of the kibana.alert.ancestors.id field
* Id of the document
*/
documentId: string;
/**
Expand All @@ -24,10 +24,6 @@ export interface RelatedAlertsByAncestryProps {
* Maintain backwards compatibility // TODO remove when possible
*/
scopeId: string;
/**
* Id of the document
*/
eventId: string;
}

/**
Expand All @@ -37,7 +33,6 @@ export const RelatedAlertsByAncestry: React.VFC<RelatedAlertsByAncestryProps> =
documentId,
indices,
scopeId,
eventId,
}) => {
const { loading, error, data, dataCount } = useFetchRelatedAlertsByAncestry({
documentId,
Expand All @@ -61,7 +56,7 @@ export const RelatedAlertsByAncestry: React.VFC<RelatedAlertsByAncestryProps> =
loading={loading}
alertIds={data}
scopeId={scopeId}
eventId={eventId}
eventId={documentId}
noItemsMessage={
<FormattedMessage
id="xpack.securitySolution.flyout.left.insights.correlations.ancestryAlertsNoDataDescription"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ describe('<AnalyzerPreview />', () => {

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

it('shows error message when documentid and index are not present', () => {
it('shows error message when index is not present', () => {
mockUseAlertPrevalenceFromProcessTree.mockReturnValue({
loading: false,
error: false,
Expand All @@ -82,7 +82,7 @@ describe('<AnalyzerPreview />', () => {

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { ANALYZER_PREVIEW_TEST_ID, ANALYZER_PREVIEW_LOADING_TEST_ID } from './test_ids';
import { getTreeNodes } from '../utils/analyzer_helpers';
import { ANCESTOR_ID, RULE_INDICES } from '../../shared/constants/field_names';
import { RULE_INDICES } from '../../shared/constants/field_names';
import { useRightPanelContext } from '../context';
import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
import type { StatsNode } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree';
Expand All @@ -33,18 +33,14 @@ interface Cache {
*/
export const AnalyzerPreview: React.FC = () => {
const [cache, setCache] = useState<Partial<Cache>>({});
const { dataFormattedForFieldBrowser: data, scopeId } = useRightPanelContext();

const documentId = find({ category: 'kibana', field: ANCESTOR_ID }, data);
const processDocumentId =
documentId && Array.isArray(documentId.values) ? documentId.values[0] : '';
const { dataFormattedForFieldBrowser: data, scopeId, eventId } = useRightPanelContext();

const index = find({ category: 'kibana', field: RULE_INDICES }, data);
const indices = index?.values ?? [];

const { statsNodes, loading, error } = useAlertPrevalenceFromProcessTree({
isActiveTimeline: isActiveTimeline(scopeId),
documentId: processDocumentId,
documentId: eventId,
indices,
});

Expand All @@ -59,7 +55,7 @@ export const AnalyzerPreview: React.FC = () => {
[cache.statsNodes]
);

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

return loading ? (
<EuiSkeletonText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('<CorrelationsOverview />', () => {
it('should show component with all rows in expandable panel', () => {
jest
.mocked(useShowRelatedAlertsByAncestry)
.mockReturnValue({ show: true, documentId: 'documentId', indices: ['index1'] });
.mockReturnValue({ show: true, indices: ['index1'] });
jest
.mocked(useShowRelatedAlertsBySameSourceEvent)
.mockReturnValue({ show: true, originalEventId: 'originalEventId' });
Expand Down Expand Up @@ -145,7 +145,7 @@ describe('<CorrelationsOverview />', () => {
it('should hide rows and show error message if show values are false', () => {
jest
.mocked(useShowRelatedAlertsByAncestry)
.mockReturnValue({ show: false, documentId: 'documentId', indices: ['index1'] });
.mockReturnValue({ show: false, indices: ['index1'] });
jest
.mocked(useShowRelatedAlertsBySameSourceEvent)
.mockReturnValue({ show: false, originalEventId: 'originalEventId' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ export const CorrelationsOverview: React.FC = () => {
});
}, [eventId, openLeftPanel, indexName, scopeId]);

const {
show: showAlertsByAncestry,
documentId,
indices,
} = useShowRelatedAlertsByAncestry({
const { show: showAlertsByAncestry, indices } = useShowRelatedAlertsByAncestry({
getFieldsData,
dataAsNestedObject,
dataFormattedForFieldBrowser,
Expand Down Expand Up @@ -115,8 +111,8 @@ export const CorrelationsOverview: React.FC = () => {
{showAlertsBySession && entityId && (
<RelatedAlertsBySession entityId={entityId} scopeId={scopeId} />
)}
{showAlertsByAncestry && documentId && indices && (
<RelatedAlertsByAncestry documentId={documentId} indices={indices} scopeId={scopeId} />
{showAlertsByAncestry && indices && (
<RelatedAlertsByAncestry documentId={eventId} indices={indices} scopeId={scopeId} />
)}
</EuiFlexGroup>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ICON = 'warning';

export interface RelatedAlertsByAncestryProps {
/**
* Value of the kibana.alert.ancestors.id field
* Id of the document
*/
documentId: string;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { isActiveTimeline } from '../../../../helpers';

export interface UseFetchRelatedAlertsByAncestryParams {
/**
* Value of the kibana.alert.ancestors.id field
* Id of the document
*/
documentId: string;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex
import { licenseService } from '../../../../common/hooks/use_license';
import { mockDataFormattedForFieldBrowser } from '../mocks/mock_data_formatted_for_field_browser';
import { mockDataAsNestedObject } from '../mocks/mock_data_as_nested_object';
import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';

jest.mock('../../../../common/hooks/use_experimental_features');
jest.mock('../../../../common/hooks/use_license', () => {
Expand All @@ -29,6 +30,9 @@ jest.mock('../../../../common/hooks/use_license', () => {
},
};
});
jest.mock(
'../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'
);
const licenseServiceMock = licenseService as jest.Mocked<typeof licenseService>;

const dataAsNestedObject = mockDataAsNestedObject;
Expand All @@ -40,8 +44,9 @@ describe('useShowRelatedAlertsByAncestry', () => {
UseShowRelatedAlertsByAncestryResult
>;

it('should return false if getFieldsData returns null', () => {
it('should return false if Process Entity Info is not available', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
(useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(false);
licenseServiceMock.isPlatinumPlus.mockReturnValue(true);
const getFieldsData = () => null;
hookResult = renderHook(() =>
Expand Down Expand Up @@ -69,7 +74,6 @@ describe('useShowRelatedAlertsByAncestry', () => {

expect(hookResult.result.current).toEqual({
show: false,
documentId: 'value',
indices: ['rule-parameters-index'],
});
});
Expand All @@ -88,26 +92,6 @@ describe('useShowRelatedAlertsByAncestry', () => {

expect(hookResult.result.current).toEqual({
show: false,
documentId: 'value',
indices: ['rule-parameters-index'],
});
});

it('should return true if getFieldsData has the correct fields', () => {
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
licenseServiceMock.isPlatinumPlus.mockReturnValue(true);
const getFieldsData = () => 'value';
hookResult = renderHook(() =>
useShowRelatedAlertsByAncestry({
getFieldsData,
dataAsNestedObject,
dataFormattedForFieldBrowser,
})
);

expect(hookResult.result.current).toEqual({
show: true,
documentId: 'value',
indices: ['rule-parameters-index'],
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import type { GetFieldsData } from '../../../../common/hooks/use_get_fields_data
import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useLicense } from '../../../../common/hooks/use_license';
import { getField } from '../utils';
import { ANCESTOR_ID, RULE_PARAMETERS_INDEX } from '../constants/field_names';
import { RULE_PARAMETERS_INDEX } from '../constants/field_names';

export interface UseShowRelatedAlertsByAncestryParams {
/**
Expand All @@ -36,10 +35,6 @@ export interface UseShowRelatedAlertsByAncestryResult {
* Returns true if the user has at least platinum privilege, and if the document has ancestry data
*/
show: boolean;
/**
* Value of the kibana.alert.ancestors.id field
*/
documentId?: string;
/**
* Values of the kibana.alert.rule.parameters.index field
*/
Expand All @@ -59,8 +54,6 @@ export const useShowRelatedAlertsByAncestry = ({
);
const hasProcessEntityInfo = useIsInvestigateInResolverActionEnabled(dataAsNestedObject);

const originalDocumentId = getField(getFieldsData(ANCESTOR_ID));

// can't use getFieldsData here as the kibana.alert.rule.parameters is different and can be nested
const originalDocumentIndex = useMemo(
() => find({ category: 'kibana', field: RULE_PARAMETERS_INDEX }, dataFormattedForFieldBrowser),
Expand All @@ -72,13 +65,11 @@ export const useShowRelatedAlertsByAncestry = ({
const show =
isRelatedAlertsByProcessAncestryEnabled &&
hasProcessEntityInfo &&
originalDocumentId != null &&
originalDocumentIndex != null &&
hasAtLeastPlatinum;

return {
show,
...(originalDocumentId && { documentId: originalDocumentId }),
...(originalDocumentIndex &&
originalDocumentIndex.values && { indices: originalDocumentIndex.values }),
};
Expand Down

0 comments on commit f288919

Please sign in to comment.