mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution] [Analyzer] Use more correct ids from alerts for entity request, clean up some tests (#137370) (#137894)
* Use more correct ids from alerts for entity request, clean up some tests
* Fix cypress tests
* Remove cypress config change
* Update failing cypress tests, address pr comment
* Address pr comment in reality
* Update cypress data to work with analyzer api
* Update alert count in rule tests
* Delete commented code
(cherry picked from commit 2a4c473fdb
)
Co-authored-by: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com>
This commit is contained in:
parent
c01a9c9609
commit
6fb0959791
14 changed files with 348 additions and 121 deletions
|
@ -11,10 +11,14 @@ import {
|
|||
SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON,
|
||||
INSIGHTS_RELATED_ALERTS_BY_SESSION,
|
||||
INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON,
|
||||
INSIGHTS_RELATED_ALERTS_BY_ANCESTRY,
|
||||
INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON,
|
||||
} from '../../screens/alerts_details';
|
||||
import { QUERY_TAB_BUTTON, TIMELINE_TITLE } from '../../screens/timeline';
|
||||
|
||||
import { expandFirstAlert } from '../../tasks/alerts';
|
||||
import { verifyInsightCount } from '../../tasks/alerts_details';
|
||||
import { setStartDate } from '../../tasks/date_picker';
|
||||
import { closeTimeline } from '../../tasks/timeline';
|
||||
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
|
||||
import { cleanKibana } from '../../tasks/common';
|
||||
|
@ -31,6 +35,8 @@ describe('Alert Flyout', () => {
|
|||
login();
|
||||
createCustomRuleEnabled(getNewRule(), 'rule1');
|
||||
visitWithoutDateRange(ALERTS_URL);
|
||||
const dateContainingAllEvents = 'Jul 27, 2015 @ 00:00:00.000';
|
||||
setStartDate(dateContainingAllEvents);
|
||||
waitForAlertsToPopulate();
|
||||
expandFirstAlert();
|
||||
});
|
||||
|
@ -56,25 +62,16 @@ describe('Alert Flyout', () => {
|
|||
});
|
||||
|
||||
it('Opens a new timeline investigation (from an insights module)', () => {
|
||||
cy.get(INSIGHTS_RELATED_ALERTS_BY_SESSION)
|
||||
.click()
|
||||
.invoke('text')
|
||||
.then((relatedAlertsBySessionText) => {
|
||||
// Extract the count from the text
|
||||
const alertCount = relatedAlertsBySessionText.match(/(\d)/);
|
||||
const actualCount = alertCount && alertCount[0];
|
||||
verifyInsightCount({
|
||||
tableSelector: INSIGHTS_RELATED_ALERTS_BY_SESSION,
|
||||
investigateSelector: INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON,
|
||||
});
|
||||
});
|
||||
|
||||
// Make sure we can see the table
|
||||
cy.contains('New Rule Test').should('be.visible');
|
||||
|
||||
// Click on the first button that lets us investigate in timeline
|
||||
cy.get(ALERT_FLYOUT).find(INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON).click();
|
||||
|
||||
// Make sure a new timeline is created and opened
|
||||
cy.get(TIMELINE_TITLE).should('contain.text', 'Untitled timeline');
|
||||
|
||||
// The alert count in this timeline should match the count shown on the alert flyout
|
||||
cy.get(QUERY_TAB_BUTTON).should('contain.text', actualCount);
|
||||
});
|
||||
it('Opens a new timeline investigation with alert ids from the process ancestry', () => {
|
||||
verifyInsightCount({
|
||||
tableSelector: INSIGHTS_RELATED_ALERTS_BY_ANCESTRY,
|
||||
investigateSelector: INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -72,7 +72,7 @@ describe('EQL rules', () => {
|
|||
const expectedTags = getEqlRule().tags.join('');
|
||||
const expectedMitre = formatMitreAttackDescription(getEqlRule().mitre);
|
||||
const expectedNumberOfRules = 1;
|
||||
const expectedNumberOfAlerts = '1 alert';
|
||||
const expectedNumberOfAlerts = '2 alerts';
|
||||
|
||||
beforeEach(() => {
|
||||
createTimeline(getEqlRule().timeline).then((response) => {
|
||||
|
|
|
@ -77,3 +77,7 @@ export const SUMMARY_VIEW_INVESTIGATE_IN_TIMELINE_BUTTON = `${SUMMARY_VIEW} [ari
|
|||
export const INSIGHTS_RELATED_ALERTS_BY_SESSION = `[data-test-subj='related-alerts-by-session']`;
|
||||
|
||||
export const INSIGHTS_INVESTIGATE_IN_TIMELINE_BUTTON = `${INSIGHTS_RELATED_ALERTS_BY_SESSION} [aria-label='Investigate in timeline']`;
|
||||
|
||||
export const INSIGHTS_RELATED_ALERTS_BY_ANCESTRY = `[data-test-subj='related-alerts-by-ancestry']`;
|
||||
|
||||
export const INSIGHTS_INVESTIGATE_ANCESTRY_ALERTS_IN_TIMELINE_BUTTON = `[data-test-subj='investigate-ancestry-in-timeline']`;
|
||||
|
|
|
@ -24,5 +24,14 @@ export const DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE =
|
|||
export const DATE_PICKER_START_DATE_POPOVER_BUTTON =
|
||||
'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
|
||||
|
||||
export const GLOBAL_FILTERS_CONTAINER = '[data-test-subj="globalDatePicker"]';
|
||||
|
||||
export const SHOW_DATES_BUTTON = '[data-test-subj="superDatePickerShowDatesButton"]';
|
||||
|
||||
export const DATE_PICKER_SHOW_DATE_POPOVER_BUTTON = `${GLOBAL_FILTERS_CONTAINER} ${SHOW_DATES_BUTTON}`;
|
||||
|
||||
export const DATE_PICKER_RELATIVE_DATE_INPUT_UNIT_SELECTOR =
|
||||
'[data-test-subj="superDatePickerRelativeDateInputUnitSelector"]';
|
||||
|
||||
export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE =
|
||||
'[data-test-subj="timeline-date-picker-container"] [data-test-subj="superDatePickerstartDatePopoverButton"]';
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
ALERT_FLYOUT,
|
||||
ENRICHMENT_COUNT_NOTIFICATION,
|
||||
JSON_VIEW_TAB,
|
||||
OVERVIEW_TAB,
|
||||
TABLE_TAB,
|
||||
FILTER_INPUT,
|
||||
} from '../screens/alerts_details';
|
||||
import { TIMELINE_TITLE, QUERY_TAB_BUTTON } from '../screens/timeline';
|
||||
|
||||
export const filterBy = (value: string) => {
|
||||
cy.get(FILTER_INPUT).type(value);
|
||||
|
@ -32,3 +34,35 @@ export const openTable = () => {
|
|||
export const openThreatIndicatorDetails = () => {
|
||||
cy.get(ENRICHMENT_COUNT_NOTIFICATION).click();
|
||||
};
|
||||
|
||||
export const verifyInsightCount = ({
|
||||
tableSelector,
|
||||
investigateSelector,
|
||||
}: {
|
||||
tableSelector: string;
|
||||
investigateSelector: string;
|
||||
}) => {
|
||||
cy.get(tableSelector).click();
|
||||
|
||||
// Make sure the table containing data has loaded before parsing the text for the alert count
|
||||
cy.get(investigateSelector);
|
||||
cy.get(tableSelector)
|
||||
.invoke('text')
|
||||
.then((relatedAlertsByAncestryText) => {
|
||||
// Extract the count from the text
|
||||
const alertCount = relatedAlertsByAncestryText.match(/(\d)/);
|
||||
const actualCount = alertCount && alertCount[0];
|
||||
|
||||
// Make sure we can see the table
|
||||
cy.contains('New Rule Test').should('be.visible');
|
||||
|
||||
// Click on the first button that lets us investigate in timeline
|
||||
cy.get(ALERT_FLYOUT).find(investigateSelector).click();
|
||||
|
||||
// Make sure a new timeline is created and opened
|
||||
cy.get(TIMELINE_TITLE).should('contain.text', 'Untitled timeline');
|
||||
|
||||
// The alert count in this timeline should match the count shown on the alert flyout
|
||||
cy.get(QUERY_TAB_BUTTON).should('contain.text', actualCount);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -13,7 +13,10 @@ import {
|
|||
DATE_PICKER_END_DATE_POPOVER_BUTTON,
|
||||
DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
|
||||
DATE_PICKER_START_DATE_POPOVER_BUTTON,
|
||||
GLOBAL_FILTERS_CONTAINER,
|
||||
SHOW_DATES_BUTTON,
|
||||
DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
|
||||
DATE_PICKER_SHOW_DATE_POPOVER_BUTTON,
|
||||
} from '../screens/date_picker';
|
||||
|
||||
export const setEndDate = (date: string) => {
|
||||
|
@ -25,11 +28,21 @@ export const setEndDate = (date: string) => {
|
|||
};
|
||||
|
||||
export const setStartDate = (date: string) => {
|
||||
cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
|
||||
cy.get(GLOBAL_FILTERS_CONTAINER);
|
||||
cy.get('.euiSuperDatePicker');
|
||||
cy.get('body').then(($container) => {
|
||||
if ($container.find(SHOW_DATES_BUTTON).length > 0) {
|
||||
cy.get(DATE_PICKER_SHOW_DATE_POPOVER_BUTTON).click({ force: true });
|
||||
} else {
|
||||
cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
|
||||
}
|
||||
});
|
||||
|
||||
cy.get(DATE_PICKER_ABSOLUTE_TAB).first().click({ force: true });
|
||||
|
||||
cy.get(DATE_PICKER_ABSOLUTE_INPUT).click().clear().type(date);
|
||||
|
||||
cy.get(DATE_PICKER_APPLY_BUTTON).click();
|
||||
};
|
||||
|
||||
export const setTimelineEndDate = (date: string) => {
|
||||
|
|
|
@ -37,6 +37,14 @@ export const Insights = React.memo<Props>(
|
|||
'insightsRelatedAlertsByProcessAncestry'
|
||||
);
|
||||
const processEntityField = find({ category: 'process', field: 'process.entity_id' }, data);
|
||||
const originalDocumentId = find(
|
||||
{ category: 'kibana', field: 'kibana.alert.ancestors.id' },
|
||||
data
|
||||
);
|
||||
const originalDocumentIndex = find(
|
||||
{ category: 'kibana', field: 'kibana.alert.rule.parameters.index' },
|
||||
data
|
||||
);
|
||||
const hasProcessEntityInfo =
|
||||
isRelatedAlertsByProcessAncestryEnabled && processEntityField && processEntityField.values;
|
||||
|
||||
|
@ -64,6 +72,13 @@ export const Insights = React.memo<Props>(
|
|||
hasSourceEventInfo ||
|
||||
hasProcessSessionInfo;
|
||||
|
||||
const canShowAncestryInsight =
|
||||
isRelatedAlertsByProcessAncestryEnabled &&
|
||||
processEntityField &&
|
||||
processEntityField.values &&
|
||||
originalDocumentId &&
|
||||
originalDocumentIndex;
|
||||
|
||||
// If we're in read-only mode or don't have any insight-related data,
|
||||
// don't render anything.
|
||||
if (isReadOnly || !canShowAtLeastOneInsight) {
|
||||
|
@ -107,17 +122,17 @@ export const Insights = React.memo<Props>(
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
{isRelatedAlertsByProcessAncestryEnabled &&
|
||||
processEntityField &&
|
||||
processEntityField.values && (
|
||||
<EuiFlexItem>
|
||||
<RelatedAlertsByProcessAncestry
|
||||
data={processEntityField}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{canShowAncestryInsight && (
|
||||
<EuiFlexItem data-test-subj="related-alerts-by-ancestry">
|
||||
<RelatedAlertsByProcessAncestry
|
||||
data={processEntityField}
|
||||
originalDocumentId={originalDocumentId}
|
||||
index={originalDocumentIndex}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -21,6 +21,24 @@ jest.mock('../../../containers/alerts/use_alert_prevalence_from_process_tree', (
|
|||
}));
|
||||
const mockUseAlertPrevalenceFromProcessTree = useAlertPrevalenceFromProcessTree as jest.Mock;
|
||||
|
||||
const props = {
|
||||
eventId: 'random',
|
||||
data: {
|
||||
field: 'testfield',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
index: {
|
||||
field: 'index',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
originalDocumentId: {
|
||||
field: '_id',
|
||||
values: ['original'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
};
|
||||
describe('RelatedAlertsByProcessAncestry', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -29,14 +47,7 @@ describe('RelatedAlertsByProcessAncestry', () => {
|
|||
it('shows an accordion and does not fetch data right away', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<RelatedAlertsByProcessAncestry
|
||||
eventId="random"
|
||||
data={{
|
||||
field: 'testfield',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
}}
|
||||
/>
|
||||
<RelatedAlertsByProcessAncestry {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -51,14 +62,7 @@ describe('RelatedAlertsByProcessAncestry', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<RelatedAlertsByProcessAncestry
|
||||
eventId="random"
|
||||
data={{
|
||||
field: 'testfield',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
}}
|
||||
/>
|
||||
<RelatedAlertsByProcessAncestry {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -75,14 +79,7 @@ describe('RelatedAlertsByProcessAncestry', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<RelatedAlertsByProcessAncestry
|
||||
eventId="random"
|
||||
data={{
|
||||
field: 'testfield',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
}}
|
||||
/>
|
||||
<RelatedAlertsByProcessAncestry {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -100,14 +97,7 @@ describe('RelatedAlertsByProcessAncestry', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<RelatedAlertsByProcessAncestry
|
||||
eventId="random"
|
||||
data={{
|
||||
field: 'testfield',
|
||||
values: ['test value'],
|
||||
isObjectArray: false,
|
||||
}}
|
||||
/>
|
||||
<RelatedAlertsByProcessAncestry {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { useMemo, useCallback, useEffect, useState } from 'react';
|
|||
import { EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import type { DataProvider } from '../../../../../common/types';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline';
|
||||
import { getDataProvider } from '../table/use_action_cell_data_provider';
|
||||
import { useAlertPrevalenceFromProcessTree } from '../../../containers/alerts/use_alert_prevalence_from_process_tree';
|
||||
|
@ -21,6 +22,8 @@ import { PROCESS_ANCESTRY, PROCESS_ANCESTRY_COUNT, PROCESS_ANCESTRY_ERROR } from
|
|||
interface Props {
|
||||
data: TimelineEventsDetailsItem;
|
||||
eventId: string;
|
||||
index: TimelineEventsDetailsItem;
|
||||
originalDocumentId: TimelineEventsDetailsItem;
|
||||
timelineId?: string;
|
||||
}
|
||||
|
||||
|
@ -56,53 +59,57 @@ interface Cache {
|
|||
* Due to the ephemeral nature of the data, it was decided to keep the
|
||||
* state inside the component rather than to add it to Redux.
|
||||
*/
|
||||
export const RelatedAlertsByProcessAncestry = React.memo<Props>(({ data, eventId, timelineId }) => {
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
const [cache, setCache] = useState<Partial<Cache>>({});
|
||||
export const RelatedAlertsByProcessAncestry = React.memo<Props>(
|
||||
({ data, originalDocumentId, index, eventId, timelineId }) => {
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
const [cache, setCache] = useState<Partial<Cache>>({});
|
||||
|
||||
const onToggle = useCallback((isOpen: boolean) => setShowContent(isOpen), []);
|
||||
const onToggle = useCallback((isOpen: boolean) => setShowContent(isOpen), []);
|
||||
|
||||
// Makes sure the component is not fetching data before the accordion
|
||||
// has been openend.
|
||||
const renderContent = useCallback(() => {
|
||||
if (!showContent) {
|
||||
return null;
|
||||
} else if (cache.alertIds) {
|
||||
// Makes sure the component is not fetching data before the accordion
|
||||
// has been openend.
|
||||
const renderContent = useCallback(() => {
|
||||
if (!showContent) {
|
||||
return null;
|
||||
} else if (cache.alertIds) {
|
||||
return (
|
||||
<ActualRelatedAlertsByProcessAncestry
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
alertIds={cache.alertIds}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ActualRelatedAlertsByProcessAncestry
|
||||
<FetchAndNotifyCachedAlertsByProcessAncestry
|
||||
data={data}
|
||||
index={index}
|
||||
originalDocumentId={originalDocumentId}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
alertIds={cache.alertIds}
|
||||
onCacheLoad={setCache}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [showContent, cache, data, eventId, timelineId, index, originalDocumentId]);
|
||||
|
||||
const isEmpty = !!cache.alertIds && cache.alertIds.length === 0;
|
||||
|
||||
return (
|
||||
<FetchAndNotifyCachedAlertsByProcessAncestry
|
||||
data={data}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
onCacheLoad={setCache}
|
||||
<InsightAccordion
|
||||
prefix="RelatedAlertsByProcessAncestry"
|
||||
// `renderContent` and the associated sub-components are making sure to
|
||||
// render the correct loading and error states so we can omit these states here
|
||||
state={isEmpty ? 'empty' : 'success'}
|
||||
text={
|
||||
// If we have fetched the alerts, display the count here, otherwise omit the count
|
||||
cache.alertIds ? PROCESS_ANCESTRY_COUNT(cache.alertIds.length) : PROCESS_ANCESTRY
|
||||
}
|
||||
renderContent={renderContent}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
);
|
||||
}, [showContent, cache, data, eventId, timelineId]);
|
||||
|
||||
const isEmpty = !!cache.alertIds && cache.alertIds.length === 0;
|
||||
|
||||
return (
|
||||
<InsightAccordion
|
||||
prefix="RelatedAlertsByProcessAncestry"
|
||||
// `renderContent` and the associated sub-components are making sure to
|
||||
// render the correct loading and error states so we can omit these states here
|
||||
state={isEmpty ? 'empty' : 'success'}
|
||||
text={
|
||||
// If we have fetched the alerts, display the count here, otherwise omit the count
|
||||
cache.alertIds ? PROCESS_ANCESTRY_COUNT(cache.alertIds.length) : PROCESS_ANCESTRY
|
||||
}
|
||||
renderContent={renderContent}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
RelatedAlertsByProcessAncestry.displayName = 'RelatedAlertsByProcessAncestry';
|
||||
|
||||
|
@ -112,10 +119,22 @@ RelatedAlertsByProcessAncestry.displayName = 'RelatedAlertsByProcessAncestry';
|
|||
const FetchAndNotifyCachedAlertsByProcessAncestry: React.FC<{
|
||||
data: TimelineEventsDetailsItem;
|
||||
eventId: string;
|
||||
index: TimelineEventsDetailsItem;
|
||||
originalDocumentId: TimelineEventsDetailsItem;
|
||||
timelineId?: string;
|
||||
onCacheLoad: (cache: Cache) => void;
|
||||
}> = ({ data, timelineId, onCacheLoad, eventId }) => {
|
||||
const { loading, error, alertIds } = useAlertPrevalenceFromProcessTree(eventId, timelineId);
|
||||
}> = ({ data, originalDocumentId, index, timelineId, onCacheLoad, eventId }) => {
|
||||
const { values: wrappedProcessEntityId } = data;
|
||||
const { values: indices } = index;
|
||||
const { values: wrappedDocumentId } = originalDocumentId;
|
||||
const documentId = Array.isArray(wrappedDocumentId) ? wrappedDocumentId[0] : '';
|
||||
const processEntityId = Array.isArray(wrappedProcessEntityId) ? wrappedProcessEntityId[0] : '';
|
||||
const { loading, error, alertIds } = useAlertPrevalenceFromProcessTree({
|
||||
processEntityId,
|
||||
timelineId: timelineId ?? TimelineId.active,
|
||||
documentId,
|
||||
indices: indices ?? [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (alertIds) {
|
||||
|
@ -162,7 +181,11 @@ const ActualRelatedAlertsByProcessAncestry: React.FC<{
|
|||
<>
|
||||
<SimpleAlertTable alertIds={alertIds} />
|
||||
<EuiSpacer />
|
||||
<InvestigateInTimelineButton asEmptyButton={false} dataProviders={dataProviders}>
|
||||
<InvestigateInTimelineButton
|
||||
asEmptyButton={false}
|
||||
dataProviders={dataProviders}
|
||||
data-test-subj={'investigate-ancestry-in-timeline'}
|
||||
>
|
||||
{ACTION_INVESTIGATE_IN_TIMELINE}
|
||||
</InvestigateInTimelineButton>
|
||||
</>
|
||||
|
|
|
@ -21,7 +21,7 @@ import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/component
|
|||
export const InvestigateInTimelineButton: React.FunctionComponent<{
|
||||
asEmptyButton: boolean;
|
||||
dataProviders: DataProvider[];
|
||||
}> = ({ asEmptyButton, children, dataProviders }) => {
|
||||
}> = ({ asEmptyButton, children, dataProviders, ...rest }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const clearTimeline = useCreateTimeline({
|
||||
|
@ -64,7 +64,11 @@ export const InvestigateInTimelineButton: React.FunctionComponent<{
|
|||
{children}
|
||||
</EuiButtonEmpty>
|
||||
) : (
|
||||
<EuiButton aria-label={ACTION_INVESTIGATE_IN_TIMELINE} onClick={configureAndOpenTimeline}>
|
||||
<EuiButton
|
||||
aria-label={ACTION_INVESTIGATE_IN_TIMELINE}
|
||||
onClick={configureAndOpenTimeline}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</EuiButton>
|
||||
);
|
||||
|
|
|
@ -27,6 +27,18 @@ interface EntityResponse {
|
|||
schema: object;
|
||||
}
|
||||
|
||||
interface UseAlertPrevalenceFromProcessTree {
|
||||
processEntityId: string;
|
||||
documentId: string;
|
||||
timelineId: string;
|
||||
indices: string[];
|
||||
}
|
||||
|
||||
interface UseAlertDocumentAnalyzerSchema {
|
||||
documentId: string;
|
||||
indices: string[];
|
||||
}
|
||||
|
||||
interface TreeResponse {
|
||||
statsNodes: Array<{
|
||||
data: object;
|
||||
|
@ -42,12 +54,12 @@ interface TreeResponse {
|
|||
alertIds: string[];
|
||||
}
|
||||
|
||||
function useAlertDocumentAnalyzerSchema(processEntityId: string, indices: string[]) {
|
||||
function useAlertDocumentAnalyzerSchema({ documentId, indices }: UseAlertDocumentAnalyzerSchema) {
|
||||
const http = useHttp();
|
||||
const query = useQuery<EntityResponse[]>(['getAlertPrevalenceSchema', processEntityId], () => {
|
||||
const query = useQuery<EntityResponse[]>(['getAlertPrevalenceSchema', documentId], () => {
|
||||
return http.get<EntityResponse[]>(`/api/endpoint/resolver/entity`, {
|
||||
query: {
|
||||
_id: processEntityId,
|
||||
_id: documentId,
|
||||
indices,
|
||||
},
|
||||
});
|
||||
|
@ -59,7 +71,7 @@ function useAlertDocumentAnalyzerSchema(processEntityId: string, indices: string
|
|||
id: null,
|
||||
schema: null,
|
||||
};
|
||||
} else if (query.data) {
|
||||
} else if (query.data && query.data.length > 0) {
|
||||
const {
|
||||
data: [{ schema, id }],
|
||||
} = query;
|
||||
|
@ -79,15 +91,20 @@ function useAlertDocumentAnalyzerSchema(processEntityId: string, indices: string
|
|||
}
|
||||
}
|
||||
|
||||
export function useAlertPrevalenceFromProcessTree(
|
||||
processEntityId: string,
|
||||
timelineId: string | undefined
|
||||
): UserAlertPrevalenceFromProcessTreeResult {
|
||||
export function useAlertPrevalenceFromProcessTree({
|
||||
processEntityId,
|
||||
documentId,
|
||||
timelineId,
|
||||
indices,
|
||||
}: UseAlertPrevalenceFromProcessTree): UserAlertPrevalenceFromProcessTreeResult {
|
||||
const http = useHttp();
|
||||
|
||||
const { selectedPatterns, to, from } = useTimelineDataFilters(timelineId);
|
||||
|
||||
const { loading, id, schema } = useAlertDocumentAnalyzerSchema(processEntityId, selectedPatterns);
|
||||
const alertAndOriginalIndices = [...new Set(selectedPatterns.concat(indices))];
|
||||
const { loading, id, schema } = useAlertDocumentAnalyzerSchema({
|
||||
documentId,
|
||||
indices: alertAndOriginalIndices,
|
||||
});
|
||||
const query = useQuery<ProcessTreeAlertPrevalenceResponse>(
|
||||
['getAlertPrevalenceFromProcessTree', id],
|
||||
() => {
|
||||
|
@ -96,7 +113,7 @@ export function useAlertPrevalenceFromProcessTree(
|
|||
schema,
|
||||
ancestors: 200,
|
||||
descendants: 500,
|
||||
indexPatterns: selectedPatterns,
|
||||
indexPatterns: alertAndOriginalIndices,
|
||||
nodes: [id],
|
||||
timeRange: { from, to },
|
||||
includeHits: true,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import type { IScopedClusterClient } from '@kbn/core/server';
|
||||
import type { AlertsClient } from '@kbn/rule-registry-plugin/server';
|
||||
import { ALERT_RULE_UUID } from '@kbn/rule-data-utils';
|
||||
import type { JsonObject } from '@kbn/utility-types';
|
||||
import type { EventStats, ResolverSchema } from '../../../../../../common/endpoint/types';
|
||||
import type { NodeID, TimeRange } from '../utils';
|
||||
|
@ -197,9 +196,7 @@ export class StatsQuery {
|
|||
},
|
||||
])
|
||||
);
|
||||
const alertIdsRaw: Array<string | undefined> = alertsBody.hits.hits.map((hit) => {
|
||||
return hit._source && hit._source[ALERT_RULE_UUID];
|
||||
});
|
||||
const alertIdsRaw: Array<string | undefined> = alertsBody.hits.hits.map((hit) => hit._id);
|
||||
const alertIds = alertIdsRaw.flatMap((id) => (!id ? [] : [id]));
|
||||
|
||||
const eventAggStats = [...eventsWithAggs.values()];
|
||||
|
|
|
@ -51,6 +51,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'riskyHostsEnabled',
|
||||
'riskyUsersEnabled',
|
||||
'insightsRelatedAlertsByProcessAncestry',
|
||||
])}`,
|
||||
`--home.disableWelcomeScreen=true`,
|
||||
],
|
||||
|
|
|
@ -120,3 +120,126 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "_aZE5nwBOpWiDweSth_E",
|
||||
"index": "auditbeat-2022",
|
||||
"source": {
|
||||
"@timestamp" : "2022-03-04T19:41:34.045Z",
|
||||
"host" : {
|
||||
"hostname" : "test.local",
|
||||
"architecture" : "x86_64",
|
||||
"os" : {
|
||||
"platform" : "darwin",
|
||||
"version" : "10.16",
|
||||
"family" : "darwin",
|
||||
"name" : "Mac OS X",
|
||||
"kernel" : "21.3.0",
|
||||
"build" : "21D62",
|
||||
"type" : "macos"
|
||||
},
|
||||
"id" : "44426D67-79AB-547C-7777-440AB8F5DDD2",
|
||||
"ip" : [
|
||||
"fe80::bade:48ff:fe00:1122",
|
||||
"fe81::4ab:9565:1199:be3",
|
||||
"192.168.5.175",
|
||||
"fe80::40d7:d0ff:fe66:f55",
|
||||
"fe81::40d8:d0ff:fe66:f55",
|
||||
"fe82::c2c:6bdf:3307:dce0",
|
||||
"fe83::5069:fcd5:e31c:7059",
|
||||
"fe80::ce81:b2c:bd2c:69e",
|
||||
"fe80::febc:bbc1:c517:827b",
|
||||
"fe80::6d09:bee6:55a5:539d",
|
||||
"fe80::c920:752e:1e0e:edc9",
|
||||
"fe80::a4a:ca38:761f:83e2"
|
||||
],
|
||||
"mac" : [
|
||||
"ad:df:48:00:11:22",
|
||||
"a6:86:e7:ae:5a:b6",
|
||||
"a9:83:e7:ae:5a:b6",
|
||||
"43:d8:d0:66:0f:55",
|
||||
"42:d8:d0:66:0f:57",
|
||||
"82:70:c7:c2:3c:01",
|
||||
"82:70:c6:c2:4c:00",
|
||||
"82:76:a6:c2:3c:05",
|
||||
"82:70:c6:b2:3c:04",
|
||||
"82:71:a6:c2:3c:01"
|
||||
],
|
||||
"name" : "siem-kibana"
|
||||
},
|
||||
"agent" : {
|
||||
"type" : "winlogbeat",
|
||||
"version" : "8.1.0",
|
||||
"ephemeral_id" : "f6df090f-656a-4a79-a6a1-0c8671c9752d",
|
||||
"id" : "0ebd469b-c164-4734-00e6-96d018098dc7",
|
||||
"name" : "test.local"
|
||||
},
|
||||
"event" : {
|
||||
"module" : "sysmon",
|
||||
"dataset" : "process",
|
||||
"kind" : "event",
|
||||
"category" : [
|
||||
"process"
|
||||
],
|
||||
"type" : [
|
||||
"start"
|
||||
],
|
||||
"action" : "process_started"
|
||||
},
|
||||
"destination": {
|
||||
"port": 80
|
||||
},
|
||||
"process" : {
|
||||
"start" : "2022-03-04T19:41:34.902Z",
|
||||
"pid" : 30884,
|
||||
"working_directory" : "/Users/test/security_solution",
|
||||
"hash" : {
|
||||
"sha1" : "ae2d46c38fa207efbea5fcecd6294eebbf5af00f"
|
||||
},
|
||||
"parent" : {
|
||||
"pid" : 777
|
||||
},
|
||||
"executable" : "/bin/zsh",
|
||||
"name" : "zsh",
|
||||
"args" : [
|
||||
"-zsh"
|
||||
],
|
||||
"entity_id" : "q6pltOhTWlQx3BCE",
|
||||
"entry_leader": {
|
||||
"entity_id": "q6pltOhTWlQx3BCE",
|
||||
"name": "fake entry",
|
||||
"pid": 2342342
|
||||
}
|
||||
},
|
||||
"message" : "Process zsh (PID: 27884) by user test STARTED",
|
||||
"user" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"name" : "staff",
|
||||
"id" : "20"
|
||||
},
|
||||
"effective" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"id" : "20"
|
||||
}
|
||||
},
|
||||
"saved" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"id" : "20"
|
||||
}
|
||||
},
|
||||
"name" : "test"
|
||||
},
|
||||
"service" : {
|
||||
"type" : "system"
|
||||
},
|
||||
"ecs" : {
|
||||
"version" : "8.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue