diff --git a/x-pack/plugins/cloud_defend/public/common/utils.ts b/x-pack/plugins/cloud_defend/public/common/utils.ts
index 2e10d8982f82e..8200bb866db8a 100644
--- a/x-pack/plugins/cloud_defend/public/common/utils.ts
+++ b/x-pack/plugins/cloud_defend/public/common/utils.ts
@@ -178,15 +178,22 @@ export function validateMaxSelectorsAndResponses(selectors: Selector[], response
return errors;
}
-export function validateStringValuesForCondition(condition: SelectorCondition, values: string[]) {
+export function validateStringValuesForCondition(condition: SelectorCondition, values?: string[]) {
const errors: string[] = [];
const maxValueBytes =
SelectorConditionsMap[condition].maxValueBytes || MAX_CONDITION_VALUE_LENGTH_BYTES;
const { pattern, patternError } = SelectorConditionsMap[condition];
- values.forEach((value) => {
- if (pattern && !new RegExp(pattern).test(value)) {
+ values?.forEach((value) => {
+ if (value?.length === 0) {
+ errors.push(
+ i18n.translate('xpack.cloudDefend.errorGenericEmptyValue', {
+ defaultMessage: '"{condition}" values cannot be empty',
+ values: { condition },
+ })
+ );
+ } else if (pattern && !new RegExp(pattern).test(value)) {
if (patternError) {
errors.push(patternError);
} else {
diff --git a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx
index e54047a169a6e..5ab2886de9604 100644
--- a/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx
+++ b/x-pack/plugins/cloud_defend/public/components/control_general_view_selector/index.test.tsx
@@ -300,22 +300,27 @@ describe('', () => {
}
updatedSelector = onChange.mock.calls[1][0];
+ expect(updatedSelector.hasErrors).toBeFalsy();
rerender();
-
expect(findByText(errorStr)).toMatchObject({});
userEvent.type(el, '/*{enter}');
updatedSelector = onChange.mock.calls[2][0];
+ expect(updatedSelector.hasErrors).toBeFalsy();
rerender();
-
expect(findByText(errorStr)).toMatchObject({});
userEvent.type(el, 'badpath{enter}');
updatedSelector = onChange.mock.calls[3][0];
-
+ expect(updatedSelector.hasErrors).toBeTruthy();
rerender();
-
expect(getByText(errorStr)).toBeTruthy();
+
+ userEvent.type(el, ' {enter}');
+ updatedSelector = onChange.mock.calls[4][0];
+ expect(updatedSelector.hasErrors).toBeTruthy();
+ rerender();
+ expect(getByText('"targetFilePath" values cannot be empty')).toBeTruthy();
});
it('validates processExecutable conditions values', async () => {
@@ -336,7 +341,7 @@ describe('', () => {
'input'
);
- const errorStr = i18n.errorInvalidProcessExecutable;
+ const regexError = i18n.errorInvalidProcessExecutable;
if (el) {
userEvent.type(el, '/usr/bin/**{enter}');
@@ -345,28 +350,33 @@ describe('', () => {
}
updatedSelector = onChange.mock.calls[1][0];
+ expect(updatedSelector.hasErrors).toBeFalsy();
rerender();
-
- expect(findByText(errorStr)).toMatchObject({});
+ expect(findByText(regexError)).toMatchObject({});
userEvent.type(el, '/*{enter}');
updatedSelector = onChange.mock.calls[2][0];
+ expect(updatedSelector.hasErrors).toBeFalsy();
rerender();
-
- expect(findByText(errorStr)).toMatchObject({});
+ expect(findByText(regexError)).toMatchObject({});
userEvent.type(el, '/usr/bin/ls{enter}');
updatedSelector = onChange.mock.calls[3][0];
+ expect(updatedSelector.hasErrors).toBeFalsy();
rerender();
-
- expect(findByText(errorStr)).toMatchObject({});
+ expect(findByText(regexError)).toMatchObject({});
userEvent.type(el, 'badpath{enter}');
updatedSelector = onChange.mock.calls[4][0];
-
+ expect(updatedSelector.hasErrors).toBeTruthy();
rerender();
+ expect(getByText(regexError)).toBeTruthy();
- expect(getByText(errorStr)).toBeTruthy();
+ userEvent.type(el, ' {enter}');
+ updatedSelector = onChange.mock.calls[4][0];
+ expect(updatedSelector.hasErrors).toBeTruthy();
+ rerender();
+ expect(getByText('"processExecutable" values cannot be empty')).toBeTruthy();
});
it('validates containerImageFullName conditions values', async () => {
@@ -385,7 +395,7 @@ describe('', () => {
'input'
);
- const errorStr = i18n.errorInvalidFullContainerImageName;
+ const regexError = i18n.errorInvalidFullContainerImageName;
if (el) {
userEvent.type(el, 'docker.io/nginx{enter}');
@@ -396,13 +406,13 @@ describe('', () => {
updatedSelector = onChange.mock.calls[1][0];
rerender();
- expect(findByText(errorStr)).toMatchObject({});
+ expect(findByText(regexError)).toMatchObject({});
userEvent.type(el, 'nginx{enter}');
updatedSelector = onChange.mock.calls[2][0];
rerender();
- expect(getByText(errorStr)).toBeTruthy();
+ expect(getByText(regexError)).toBeTruthy();
});
it('validates kubernetesPodLabel conditions values', async () => {
diff --git a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
index 0849fcb912e79..33d4a1c010caa 100644
--- a/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
+++ b/x-pack/plugins/cloud_defend/public/components/control_yaml_view/hooks/policy_schema.json
@@ -177,7 +177,8 @@
"minItems": 1,
"items": {
"type": "string",
- "pattern": "^(?:\\/[^\\/\\*]+)+(?:\\/\\*|\\/\\*\\*)?$"
+ "pattern": "^(?:\\/[^\\/\\*]+)*(?:\\/\\*|\\/\\*\\*)?$",
+ "minLength": 1
}
},
"ignoreVolumeMounts": {
@@ -319,7 +320,8 @@
"minItems": 1,
"items": {
"type": "string",
- "pattern": "^(?:\\/[^\\/\\*]+)+(?:\\/\\*|\\/\\*\\*)?$"
+ "pattern": "^(?:\\/[^\\/\\*]+)*(?:\\/\\*|\\/\\*\\*)?$",
+ "minLength": 1
}
},
"processName": {
diff --git a/x-pack/plugins/cloud_defend/public/types.ts b/x-pack/plugins/cloud_defend/public/types.ts
index d8f23302b3baa..2e9b864db5a44 100755
--- a/x-pack/plugins/cloud_defend/public/types.ts
+++ b/x-pack/plugins/cloud_defend/public/types.ts
@@ -134,7 +134,7 @@ export const SelectorConditionsMap: SelectorConditionsMapProps = {
selectorType: 'file',
type: 'stringArray',
maxValueBytes: 255,
- pattern: '^(?:\\/[^\\/\\*]+)+(?:\\/\\*|\\/\\*\\*)?$',
+ pattern: '^(?:\\/[^\\/\\*]+)*(?:\\/\\*|\\/\\*\\*)?$',
patternError: i18n.errorInvalidTargetFilePath,
},
ignoreVolumeFiles: { selectorType: 'file', type: 'flag', not: ['ignoreVolumeMounts'] },
@@ -143,7 +143,7 @@ export const SelectorConditionsMap: SelectorConditionsMapProps = {
selectorType: 'process',
type: 'stringArray',
not: ['processName'],
- pattern: '^(?:\\/[^\\/\\*]+)+(?:\\/\\*|\\/\\*\\*)?$',
+ pattern: '^(?:\\/[^\\/\\*]+)*(?:\\/\\*|\\/\\*\\*)?$',
patternError: i18n.errorInvalidProcessExecutable,
},
processName: {