mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Security Solution][Artifacts][Trusted Apps] Wildcard warning with IS operator for trusted apps creation/editing (#175356)
## Summary - [x] Adds updated warning messaging for trusted apps entries that use wildcards `*?` with the "IS" operator - [x] Three different warnings: callout, individual entry item warnings and a final confirmation modal when the user tries to add a trusted app with ineffective IS / wildcard combination etnry. - [x] Unit tests # Screenshots <img width="829" alt="image" src="c7beec62
-a249-4535-ac0b-34f9be57f542"> <img width="1649" alt="image" src="22f38f1b
-7e6b-4b69-8d03-4d74d8674fa6"> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
03a5eb6635
commit
9a73eb4d3d
12 changed files with 330 additions and 11 deletions
|
@ -17,3 +17,4 @@ export * from './src/types';
|
|||
export * from './src/list_header';
|
||||
export * from './src/header_menu';
|
||||
export * from './src/generate_linked_rules_menu_item';
|
||||
export * from './src/wildcard_with_wrong_operator_callout';
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
|
||||
export const WildCardWithWrongOperatorCallout = () => {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('exceptionList-components.wildcardWithWrongOperatorCallout.title', {
|
||||
defaultMessage: 'Please review your entries',
|
||||
})}
|
||||
iconType="warning"
|
||||
color="warning"
|
||||
size="s"
|
||||
data-test-subj="wildcardWithWrongOperatorCallout"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="exceptionList-components.wildcardWithWrongOperatorCallout.body"
|
||||
defaultMessage="Using a '*' or a '?' in the value with the 'IS' operator can make the entry ineffective. {operator} to '{matches}' to ensure wildcards run properly."
|
||||
values={{
|
||||
operator: (
|
||||
<strong>
|
||||
{i18n.translate(
|
||||
'exceptionList-components.wildcardWithWrongOperatorCallout.changeTheOperator',
|
||||
{ defaultMessage: 'Change the operator' }
|
||||
)}
|
||||
</strong>
|
||||
),
|
||||
matches: (
|
||||
<strong>
|
||||
{i18n.translate(
|
||||
'exceptionList-components.wildcardWithWrongOperatorCallout.matches',
|
||||
{ defaultMessage: 'matches' }
|
||||
)}
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
"@kbn/securitysolution-autocomplete",
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/i18n",
|
||||
"@kbn/i18n-react",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
hasSimpleExecutableName,
|
||||
OperatingSystem,
|
||||
ConditionEntryField,
|
||||
hasWildcardAndInvalidOperator,
|
||||
validatePotentialWildcardInput,
|
||||
validateFilePathInput,
|
||||
validateWildcardInput,
|
||||
|
@ -128,6 +129,21 @@ describe('validateFilePathInput', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Wildcard and invalid operator', () => {
|
||||
it('should return TRUE when operator is not "WILDCARD" and value contains a wildcard', () => {
|
||||
expect(hasWildcardAndInvalidOperator({ operator: 'match', value: 'asdf*' })).toEqual(true);
|
||||
});
|
||||
it('should return FALSE when operator is not "WILDCARD" and value does not contain a wildcard', () => {
|
||||
expect(hasWildcardAndInvalidOperator({ operator: 'match', value: 'asdf' })).toEqual(false);
|
||||
});
|
||||
it('should return FALSE when operator is "WILDCARD" and value contains a wildcard', () => {
|
||||
expect(hasWildcardAndInvalidOperator({ operator: 'wildcard', value: 'asdf*' })).toEqual(false);
|
||||
});
|
||||
it('should return FALSE when operator is "WILDCARD" and value does not contain a wildcard', () => {
|
||||
expect(hasWildcardAndInvalidOperator({ operator: 'wildcard', value: 'asdf' })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('No Warnings', () => {
|
||||
it('should not show warnings on non path entries ', () => {
|
||||
expect(
|
||||
|
|
|
@ -106,6 +106,20 @@ export const validateWildcardInput = (value?: string): string | undefined => {
|
|||
}
|
||||
};
|
||||
|
||||
export const hasWildcardAndInvalidOperator = ({
|
||||
operator,
|
||||
value,
|
||||
}: {
|
||||
operator: EntryTypes | TrustedAppEntryTypes;
|
||||
value: string;
|
||||
}): boolean => {
|
||||
if (operator !== 'wildcard' && validateWildcardInput(value)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const hasSimpleExecutableName = ({
|
||||
os,
|
||||
type,
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiModal,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiModalHeader,
|
||||
EuiModalHeaderTitle,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { useTestIdGenerator } from '../../../hooks/use_test_id_generator';
|
||||
|
||||
interface ConfirmArtifactModalProps {
|
||||
title: string;
|
||||
body: string;
|
||||
confirmButton: string;
|
||||
cancelButton: string;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
'data-test-subj'?: string;
|
||||
}
|
||||
|
||||
export const ArtifactConfirmModal = memo<ConfirmArtifactModalProps>(
|
||||
({
|
||||
title,
|
||||
body,
|
||||
confirmButton,
|
||||
cancelButton,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
'data-test-subj': dataTestSubj,
|
||||
}) => {
|
||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||
|
||||
return (
|
||||
<EuiModal onClose={onCancel} data-test-subj={dataTestSubj}>
|
||||
<EuiModalHeader data-test-subj={getTestId('header')}>
|
||||
<EuiModalHeaderTitle>{title}</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiModalBody data-test-subj={getTestId('body')}>
|
||||
<EuiText>
|
||||
<p>{body}</p>
|
||||
</EuiText>
|
||||
</EuiModalBody>
|
||||
|
||||
<EuiModalFooter>
|
||||
<EuiButtonEmpty onClick={onCancel} data-test-subj={getTestId('cancelButton')}>
|
||||
{cancelButton}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiButton fill onClick={onSuccess} data-test-subj={getTestId('submitButton')}>
|
||||
{confirmButton}
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModal>
|
||||
);
|
||||
}
|
||||
);
|
||||
ArtifactConfirmModal.displayName = 'ArtifactConfirmModal';
|
|
@ -324,6 +324,51 @@ describe('When the flyout is opened in the ArtifactListPage component', () => {
|
|||
expect(location.search).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and there is a confirmModal', () => {
|
||||
beforeEach(async () => {
|
||||
const _renderAndWaitForFlyout = render;
|
||||
|
||||
// Override renderAndWaitForFlyout to also set the form data as "valid"
|
||||
render = async (...props) => {
|
||||
await _renderAndWaitForFlyout(...props);
|
||||
|
||||
act(() => {
|
||||
const lastProps = getLastFormComponentProps();
|
||||
lastProps.onChange({
|
||||
item: { ...lastProps.item, name: 'some name' },
|
||||
isValid: true,
|
||||
confirmModalLabels: {
|
||||
title: 'title',
|
||||
body: 'body',
|
||||
confirmButton: 'add',
|
||||
cancelButton: 'cancel',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return renderResult;
|
||||
};
|
||||
});
|
||||
|
||||
it('should show the warning modal', async () => {
|
||||
await render();
|
||||
act(() => {
|
||||
userEvent.click(renderResult.getByTestId('testPage-flyout-submitButton'));
|
||||
});
|
||||
expect(renderResult.getByTestId('artifactConfirmModal')).toBeTruthy();
|
||||
expect(renderResult.getByTestId('artifactConfirmModal-header').textContent).toEqual(
|
||||
'title'
|
||||
);
|
||||
expect(renderResult.getByTestId('artifactConfirmModal-body').textContent).toEqual('body');
|
||||
expect(renderResult.getByTestId('artifactConfirmModal-submitButton').textContent).toEqual(
|
||||
'add'
|
||||
);
|
||||
expect(renderResult.getByTestId('artifactConfirmModal-cancelButton').textContent).toEqual(
|
||||
'cancel'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and in Edit mode', () => {
|
||||
|
|
|
@ -42,6 +42,7 @@ import { useWithArtifactSubmitData } from '../hooks/use_with_artifact_submit_dat
|
|||
import { useIsArtifactAllowedPerPolicyUsage } from '../hooks/use_is_artifact_allowed_per_policy_usage';
|
||||
import { useGetArtifact } from '../../../hooks/artifacts';
|
||||
import type { PolicyData } from '../../../../../common/endpoint/types';
|
||||
import { ArtifactConfirmModal } from './artifact_confirm_modal';
|
||||
|
||||
export const ARTIFACT_FLYOUT_LABELS = Object.freeze({
|
||||
flyoutEditTitle: i18n.translate('xpack.securitySolution.artifactListPage.flyoutEditTitle', {
|
||||
|
@ -207,11 +208,12 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
..._labels,
|
||||
};
|
||||
}, [_labels]);
|
||||
// TODO:PT Refactor internal/external state into the `useEithArtifactSucmitData()` hook
|
||||
// TODO:PT Refactor internal/external state into the `useWithArtifactSubmitData()` hook
|
||||
const [externalIsSubmittingData, setExternalIsSubmittingData] = useState<boolean>(false);
|
||||
const [externalSubmitHandlerError, setExternalSubmitHandlerError] = useState<
|
||||
IHttpFetchError | undefined
|
||||
>(undefined);
|
||||
const [showConfirmModal, setShowConfirmModal] = useState<boolean>(false);
|
||||
|
||||
const isEditFlow = urlParams.show === 'edit';
|
||||
const formMode: ArtifactFormComponentProps['mode'] = isEditFlow ? 'edit' : 'create';
|
||||
|
@ -270,11 +272,12 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
}, [isSubmittingData, onClose, setUrlParams, urlParams]);
|
||||
|
||||
const handleFormComponentOnChange: ArtifactFormComponentProps['onChange'] = useCallback(
|
||||
({ item: updatedItem, isValid }) => {
|
||||
({ item: updatedItem, isValid, confirmModalLabels }) => {
|
||||
if (isMounted()) {
|
||||
setFormState({
|
||||
item: updatedItem,
|
||||
isValid,
|
||||
confirmModalLabels,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -316,10 +319,42 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
setExternalIsSubmittingData(false);
|
||||
}
|
||||
});
|
||||
} else if (formState.confirmModalLabels) {
|
||||
setShowConfirmModal(true);
|
||||
} else {
|
||||
submitData(formState.item).then(handleSuccess);
|
||||
}
|
||||
}, [formMode, formState.item, handleSuccess, isMounted, submitData, submitHandler]);
|
||||
}, [
|
||||
formMode,
|
||||
formState.item,
|
||||
formState.confirmModalLabels,
|
||||
handleSuccess,
|
||||
isMounted,
|
||||
submitData,
|
||||
submitHandler,
|
||||
]);
|
||||
|
||||
const confirmModalOnSuccess = useCallback(
|
||||
() => submitData(formState.item).then(handleSuccess),
|
||||
[submitData, formState.item, handleSuccess]
|
||||
);
|
||||
|
||||
const confirmModal = useMemo(() => {
|
||||
if (formState.confirmModalLabels) {
|
||||
const { title, body, confirmButton, cancelButton } = formState.confirmModalLabels;
|
||||
return (
|
||||
<ArtifactConfirmModal
|
||||
title={title}
|
||||
body={body}
|
||||
confirmButton={confirmButton}
|
||||
cancelButton={cancelButton}
|
||||
onSuccess={confirmModalOnSuccess}
|
||||
onCancel={() => setShowConfirmModal(false)}
|
||||
data-test-subj="artifactConfirmModal"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}, [formState, confirmModalOnSuccess]);
|
||||
|
||||
// If we don't have the actual Artifact data yet for edit (in initialization phase - ex. came in with an
|
||||
// ID in the url that was not in the list), then retrieve it now
|
||||
|
@ -342,7 +377,7 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
isMounted,
|
||||
]);
|
||||
|
||||
// If we got an error while trying ot retrieve the item for edit, then show a toast message
|
||||
// If we got an error while trying to retrieve the item for edit, then show a toast message
|
||||
useEffect(() => {
|
||||
if (isEditFlow && error) {
|
||||
toasts.addWarning(labels.flyoutEditItemLoadFailure(error?.body?.message || error.message));
|
||||
|
@ -363,7 +398,6 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
<h2>{isEditFlow ? labels.flyoutEditTitle : labels.flyoutCreateTitle}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
{!isInitializing && showExpiredLicenseBanner && (
|
||||
<EuiCallOut
|
||||
title={labels.flyoutDowngradedLicenseTitle}
|
||||
|
@ -375,7 +409,6 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
{labels.flyoutDowngradedLicenseDocsInfo(securitySolution)}
|
||||
</EuiCallOut>
|
||||
)}
|
||||
|
||||
<EuiFlyoutBody>
|
||||
{isInitializing && <ManagementPageLoader data-test-subj={getTestId('loader')} />}
|
||||
|
||||
|
@ -391,7 +424,6 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
/>
|
||||
)}
|
||||
</EuiFlyoutBody>
|
||||
|
||||
{!isInitializing && (
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
|
@ -420,6 +452,7 @@ export const ArtifactFlyout = memo<ArtifactFlyoutProps>(
|
|||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
)}
|
||||
{showConfirmModal && confirmModal}
|
||||
</EuiFlyout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,4 +40,12 @@ export interface ArtifactFormComponentProps {
|
|||
export interface ArtifactFormComponentOnChangeCallbackProps {
|
||||
isValid: boolean;
|
||||
item: ExceptionListItemSchema | CreateExceptionListItemSchema;
|
||||
confirmModalLabels?: ArtifactConfirmModalLabelProps;
|
||||
}
|
||||
|
||||
export interface ArtifactConfirmModalLabelProps {
|
||||
title: string;
|
||||
body: string;
|
||||
confirmButton: string;
|
||||
cancelButton: string;
|
||||
}
|
||||
|
|
|
@ -531,6 +531,16 @@ describe('Trusted apps form', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('and a wildcard value is used with the IS operator', () => {
|
||||
beforeEach(() => render());
|
||||
it('shows callout warning and help text warning', () => {
|
||||
setTextFieldValue(getConditionValue(getCondition()), 'somewildcard*');
|
||||
rerenderWithLatestProps();
|
||||
expect(renderResult.getByTestId('wildcardWithWrongOperatorCallout')).toBeTruthy();
|
||||
expect(renderResult.getByText(INPUT_ERRORS.wildcardWithWrongOperatorWarning(0)));
|
||||
});
|
||||
});
|
||||
|
||||
describe('and all required data passes validation', () => {
|
||||
it('should call change callback with isValid set to true and contain the new item', () => {
|
||||
const propsItem: Partial<ArtifactFormComponentProps['item']> = {
|
||||
|
|
|
@ -22,12 +22,13 @@ import {
|
|||
import type { AllConditionEntryFields, EntryTypes } from '@kbn/securitysolution-utils';
|
||||
import {
|
||||
hasSimpleExecutableName,
|
||||
hasWildcardAndInvalidOperator,
|
||||
isPathValid,
|
||||
ConditionEntryField,
|
||||
OperatingSystem,
|
||||
} from '@kbn/securitysolution-utils';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
import { WildCardWithWrongOperatorCallout } from '@kbn/securitysolution-exception-list-components';
|
||||
import type {
|
||||
TrustedAppConditionEntry,
|
||||
NewTrustedApp,
|
||||
|
@ -57,6 +58,7 @@ import {
|
|||
NAME_LABEL,
|
||||
POLICY_SELECT_DESCRIPTION,
|
||||
SELECT_OS_LABEL,
|
||||
CONFIRM_WARNING_MODAL_LABELS,
|
||||
} from '../translations';
|
||||
import { OS_TITLES } from '../../../../common/translations';
|
||||
import type { LogicalConditionBuilderProps } from './logical_condition';
|
||||
|
@ -87,13 +89,17 @@ interface ValidationResult {
|
|||
result: Partial<{
|
||||
[key in keyof NewTrustedApp]: FieldValidationState;
|
||||
}>;
|
||||
|
||||
/** Additional Warning callout after submit */
|
||||
extraWarning?: boolean;
|
||||
}
|
||||
|
||||
const addResultToValidation = (
|
||||
validation: ValidationResult,
|
||||
field: keyof NewTrustedApp,
|
||||
type: 'warnings' | 'errors',
|
||||
resultValue: React.ReactNode
|
||||
resultValue: React.ReactNode,
|
||||
addToFront?: boolean
|
||||
) => {
|
||||
if (!validation.result[field]) {
|
||||
validation.result[field] = {
|
||||
|
@ -103,8 +109,14 @@ const addResultToValidation = (
|
|||
};
|
||||
}
|
||||
const errorMarkup: React.ReactNode = type === 'warnings' ? <div>{resultValue}</div> : resultValue;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
validation.result[field]![type].push(errorMarkup);
|
||||
|
||||
if (addToFront) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
validation.result[field]![type].unshift(errorMarkup);
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
validation.result[field]![type].push(errorMarkup);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
validation.result[field]!.isInvalid = true;
|
||||
};
|
||||
|
@ -115,6 +127,7 @@ const validateValues = (values: ArtifactFormComponentProps['item']): ValidationR
|
|||
isValid,
|
||||
result: {},
|
||||
};
|
||||
let extraWarning: ValidationResult['extraWarning'];
|
||||
|
||||
// Name field
|
||||
if (!values.name.trim()) {
|
||||
|
@ -152,6 +165,21 @@ const validateValues = (values: ArtifactFormComponentProps['item']): ValidationR
|
|||
value: (entry as TrustedAppConditionEntry).value,
|
||||
});
|
||||
|
||||
if (
|
||||
hasWildcardAndInvalidOperator({
|
||||
operator: entry.type as EntryTypes,
|
||||
value: (entry as TrustedAppConditionEntry).value,
|
||||
})
|
||||
) {
|
||||
extraWarning = true;
|
||||
addResultToValidation(
|
||||
validation,
|
||||
'entries',
|
||||
'warnings',
|
||||
INPUT_ERRORS.wildcardWithWrongOperatorWarning(index)
|
||||
);
|
||||
}
|
||||
|
||||
if (!entry.field || !(entry as TrustedAppConditionEntry).value.trim()) {
|
||||
isValid = false;
|
||||
addResultToValidation(validation, 'entries', 'errors', INPUT_ERRORS.mustHaveValue(index));
|
||||
|
@ -181,6 +209,19 @@ const validateValues = (values: ArtifactFormComponentProps['item']): ValidationR
|
|||
});
|
||||
}
|
||||
|
||||
if (extraWarning) {
|
||||
addResultToValidation(
|
||||
validation,
|
||||
'entries',
|
||||
'errors',
|
||||
<>
|
||||
<EuiSpacer size="s" />
|
||||
<WildCardWithWrongOperatorCallout />
|
||||
</>,
|
||||
true
|
||||
);
|
||||
validation.extraWarning = extraWarning;
|
||||
}
|
||||
validation.isValid = isValid;
|
||||
return validation;
|
||||
};
|
||||
|
@ -245,6 +286,9 @@ export const TrustedAppsForm = memo<ArtifactFormComponentProps>(
|
|||
onChange({
|
||||
item: updatedFormValues,
|
||||
isValid: updatedValidationResult.isValid,
|
||||
confirmModalLabels: updatedValidationResult.extraWarning
|
||||
? CONFIRM_WARNING_MODAL_LABELS
|
||||
: undefined,
|
||||
});
|
||||
},
|
||||
[onChange]
|
||||
|
|
|
@ -154,4 +154,31 @@ export const INPUT_ERRORS = {
|
|||
values: { row: index + 1 },
|
||||
}
|
||||
),
|
||||
wildcardWithWrongOperatorWarning: (index: number) =>
|
||||
i18n.translate('xpack.securitySolution.trustedapps.create.conditionWrongOperatorMsg', {
|
||||
defaultMessage: `[{row}] Using a '*' or a '?' in the value with the 'IS' operator can make the entry ineffective. Change the operator to 'matches' to ensure wildcards run properly.`,
|
||||
values: { row: index + 1 },
|
||||
}),
|
||||
};
|
||||
|
||||
export const CONFIRM_WARNING_MODAL_LABELS = {
|
||||
title: i18n.translate('xpack.securitySolution.trustedapps.confirmWarningModal.title', {
|
||||
defaultMessage: 'Confirm trusted application',
|
||||
}),
|
||||
body: i18n.translate('xpack.securitySolution.trustedapps.confirmWarningModal.body', {
|
||||
defaultMessage:
|
||||
'Using a "*" or a "?" in the value and with the "IS" operator can make the entry ineffective. Change the operator to ‘matches’ to ensure wildcards run properly. Select “cancel” to revise your entry, or "add" to continue with entry in its current state.',
|
||||
}),
|
||||
confirmButton: i18n.translate(
|
||||
'xpack.securitySolution.trustedapps.confirmWarningModal.confirmButtonText',
|
||||
{
|
||||
defaultMessage: 'Add',
|
||||
}
|
||||
),
|
||||
cancelButton: i18n.translate(
|
||||
'xpack.securitySolution.trustedapps.confirmWarningModal.cancelButtonText',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue