Skip to content

Commit aabb445

Browse files
dplumleePedro Jaramillo
authored andcommitted
[Security Solution][Exceptions] - Exceptions modal pt 2 (#70886)
* makes comment updates * adds tests * adds back non ecs data to timeline * comments * fixes jest tests * fixes typo
1 parent 1d8e67e commit aabb445

11 files changed

Lines changed: 429 additions & 79 deletions

File tree

x-pack/plugins/security_solution/public/alerts/components/alerts_table/default_config.tsx

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
SetEventsLoadingProps,
3737
UpdateTimelineLoading,
3838
} from './types';
39-
import { Ecs } from '../../../graphql/types';
39+
import { Ecs, TimelineNonEcsData } from '../../../graphql/types';
4040
import { AddExceptionOnClick } from '../../../common/components/exceptions/add_exception_modal';
4141
import { getMappedNonEcsValue } from '../../../common/components/exceptions/helpers';
4242

@@ -174,6 +174,8 @@ export const requiredFieldsForActions = [
174174
'signal.rule.query',
175175
'signal.rule.to',
176176
'signal.rule.id',
177+
'signal.original_event.kind',
178+
'signal.original_event.module',
177179

178180
// Endpoint exception fields
179181
'file.path',
@@ -189,6 +191,7 @@ interface AlertActionArgs {
189191
createTimeline: CreateTimeline;
190192
dispatch: Dispatch;
191193
ecsRowData: Ecs;
194+
nonEcsRowData: TimelineNonEcsData[];
192195
hasIndexWrite: boolean;
193196
onAlertStatusUpdateFailure: (status: Status, error: Error) => void;
194197
onAlertStatusUpdateSuccess: (count: number, status: Status) => void;
@@ -211,6 +214,7 @@ export const getAlertActions = ({
211214
createTimeline,
212215
dispatch,
213216
ecsRowData,
217+
nonEcsRowData,
214218
hasIndexWrite,
215219
onAlertStatusUpdateFailure,
216220
onAlertStatusUpdateSuccess,
@@ -281,6 +285,18 @@ export const getAlertActions = ({
281285
width: DEFAULT_ICON_BUTTON_WIDTH,
282286
};
283287

288+
const isEndpointAlert = () => {
289+
const [module] = getMappedNonEcsValue({
290+
data: nonEcsRowData,
291+
fieldName: 'signal.original_event.module',
292+
});
293+
const [kind] = getMappedNonEcsValue({
294+
data: nonEcsRowData,
295+
fieldName: 'signal.original_event.kind',
296+
});
297+
return module === 'endpoint' && kind === 'alert';
298+
};
299+
284300
return [
285301
{
286302
...getInvestigateInResolverAction({ dispatch, timelineId }),
@@ -305,15 +321,14 @@ export const getAlertActions = ({
305321
...(FILTER_OPEN !== status ? [openAlertActionComponent] : []),
306322
...(FILTER_CLOSED !== status ? [closeAlertActionComponent] : []),
307323
...(FILTER_IN_PROGRESS !== status ? [inProgressAlertActionComponent] : []),
308-
// TODO: disable this option if the alert is not an Endpoint alert
309324
{
310325
onClick: ({ ecsData, data }: TimelineRowActionOnClick) => {
311-
const ruleNameValue = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
312-
const ruleId = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
313-
if (ruleId !== undefined && ruleId.length > 0) {
326+
const [ruleName] = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
327+
const [ruleId] = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
328+
if (ruleId !== undefined) {
314329
openAddExceptionModal({
315-
ruleName: ruleNameValue ? ruleNameValue[0] : '',
316-
ruleId: ruleId[0],
330+
ruleName: ruleName ?? '',
331+
ruleId,
317332
exceptionListType: 'endpoint',
318333
alertData: {
319334
ecsData,
@@ -323,20 +338,20 @@ export const getAlertActions = ({
323338
}
324339
},
325340
id: 'addEndpointException',
326-
isActionDisabled: () => !canUserCRUD || !hasIndexWrite,
341+
isActionDisabled: () => !canUserCRUD || !hasIndexWrite || !isEndpointAlert(),
327342
dataTestSubj: 'add-endpoint-exception-menu-item',
328343
ariaLabel: 'Add Endpoint Exception',
329344
content: <EuiText size="m">{i18n.ACTION_ADD_ENDPOINT_EXCEPTION}</EuiText>,
330345
displayType: 'contextMenu',
331346
},
332347
{
333348
onClick: ({ ecsData, data }: TimelineRowActionOnClick) => {
334-
const ruleNameValue = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
335-
const ruleId = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
336-
if (ruleId !== undefined && ruleId.length > 0) {
349+
const [ruleName] = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
350+
const [ruleId] = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
351+
if (ruleId !== undefined) {
337352
openAddExceptionModal({
338-
ruleName: ruleNameValue ? ruleNameValue[0] : '',
339-
ruleId: ruleId[0],
353+
ruleName: ruleName ?? '',
354+
ruleId,
340355
exceptionListType: 'detection',
341356
alertData: {
342357
ecsData,

x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import { inputsSelectors, State, inputsModel } from '../../../common/store';
2222
import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline';
2323
import { TimelineModel } from '../../../timelines/store/timeline/model';
2424
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
25-
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
25+
import {
26+
useManageTimeline,
27+
TimelineRowActionArgs,
28+
} from '../../../timelines/components/manage_timeline';
2629
import { useApolloClient } from '../../../common/utils/apollo_context';
2730

2831
import { updateAlertStatusAction } from './actions';
@@ -48,7 +51,6 @@ import {
4851
displaySuccessToast,
4952
displayErrorToast,
5053
} from '../../../common/components/toasters';
51-
import { Ecs } from '../../../graphql/types';
5254
import { getInvestigateInResolverAction } from '../../../timelines/components/timeline/body/helpers';
5355
import {
5456
AddExceptionModal,
@@ -321,12 +323,13 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
321323

322324
// Send to Timeline / Update Alert Status Actions for each table row
323325
const additionalActions = useMemo(
324-
() => (ecsRowData: Ecs) =>
326+
() => ({ ecsData, nonEcsData }: TimelineRowActionArgs) =>
325327
getAlertActions({
326328
apolloClient,
327329
canUserCRUD,
328330
createTimeline: createTimelineCallback,
329-
ecsRowData,
331+
ecsRowData: ecsData,
332+
nonEcsRowData: nonEcsData,
330333
dispatch,
331334
hasIndexWrite,
332335
onAlertStatusUpdateFailure,
@@ -401,9 +404,12 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
401404
closeAddExceptionModal();
402405
}, [closeAddExceptionModal]);
403406

404-
const onAddExceptionConfirm = useCallback(() => {
405-
closeAddExceptionModal();
406-
}, [closeAddExceptionModal]);
407+
const onAddExceptionConfirm = useCallback(
408+
(didCloseAlert: boolean) => {
409+
closeAddExceptionModal();
410+
},
411+
[closeAddExceptionModal]
412+
);
407413

408414
if (loading || isEmpty(signalsIndex)) {
409415
return (

x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
defaultEndpointExceptionItems,
4444
entryHasListType,
4545
entryHasNonEcsType,
46+
getMappedNonEcsValue,
4647
} from '../helpers';
4748
import { useFetchIndexPatterns } from '../../../../alerts/containers/detection_engine/rules';
4849

@@ -65,7 +66,7 @@ interface AddExceptionModalProps {
6566
nonEcsData: TimelineNonEcsData[];
6667
};
6768
onCancel: () => void;
68-
onConfirm: () => void;
69+
onConfirm: (didCloseAlert: boolean) => void;
6970
}
7071

7172
const Modal = styled(EuiModal)`
@@ -130,8 +131,8 @@ export const AddExceptionModal = memo(function AddExceptionModal({
130131
);
131132
const onSuccess = useCallback(() => {
132133
displaySuccessToast(i18n.ADD_EXCEPTION_SUCCESS, dispatchToaster);
133-
onConfirm();
134-
}, [dispatchToaster, onConfirm]);
134+
onConfirm(shouldCloseAlert);
135+
}, [dispatchToaster, onConfirm, shouldCloseAlert]);
135136

136137
const [{ isLoading: addExceptionIsLoading }, addOrUpdateExceptionItems] = useAddOrUpdateException(
137138
{
@@ -193,6 +194,12 @@ export const AddExceptionModal = memo(function AddExceptionModal({
193194
indexPatterns,
194195
]);
195196

197+
useEffect(() => {
198+
if (shouldDisableBulkClose === true) {
199+
setShouldBulkCloseAlert(false);
200+
}
201+
}, [shouldDisableBulkClose]);
202+
196203
const onCommentChange = useCallback(
197204
(value: string) => {
198205
setComment(value);
@@ -214,18 +221,33 @@ export const AddExceptionModal = memo(function AddExceptionModal({
214221
[setShouldBulkCloseAlert]
215222
);
216223

224+
const retrieveAlertOsTypes = useCallback(() => {
225+
const osDefaults = ['windows', 'macos', 'linux'];
226+
if (alertData) {
227+
const osTypes = getMappedNonEcsValue({
228+
data: alertData.nonEcsData,
229+
fieldName: 'host.os.family',
230+
});
231+
if (osTypes.length === 0) {
232+
return osDefaults;
233+
}
234+
return osTypes;
235+
}
236+
return osDefaults;
237+
}, [alertData]);
238+
217239
const enrichExceptionItems = useCallback(() => {
218240
let enriched: Array<ExceptionListItemSchema | CreateExceptionListItemSchema> = [];
219241
enriched =
220242
comment !== ''
221243
? enrichExceptionItemsWithComments(exceptionItemsToAdd, [{ comment }])
222244
: exceptionItemsToAdd;
223245
if (exceptionListType === 'endpoint') {
224-
const osTypes = alertData ? ['windows'] : ['windows', 'macos', 'linux'];
246+
const osTypes = retrieveAlertOsTypes();
225247
enriched = enrichExceptionItemsWithOS(enriched, osTypes);
226248
}
227249
return enriched;
228-
}, [comment, exceptionItemsToAdd, exceptionListType, alertData]);
250+
}, [comment, exceptionItemsToAdd, exceptionListType, retrieveAlertOsTypes]);
229251

230252
const onAddExceptionConfirm = useCallback(() => {
231253
if (addOrUpdateExceptionItems !== null) {

x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { AddExceptionComments } from '../add_exception_comments';
3737
import {
3838
enrichExceptionItemsWithComments,
3939
enrichExceptionItemsWithOS,
40-
getOsTagValues,
40+
getOperatingSystems,
4141
entryHasListType,
4242
entryHasNonEcsType,
4343
} from '../helpers';
@@ -135,6 +135,12 @@ export const EditExceptionModal = memo(function EditExceptionModal({
135135
indexPatterns,
136136
]);
137137

138+
useEffect(() => {
139+
if (shouldDisableBulkClose === true) {
140+
setShouldBulkCloseAlert(false);
141+
}
142+
}, [shouldDisableBulkClose]);
143+
138144
const handleBuilderOnChange = useCallback(
139145
({
140146
exceptionItems,
@@ -167,7 +173,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({
167173
...(comment !== '' ? [{ comment }] : []),
168174
]);
169175
if (exceptionListType === 'endpoint') {
170-
const osTypes = exceptionItem._tags ? getOsTagValues(exceptionItem._tags) : ['windows'];
176+
const osTypes = exceptionItem._tags ? getOperatingSystems(exceptionItem._tags) : [];
171177
enriched = enrichExceptionItemsWithOS(enriched, osTypes);
172178
}
173179
return enriched;
@@ -199,6 +205,8 @@ export const EditExceptionModal = memo(function EditExceptionModal({
199205
{!isSignalIndexLoading && (
200206
<>
201207
<ModalBodySection className="builder-section">
208+
<EuiText>{i18n.EXCEPTION_BUILDER_INFO}</EuiText>
209+
<EuiSpacer />
202210
<ExceptionBuilder
203211
exceptionListItems={[exceptionItem]}
204212
listType={exceptionListType}

x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const ENDPOINT_QUARANTINE_TEXT = i18n.translate(
4747
);
4848

4949
export const EXCEPTION_BUILDER_INFO = i18n.translate(
50-
'xpack.securitySolution.exceptions.addException.infoLabel',
50+
'xpack.securitySolution.exceptions.editException.infoLabel',
5151
{
5252
defaultMessage: "Alerts are generated when the rule's conditions are met, except when:",
5353
}

0 commit comments

Comments
 (0)