Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Exceptions] - Exception Modal Part I #70639

Merged
merged 39 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7754af5
adds 2 menu items to alert page, progress on exception modal
peluja1012 Jun 15, 2020
effa4bd
adds enriching
dplumlee Jun 18, 2020
3f0ec65
remove unused useExceptionList()
peluja1012 Jun 18, 2020
b98db45
implements some types
dplumlee Jun 19, 2020
82cf87c
move add exception modal files
peluja1012 Jun 19, 2020
03435bc
Exception builder changes to support latest schema
peluja1012 Jun 22, 2020
baa5bfc
Changes to lists plugin schemas and fix api bug
peluja1012 Jun 22, 2020
b4c6a64
Adding and editing exceptions working
peluja1012 Jun 22, 2020
3e11a91
fixes duplicate types
dplumlee Jun 23, 2020
f434788
updates os tag input
dplumlee Jun 25, 2020
021744b
fixes comment style
dplumlee Jun 29, 2020
4480311
removes checkbox programatically
dplumlee Jun 29, 2020
e93e3b3
grahpql updates to expose exceptions_list
peluja1012 Jun 29, 2020
fb68887
Add fetch_or_create_exception_list hook
peluja1012 Jun 29, 2020
59a7ddd
fixes data population
dplumlee Jun 30, 2020
69e5ece
refactor use_add_exception hook, add tests
peluja1012 Jul 1, 2020
b30412a
fix rebase issues, pending updates to edit modal
peluja1012 Jul 1, 2020
d69be16
fix edit modal and default endpoint exceptions
peluja1012 Jul 1, 2020
0a6453e
adds second checkbox
dplumlee Jul 2, 2020
05e6688
adds signal index stuff
dplumlee Jul 2, 2020
17a2fe7
switches boolean logic
dplumlee Jul 2, 2020
d0fb00d
fix some type errors
peluja1012 Jul 2, 2020
d56903a
remove unnecesary code
peluja1012 Jul 2, 2020
08aefd2
fixes checkbox logic in edit modal
dplumlee Jul 2, 2020
7e7e515
fixes recursive prop passing
dplumlee Jul 2, 2020
c48087e
addresses comments/fixes types
dplumlee Jul 2, 2020
1de1ac6
Revert schema type changes
peluja1012 Jul 2, 2020
a1c0fc3
type fixes
dplumlee Jul 2, 2020
e941f6c
fixes regular exception modal
dplumlee Jul 2, 2020
2cf0d15
fix more type errors, remove console log
peluja1012 Jul 3, 2020
c27cfd7
fix tests
peluja1012 Jul 3, 2020
ab22755
move add exception hook, lint
peluja1012 Jul 3, 2020
5acc645
close alert checkbox closes alert
peluja1012 Jul 4, 2020
0ef4817
address PR comments
peluja1012 Jul 6, 2020
6feff18
add type to patch rule call, fix ts errors
peluja1012 Jul 7, 2020
1b27b62
fix lint
peluja1012 Jul 7, 2020
b49a1b0
fix merge problems after conflict
peluja1012 Jul 7, 2020
0fe335b
Address PR comments
peluja1012 Jul 7, 2020
f03a20f
undo graphql type change
peluja1012 Jul 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions x-pack/plugins/lists/public/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export { useFindLists } from './lists/hooks/use_find_lists';
export { useImportList } from './lists/hooks/use_import_list';
export { useDeleteList } from './lists/hooks/use_delete_list';
export { useExportList } from './lists/hooks/use_export_list';
export {
addExceptionListItem,
updateExceptionListItem,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we need to export these as they are all available in useApi

fetchExceptionListById,
addExceptionList,
} from './exceptions/api';
export {
ExceptionList,
ExceptionIdentifiers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import {
UpdateTimelineLoading,
} from './types';
import { Ecs } from '../../../graphql/types';
import { AddExceptionOnClick } from '../../../common/components/exceptions/add_exception_modal';
import { getMappedNonEcsValue } from '../../../common/components/exceptions/helpers';

export const buildAlertStatusFilter = (status: Status): Filter[] => [
{
Expand Down Expand Up @@ -172,6 +174,13 @@ export const requiredFieldsForActions = [
'signal.rule.query',
'signal.rule.to',
'signal.rule.id',

// Endpoint exception fields
'file.path',
'file.Ext.code_signature.subject_name',
'file.Ext.code_signature.trusted',
'file.hash.sha1',
'host.os.family',
];

interface AlertActionArgs {
Expand All @@ -188,6 +197,12 @@ interface AlertActionArgs {
status: Status;
timelineId: string;
updateTimelineIsLoading: UpdateTimelineLoading;
openAddExceptionModal: ({
exceptionListType,
alertData,
ruleName,
ruleId,
}: AddExceptionOnClick) => void;
}

export const getAlertActions = ({
Expand All @@ -204,6 +219,7 @@ export const getAlertActions = ({
status,
timelineId,
updateTimelineIsLoading,
openAddExceptionModal,
}: AlertActionArgs): TimelineRowAction[] => {
const openAlertActionComponent: TimelineRowAction = {
ariaLabel: 'Open alert',
Expand Down Expand Up @@ -289,5 +305,52 @@ export const getAlertActions = ({
...(FILTER_OPEN !== status ? [openAlertActionComponent] : []),
...(FILTER_CLOSED !== status ? [closeAlertActionComponent] : []),
...(FILTER_IN_PROGRESS !== status ? [inProgressAlertActionComponent] : []),
// TODO: disable this option if the alert is not an Endpoint alert
{
onClick: ({ ecsData, data }: TimelineRowActionOnClick) => {
const ruleNameValue = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
const ruleId = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
if (ruleId !== undefined && ruleId.length > 0) {
openAddExceptionModal({
ruleName: ruleNameValue ? ruleNameValue[0] : '',
ruleId: ruleId[0],
exceptionListType: 'endpoint',
alertData: {
ecsData,
nonEcsData: data,
},
});
}
},
id: 'addEndpointException',
isActionDisabled: () => !canUserCRUD || !hasIndexWrite,
dataTestSubj: 'add-endpoint-exception-menu-item',
ariaLabel: 'Add Endpoint Exception',
content: <EuiText size="m">{i18n.ACTION_ADD_ENDPOINT_EXCEPTION}</EuiText>,
displayType: 'contextMenu',
},
{
onClick: ({ ecsData, data }: TimelineRowActionOnClick) => {
const ruleNameValue = getMappedNonEcsValue({ data, fieldName: 'signal.rule.name' });
const ruleId = getMappedNonEcsValue({ data, fieldName: 'signal.rule.id' });
if (ruleId !== undefined && ruleId.length > 0) {
openAddExceptionModal({
ruleName: ruleNameValue ? ruleNameValue[0] : '',
ruleId: ruleId[0],
exceptionListType: 'detection',
alertData: {
ecsData,
nonEcsData: data,
},
});
}
},
id: 'addException',
isActionDisabled: () => !canUserCRUD || !hasIndexWrite,
dataTestSubj: 'add-exception-menu-item',
ariaLabel: 'Add Exception',
content: <EuiText size="m">{i18n.ACTION_ADD_EXCEPTION}</EuiText>,
displayType: 'contextMenu',
},
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ import {
} from '../../../common/components/toasters';
import { Ecs } from '../../../graphql/types';
import { getInvestigateInResolverAction } from '../../../timelines/components/timeline/body/helpers';
import {
AddExceptionModal,
AddExceptionOnClick,
} from '../../../common/components/exceptions/add_exception_modal';

interface OwnProps {
timelineId: TimelineIdLiteral;
Expand All @@ -64,6 +68,13 @@ interface OwnProps {

type AlertsTableComponentProps = OwnProps & PropsFromRedux;

const addExceptionModalInitialState: AddExceptionOnClick = {
ruleName: '',
ruleId: '',
exceptionListType: 'detection',
alertData: undefined,
};

export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
timelineId,
canUserCRUD,
Expand Down Expand Up @@ -92,6 +103,10 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({

const [showClearSelectionAction, setShowClearSelectionAction] = useState(false);
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);
const [shouldShowAddExceptionModal, setShouldShowAddExceptionModal] = useState(false);
const [addExceptionModalState, setAddExceptionModalState] = useState<AddExceptionOnClick>(
addExceptionModalInitialState
);
const [{ browserFields, indexPatterns }] = useFetchIndexPatterns(
signalsIndex !== '' ? [signalsIndex] : []
);
Expand Down Expand Up @@ -192,6 +207,21 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
[dispatchToaster]
);

const openAddExceptionModalCallback = useCallback(
({ ruleName, ruleId, exceptionListType, alertData }: AddExceptionOnClick) => {
if (alertData !== null && alertData !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use != here to check for both null and undefined

setShouldShowAddExceptionModal(true);
setAddExceptionModalState({
ruleName,
ruleId,
exceptionListType,
alertData,
});
}
},
[setShouldShowAddExceptionModal, setAddExceptionModalState]
);

// Catches state change isSelectAllChecked->false upon user selection change to reset utility bar
useEffect(() => {
if (!isSelectAllChecked) {
Expand Down Expand Up @@ -306,6 +336,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
status: filterGroup,
timelineId,
updateTimelineIsLoading,
openAddExceptionModal: openAddExceptionModalCallback,
}),
[
apolloClient,
Expand All @@ -320,6 +351,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
updateTimelineIsLoading,
onAlertStatusUpdateSuccess,
onAlertStatusUpdateFailure,
openAddExceptionModalCallback,
]
);
const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]);
Expand Down Expand Up @@ -360,6 +392,19 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
[onFilterGroupChangedCallback]
);

const closeAddExceptionModal = useCallback(() => {
setShouldShowAddExceptionModal(false);
setAddExceptionModalState(addExceptionModalInitialState);
}, [setShouldShowAddExceptionModal, setAddExceptionModalState]);

const onAddExceptionCancel = useCallback(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could onAddExceptionCancel and onAddExceptionConfirm be combined into one function like onCloseAddExceptionModal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will look into this in the follow up PR

closeAddExceptionModal();
}, [closeAddExceptionModal]);

const onAddExceptionConfirm = useCallback(() => {
closeAddExceptionModal();
}, [closeAddExceptionModal]);

if (loading || isEmpty(signalsIndex)) {
return (
<EuiPanel>
Expand All @@ -370,16 +415,28 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
}

return (
<StatefulEventsViewer
defaultIndices={defaultIndices}
pageFilters={defaultFiltersMemo}
defaultModel={alertsDefaultModel}
end={to}
headerFilterGroup={headerFilterGroup}
id={timelineId}
start={from}
utilityBar={utilityBarCallback}
/>
<>
<StatefulEventsViewer
defaultIndices={defaultIndices}
pageFilters={defaultFiltersMemo}
defaultModel={alertsDefaultModel}
end={to}
headerFilterGroup={headerFilterGroup}
id={timelineId}
start={from}
utilityBar={utilityBarCallback}
/>
{shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as before, using != null will catch both undefined and null

<AddExceptionModal
ruleName={addExceptionModalState.ruleName}
ruleId={addExceptionModalState.ruleId}
exceptionListType={addExceptionModalState.exceptionListType}
alertData={addExceptionModalState.alertData}
onCancel={onAddExceptionCancel}
onConfirm={onAddExceptionConfirm}
/>
)}
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ export const ACTION_INVESTIGATE_IN_TIMELINE = i18n.translate(
}
);

export const ACTION_ADD_EXCEPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.actions.addException',
{
defaultMessage: 'Add exception',
}
);

export const ACTION_ADD_ENDPOINT_EXCEPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.actions.addEndpointException',
{
defaultMessage: 'Add Endpoint exception',
}
);

export const CLOSED_ALERT_SUCCESS_TOAST = (totalAlerts: number) =>
i18n.translate('xpack.securitySolution.detectionEngine.alerts.closedAlertSuccessToastMessage', {
values: { totalAlerts },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import {
AddRulesProps,
PatchRuleProps,
NewRule,
PrePackagedRulesStatusResponse,
BasicFetchProps,
Expand All @@ -20,6 +21,9 @@ import { ruleMock, savedRuleMock, rulesMock } from '../mock';
export const addRule = async ({ rule, signal }: AddRulesProps): Promise<NewRule> =>
Promise.resolve(ruleMock);

export const patchRule = async ({ ruleProperties, signal }: PatchRuleProps): Promise<NewRule> =>
Promise.resolve(ruleMock);

export const getPrePackagedRulesStatus = async ({
signal,
}: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
ImportDataResponse,
PrePackagedRulesStatusResponse,
BulkRuleResponse,
PatchRuleProps,
} from './types';
import { KibanaServices } from '../../../../common/lib/kibana';
import * as i18n from '../../../pages/detection_engine/rules/translations';
Expand All @@ -47,6 +48,21 @@ export const addRule = async ({ rule, signal }: AddRulesProps): Promise<NewRule>
signal,
});

/**
* Patch provided Rule
*
* @param ruleProperties to patch
* @param signal to cancel request
*
* @throws An error if response is not OK
*/
export const patchRule = async ({ ruleProperties, signal }: PatchRuleProps): Promise<NewRule> =>
KibanaServices.get().http.fetch<NewRule>(DETECTION_ENGINE_RULES_URL, {
method: 'PATCH',
body: JSON.stringify(ruleProperties),
signal,
});

/**
* Fetches all rules from the Detection Engine API
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
listArray,
listArrayOrUndefined,
} from '../../../../../common/detection_engine/schemas/types';
import { PatchRulesSchema } from '../../../../../common/detection_engine/schemas/request/patch_rules_schema';

/**
* Params is an "record", since it is a type of AlertActionParams which is action templates.
Expand Down Expand Up @@ -80,6 +81,11 @@ export interface AddRulesProps {
signal: AbortSignal;
}

export interface PatchRuleProps {
ruleProperties: PatchRulesSchema;
signal: AbortSignal;
}

const MetaRule = t.intersection([
t.type({
from: t.string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ export const RuleDetailsPageComponent: FC<PropsFromRedux> = ({
{ruleDetailTab === RuleDetailTabs.exceptions && (
<ExceptionsViewer
ruleId={ruleId ?? ''}
ruleName={rule?.name ?? ''}
availableListTypes={exceptionLists.allowedExceptionListTypes}
commentsAccordionId={'ruleDetailsTabExceptions'}
exceptionListsMeta={exceptionLists.lists}
Expand Down
Loading