[D4C] Further cloud_defend policy validation work (#154616)

## Summary

Adds some additional validation to the yaml editor for both string byte
length checks as well as combined maximum allowed selectors and
responses by type.

### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### Screenshots


![image](https://user-images.githubusercontent.com/16198204/230976116-881bf152-6af9-45ac-9c09-f8ad05d69795.png)

![image](https://user-images.githubusercontent.com/16198204/230976299-e7128486-a4a4-42d4-b979-3507b429535b.png)

![image](https://user-images.githubusercontent.com/16198204/230977899-61b66109-ded3-4c1d-9de9-3fa55699f5ae.png)
This commit is contained in:
Karl Godard 2023-04-11 10:47:58 -07:00 committed by GitHub
parent 6431787de1
commit db5ad71637
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 307 additions and 98 deletions

View file

@ -7,9 +7,9 @@
export const DEFAULT_VISIBLE_ROWS_PER_PAGE = 10; // generic default # of table rows to show (currently we only have a list of policies)
export const LOCAL_STORAGE_PAGE_SIZE = 'cloudDefend:userPageSize';
export const VALID_SELECTOR_NAME_REGEX = /^[a-z0-9][a-z0-9_\-]*$/i; // alphanumberic (no - or _ allowed on first char)
export const MAX_SELECTORS_AND_RESPONSES_PER_TYPE = 64;
export const MAX_SELECTOR_NAME_LENGTH = 128; // chars
export const MAX_CONDITION_VALUE_LENGTH_BYTES = 511;
export const MAX_FILE_PATH_VALUE_LENGTH_BYTES = 255;
export const MAX_CONDITION_VALUE_LENGTH_BYTES = 511; // max length for all condition values. some props override this in cloud_defend/public/types.ts
// TODO: temporary until I change condition value length checks in the yaml editor view to be byte based.
export const MAX_CONDITION_VALUE_LENGTH = 64;

View file

@ -6,6 +6,7 @@
*/
import yaml from 'js-yaml';
import { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import { i18n } from '@kbn/i18n';
import {
Selector,
Response,
@ -17,6 +18,10 @@ import {
SelectorConditionsMap,
SelectorCondition,
} from '../types';
import {
MAX_CONDITION_VALUE_LENGTH_BYTES,
MAX_SELECTORS_AND_RESPONSES_PER_TYPE,
} from './constants';
export function getInputFromPolicy(policy: NewPackagePolicy, inputId: string) {
return policy.inputs.find((input) => input.type === inputId);
@ -49,6 +54,79 @@ export function conditionCombinationInvalid(
return !!invalid;
}
type TotalByType = {
[key in SelectorType]: number;
};
export function getTotalsByType(selectors: Selector[], responses: Response[]) {
const totalsByType: TotalByType = { process: 0, file: 0 };
selectors.forEach((selector) => {
totalsByType[selector.type]++;
});
responses.forEach((response) => {
totalsByType[response.type]++;
});
return totalsByType;
}
export function validateMaxSelectorsAndResponses(selectors: Selector[], responses: Response[]) {
const errors: string[] = [];
const totalsByType = getTotalsByType(selectors, responses);
// check selectors + responses doesn't exceed MAX_SELECTORS_AND_RESPONSES_PER_TYPE
Object.values(totalsByType).forEach((count) => {
if (count > MAX_SELECTORS_AND_RESPONSES_PER_TYPE) {
errors.push(
i18n.translate('xpack.cloudDefend.errorMaxSelectorsResponsesExceeded', {
defaultMessage:
'You cannot exceed {max} selectors + responses for a given type e.g file, process',
values: { max: MAX_SELECTORS_AND_RESPONSES_PER_TYPE },
})
);
}
});
return errors;
}
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)) {
if (patternError) {
errors.push(patternError);
} else {
errors.push(
i18n.translate('xpack.cloudDefend.errorGenericRegexFailure', {
defaultMessage: '"{condition}" values must match the pattern: /{pattern}/',
values: { condition, pattern },
})
);
}
}
const bytes = new Blob([value]).size;
if (bytes > maxValueBytes) {
errors.push(
i18n.translate('xpack.cloudDefend.errorMaxValueBytesExceeded', {
defaultMessage: '"{condition}" values cannot exceed {maxValueBytes} bytes',
values: { condition, maxValueBytes },
})
);
}
});
return errors;
}
export function getRestrictedValuesForCondition(
type: SelectorType,
condition: SelectorCondition

View file

@ -10,7 +10,11 @@ import { render, waitFor } from '@testing-library/react';
import { coreMock } from '@kbn/core/public/mocks';
import userEvent from '@testing-library/user-event';
import { TestProvider } from '../../test/test_provider';
import { getCloudDefendNewPolicyMock, MOCK_YAML_INVALID_CONFIGURATION } from '../../test/mocks';
import {
getCloudDefendNewPolicyMock,
MOCK_YAML_INVALID_CONFIGURATION,
MOCK_YAML_TOO_MANY_FILE_SELECTORS_RESPONSES,
} from '../../test/mocks';
import { ControlGeneralView } from '.';
import { getInputFromPolicy } from '../../common/utils';
import { INPUT_CONTROL } from '../../../common/constants';
@ -156,4 +160,15 @@ describe('<ControlGeneralView />', () => {
expect(queryAllByTestId('cloud-defend-selector')).toHaveLength(0);
expect(queryAllByTestId('cloud-defend-response')).toHaveLength(0);
});
it('prevents the user from adding more than MAX_SELECTORS_AND_RESPONSES_PER_TYPE', async () => {
const { getByTestId } = render(
<WrappedComponent
policy={getCloudDefendNewPolicyMock(MOCK_YAML_TOO_MANY_FILE_SELECTORS_RESPONSES)}
/>
);
userEvent.click(getByTestId('cloud-defend-btnAddSelector'));
expect(getByTestId('cloud-defend-btnAddFileSelector')).toBeDisabled();
});
});

View file

@ -24,22 +24,26 @@ import {
getSelectorsAndResponsesFromYaml,
getDefaultSelectorByType,
getDefaultResponseByType,
getTotalsByType,
} from '../../common/utils';
import { SelectorType, Selector, Response, ViewDeps } from '../../types';
import * as i18n from './translations';
import { ControlGeneralViewSelector } from '../control_general_view_selector';
import { ControlGeneralViewResponse } from '../control_general_view_response';
import { MAX_SELECTORS_AND_RESPONSES_PER_TYPE } from '../../common/constants';
interface AddSelectorButtonProps {
type: 'Selector' | 'Response';
onSelectType(type: SelectorType): void;
selectors: Selector[];
responses: Response[];
}
/**
* dual purpose button for adding selectors and responses by type
*/
const AddButton = ({ type, onSelectType, selectors }: AddSelectorButtonProps) => {
const AddButton = ({ type, onSelectType, selectors, responses }: AddSelectorButtonProps) => {
const totalsByType = useMemo(() => getTotalsByType(selectors, responses), [responses, selectors]);
const [isPopoverOpen, setPopover] = useState(false);
const onButtonClick = () => {
setPopover(!isPopoverOpen);
@ -84,7 +88,10 @@ const AddButton = ({ type, onSelectType, selectors }: AddSelectorButtonProps) =>
key={`addFile${type}`}
icon="document"
onClick={addFile}
disabled={type === 'Response' && selectorCounts.file === 0}
disabled={
(type === 'Response' && selectorCounts.file === 0) ||
totalsByType.file >= MAX_SELECTORS_AND_RESPONSES_PER_TYPE
}
data-test-subj={`cloud-defend-btnAddFile${type}`}
>
{isSelector ? i18n.fileSelector : i18n.fileResponse}
@ -93,7 +100,10 @@ const AddButton = ({ type, onSelectType, selectors }: AddSelectorButtonProps) =>
key={`addProcess${type}`}
icon="gear"
onClick={addProcess}
disabled={type === 'Response' && selectorCounts.process === 0}
disabled={
(type === 'Response' && selectorCounts.process === 0) ||
totalsByType.process >= MAX_SELECTORS_AND_RESPONSES_PER_TYPE
}
data-test-subj={`cloud-defend-btnAddProcess${type}`}
>
{isSelector ? i18n.processSelector : i18n.processResponse}
@ -343,7 +353,12 @@ export const ControlGeneralView = ({ policy, onChange, show }: ViewDeps) => {
);
})}
<AddButton type="Selector" onSelectType={onAddSelector} selectors={selectors} />
<AddButton
type="Selector"
onSelectType={onAddSelector}
selectors={selectors}
responses={responses}
/>
<EuiSpacer size="m" />
@ -371,7 +386,12 @@ export const ControlGeneralView = ({ policy, onChange, show }: ViewDeps) => {
</EuiFlexItem>
);
})}
<AddButton type="Response" onSelectType={onAddResponse} selectors={selectors} />
<AddButton
type="Response"
onSelectType={onAddResponse}
selectors={selectors}
responses={responses}
/>
<EuiSpacer size="m" />
</EuiFlexGroup>
);

View file

@ -148,13 +148,6 @@ export const errorValueRequired = i18n.translate('xpack.cloudDefend.errorValueRe
defaultMessage: 'At least one value is required.',
});
export const errorValueLengthExceeded = i18n.translate(
'xpack.cloudDefend.errorValueLengthExceeded',
{
defaultMessage: 'Values must not exceed 32 characters.',
}
);
export const getSelectorIconTooltip = (type: SelectorType) => {
switch (type) {
case 'process':

View file

@ -189,7 +189,7 @@ describe('<ControlGeneralViewSelector />', () => {
throw new Error("Can't find input");
}
expect(getByText(i18n.errorValueLengthExceeded)).toBeTruthy();
expect(getByText('"containerImageName" values cannot exceed 511 bytes')).toBeTruthy();
});
it('prevents targetFilePath conditions from having values that exceed MAX_FILE_PATH_VALUE_LENGTH_BYTES', async () => {
@ -212,7 +212,7 @@ describe('<ControlGeneralViewSelector />', () => {
throw new Error("Can't find input");
}
expect(getByText(i18n.errorValueLengthExceeded)).toBeTruthy();
expect(getByText('"targetFilePath" values cannot exceed 255 bytes')).toBeTruthy();
});
it('shows an error if condition values fail their pattern regex', async () => {

View file

@ -25,7 +25,6 @@ import {
EuiText,
EuiCheckbox,
} from '@elastic/eui';
import { i18n as i18nLib } from '@kbn/i18n';
import { useStyles } from './styles';
import {
ControlGeneralViewSelectorDeps,
@ -40,16 +39,10 @@ import {
getSelectorTypeIcon,
conditionCombinationInvalid,
getRestrictedValuesForCondition,
validateStringValuesForCondition,
} from '../../common/utils';
import * as i18n from '../control_general_view/translations';
import {
VALID_SELECTOR_NAME_REGEX,
MAX_SELECTOR_NAME_LENGTH,
MAX_CONDITION_VALUE_LENGTH_BYTES,
MAX_FILE_PATH_VALUE_LENGTH_BYTES,
} from '../../common/constants';
const { translate } = i18nLib;
import { VALID_SELECTOR_NAME_REGEX, MAX_SELECTOR_NAME_LENGTH } from '../../common/constants';
interface ConditionProps {
label: string;
@ -290,30 +283,11 @@ export const ControlGeneralViewSelector = ({
errors.push(i18n.errorValueRequired);
}
const { pattern, patternError } = SelectorConditionsMap[prop];
const stringValueErrors = validateStringValuesForCondition(prop, values);
values.forEach((value) => {
const bytes = new Blob([value]).size;
if (pattern && !new RegExp(pattern).test(value)) {
if (patternError) {
errors.push(patternError);
} else {
errors.push(
translate('xpack.cloudDefend.errorGenericRegexFailure', {
defaultMessage: '"{prop}" values must match the pattern: /{pattern}/',
values: { prop, pattern },
})
);
}
} else if (prop === 'targetFilePath') {
if (bytes > MAX_FILE_PATH_VALUE_LENGTH_BYTES) {
errors.push(i18n.errorValueLengthExceeded);
}
} else if (bytes > MAX_CONDITION_VALUE_LENGTH_BYTES) {
errors.push(i18n.errorValueLengthExceeded);
}
});
if (stringValueErrors.length > 0) {
errors.push(...stringValueErrors);
}
if (errors.length) {
errorMap[prop] = errors;

View file

@ -74,7 +74,6 @@ export const ControlSettings = ({ policy, onChange }: SettingsDeps) => {
</EuiTabs>
</EuiFlexItem>
<EuiFlexItem>
{/** general view removed from DOM for performance and to avoid errors when invalid yaml is passed to it**/}
{isGeneralViewSelected && (
<ControlGeneralView
show={isGeneralViewSelected}
@ -82,9 +81,9 @@ export const ControlSettings = ({ policy, onChange }: SettingsDeps) => {
onChange={onGeneralChanges}
/>
)}
{/** Yaml view is kept in the dom at all times to prevent some sizing/rendering issues.
Also only listening for changes if yaml view visible to avoid isValid race condition **/}
<ControlYamlView show={isYamlViewSelected} policy={policy} onChange={onYamlChanges} />
{isYamlViewSelected && (
<ControlYamlView show={isYamlViewSelected} policy={policy} onChange={onYamlChanges} />
)}
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -8,8 +8,16 @@ import React from 'react';
import { render } from '@testing-library/react';
import '@kbn/kibana-react-plugin/public/code_editor/code_editor.test.helpers';
import { TestProvider } from '../../test/test_provider';
import { getCloudDefendNewPolicyMock, MOCK_YAML_INVALID_CONFIGURATION } from '../../test/mocks';
import {
getCloudDefendNewPolicyMock,
MOCK_YAML_INVALID_CONFIGURATION,
MOCK_YAML_INVALID_ACTIONS,
MOCK_YAML_TOO_MANY_FILE_SELECTORS_RESPONSES,
MOCK_YAML_INVALID_STRING_ARRAY_CONDITION,
} from '../../test/mocks';
import { ControlYamlView } from '.';
import * as i18n from './translations';
import { MAX_SELECTORS_AND_RESPONSES_PER_TYPE } from '../../common/constants';
describe('<ControlYamlView />', () => {
const onChange = jest.fn();
@ -31,4 +39,39 @@ describe('<ControlYamlView />', () => {
<WrappedComponent policy={getCloudDefendNewPolicyMock(MOCK_YAML_INVALID_CONFIGURATION)} />
);
});
it('handles additionalErrors: max selectors+responses exceeded ', async () => {
const { getByText, getByTestId } = render(
<WrappedComponent
policy={getCloudDefendNewPolicyMock(MOCK_YAML_TOO_MANY_FILE_SELECTORS_RESPONSES)}
/>
);
expect(getByTestId('cloudDefendAdditionalErrors')).toBeTruthy();
expect(
getByText(
`You cannot exceed ${MAX_SELECTORS_AND_RESPONSES_PER_TYPE} selectors + responses for a given type e.g file, process`
)
).toBeTruthy();
});
it('handles additionalErrors: block action error', async () => {
const { getByText, getByTestId } = render(
<WrappedComponent policy={getCloudDefendNewPolicyMock(MOCK_YAML_INVALID_ACTIONS)} />
);
expect(getByTestId('cloudDefendAdditionalErrors')).toBeTruthy();
expect(getByText(i18n.errorAlertActionRequired)).toBeTruthy();
});
it('handles additionalErrors: selector condition value byte length', async () => {
const { getByText, getByTestId } = render(
<WrappedComponent
policy={getCloudDefendNewPolicyMock(MOCK_YAML_INVALID_STRING_ARRAY_CONDITION)}
/>
);
expect(getByTestId('cloudDefendAdditionalErrors')).toBeTruthy();
expect(getByText('"sessionLeaderName" values cannot exceed 16 bytes')).toBeTruthy();
});
});

View file

@ -8,36 +8,78 @@ import React, { useCallback, useEffect, useState } from 'react';
import { EuiSpacer, EuiText, EuiFlexGroup, EuiFlexItem, EuiForm } from '@elastic/eui';
import { CodeEditor, YamlLang } from '@kbn/kibana-react-plugin/public';
import { monaco } from '@kbn/monaco';
import yaml from 'js-yaml';
import { uniq } from 'lodash';
import { INPUT_CONTROL } from '../../../common/constants';
import { useStyles } from './styles';
import { useConfigModel } from './hooks/use_config_model';
import { getInputFromPolicy } from '../../common/utils';
import {
getInputFromPolicy,
validateStringValuesForCondition,
getSelectorsAndResponsesFromYaml,
validateMaxSelectorsAndResponses,
} from '../../common/utils';
import * as i18n from './translations';
import { ViewDeps } from '../../types';
import { ViewDeps, SelectorConditionsMap, SelectorCondition } from '../../types';
const { editor } = monaco;
const TEXT_EDITOR_PADDING = 10;
interface ConfigError {
interface EditorError {
line: number;
message: string;
}
export const ControlYamlView = ({ policy, onChange, show }: ViewDeps) => {
const styles = useStyles();
const [errors, setErrors] = useState<ConfigError[]>([]);
const [actionsValid, setActionsValid] = useState(true);
const [editorErrors, setEditorErrors] = useState<EditorError[]>([]);
const [additionalErrors, setAdditionalErrors] = useState<string[]>([]);
const input = getInputFromPolicy(policy, INPUT_CONTROL);
const configuration = input?.vars?.configuration?.value || '';
const currentModel = useConfigModel(configuration);
// not all validations can be done via json-schema
const validateAdditional = useCallback((value) => {
const errors: string[] = [];
const { selectors, responses } = getSelectorsAndResponsesFromYaml(value);
errors.push(...validateMaxSelectorsAndResponses(selectors, responses));
// validate selectors
selectors.forEach((selector) => {
Object.keys(selector).map((prop) => {
const condition = prop as SelectorCondition;
if (SelectorConditionsMap[condition]?.type === 'stringArray') {
const values = selector[condition] as string[];
errors.push(...validateStringValuesForCondition(condition, values));
}
});
});
// validate responses
responses.forEach((response) => {
// for now we force 'alert' action if 'block' action added.
if (response.actions.includes('block') && !response.actions.includes('alert')) {
errors.push(i18n.errorAlertActionRequired);
}
});
return uniq(errors);
}, []);
useEffect(() => {
// for on mount
const otherErrors = validateAdditional(configuration);
if (otherErrors.length !== additionalErrors.length) {
setAdditionalErrors(otherErrors);
}
const listener = editor.onDidChangeMarkers(([resource]) => {
const markers = editor.getModelMarkers({ resource });
const errs = markers.map((marker) => {
const error: ConfigError = {
const error: EditorError = {
line: marker.startLineNumber,
message: marker.message,
};
@ -46,49 +88,38 @@ export const ControlYamlView = ({ policy, onChange, show }: ViewDeps) => {
});
// prevents infinite loop
if (JSON.stringify(errs) !== JSON.stringify(errors)) {
onChange({ isValid: actionsValid && errs.length === 0, updatedPolicy: policy });
setErrors(errs);
if (
otherErrors.length !== additionalErrors.length ||
JSON.stringify(errs) !== JSON.stringify(editorErrors)
) {
onChange({
isValid: otherErrors.length === 0 && errs.length === 0,
updatedPolicy: policy,
});
setEditorErrors(errs);
}
});
return () => {
listener.dispose();
};
}, [actionsValid, errors, onChange, policy]);
// for now we force 'alert' action on all responses. This restriction may be removed in future when we have a plan to record all responses. e.g. audit
const validateActions = useCallback((value) => {
try {
const json = yaml.load(value);
for (let i = 0; i < json.responses.length; i++) {
const response = json.responses[i];
if (!response.actions.includes('alert')) {
return false;
}
}
} catch {
// noop
}
return true;
}, []);
}, [editorErrors, onChange, policy, additionalErrors.length, validateAdditional, configuration]);
const onYamlChange = useCallback(
(value) => {
if (input?.vars) {
input.vars.configuration.value = value;
const areActionsValid = validateActions(value);
const errs = validateAdditional(value);
setAdditionalErrors(errs);
setActionsValid(areActionsValid);
onChange({ isValid: areActionsValid && errors.length === 0, updatedPolicy: policy });
onChange({
isValid: errs.length === 0 && editorErrors.length === 0,
updatedPolicy: policy,
});
}
},
[errors.length, input?.vars, onChange, policy, validateActions]
[editorErrors.length, input?.vars, onChange, policy, validateAdditional]
);
return (
@ -98,7 +129,13 @@ export const ControlYamlView = ({ policy, onChange, show }: ViewDeps) => {
{i18n.controlYamlHelp}
</EuiText>
<EuiSpacer size="s" />
{!actionsValid && <EuiForm isInvalid={true} error={i18n.errorAlertActionRequired} />}
{additionalErrors.length > 0 && (
<EuiForm
data-test-subj="cloudDefendAdditionalErrors"
isInvalid={true}
error={additionalErrors}
/>
)}
<div css={styles.yamlEditor}>
<CodeEditor
width="100%"

View file

@ -8,10 +8,10 @@
import { i18n } from '@kbn/i18n';
export const errorAlertActionRequired = i18n.translate('xpack.cloudDefend.alertActionRequired', {
defaultMessage:
'[Technical Preview] The "alert" action is required on all responses. This restriction will be removed once all responses become auditable.',
defaultMessage: 'The alert action is required when "block" action used.',
});
export const controlYamlHelp = i18n.translate('xpack.cloudDefend.controlYamlHelp', {
defaultMessage: 'Configure BPF/LSM controls by creating selectors, and responses below.',
defaultMessage:
'Configure your policy by creating "file" or "process" selectors and responses below.',
});

View file

@ -7,6 +7,7 @@
import type { NewPackagePolicy } from '@kbn/fleet-plugin/public';
import type { PackagePolicy } from '@kbn/fleet-plugin/common';
import { INTEGRATION_PACKAGE_NAME, INPUT_CONTROL, ALERTS_DATASET } from '../../common/constants';
import { MAX_SELECTORS_AND_RESPONSES_PER_TYPE } from '../common/constants';
export const MOCK_YAML_CONFIGURATION = `file:
selectors:
@ -34,10 +35,56 @@ export const MOCK_YAML_CONFIGURATION = `file:
- alert
`;
// block on it's own should be prevented
export const MOCK_YAML_INVALID_ACTIONS = `file:
selectors:
- name: default
operation:
- createExecutable
- modifyExecutable
responses:
- match:
- default
actions:
- block
`;
export const MOCK_YAML_INVALID_STRING_ARRAY_CONDITION = `file:
selectors:
- name: default
operation:
- createExecutable
- modifyExecutable
sessionLeaderName:
- reallylongsessionleadernamethatshouldnotbeallowed
responses:
- match:
- default
actions:
- log
`;
export const MOCK_YAML_INVALID_CONFIGURATION = `
s
`;
export const MOCK_YAML_TOO_MANY_FILE_SELECTORS_RESPONSES = `file:
selectors:
- name: default
operation:
- createExecutable
- modifyExecutable
responses:
${new Array(MAX_SELECTORS_AND_RESPONSES_PER_TYPE + 1)
.fill(0)
.map(() => {
return ` - match: [default]
actions: [alert]
`;
})
.join('')}
`;
export const getCloudDefendNewPolicyMock = (yaml = MOCK_YAML_CONFIGURATION): NewPackagePolicy => ({
name: 'some-cloud_defend-policy',
description: '',

View file

@ -87,6 +87,7 @@ export interface SelectorConditionOptions {
pattern?: string;
patternError?: string;
selectorType?: SelectorType;
maxValueBytes?: number; // defaults to const MAX_FILE_PATH_VALUE_LENGTH_BYTES
not?: SelectorCondition[];
values?:
| {
@ -131,14 +132,18 @@ export const SelectorConditionsMap: SelectorConditionsMapProps = {
process: ['fork', 'exec'],
},
},
targetFilePath: { selectorType: 'file', type: 'stringArray' },
targetFilePath: {
selectorType: 'file',
type: 'stringArray',
maxValueBytes: 255,
},
ignoreVolumeFiles: { selectorType: 'file', type: 'flag', not: ['ignoreVolumeMounts'] },
ignoreVolumeMounts: { selectorType: 'file', type: 'flag', not: ['ignoreVolumeFiles'] },
processExecutable: { selectorType: 'process', type: 'stringArray', not: ['processName'] },
processName: { selectorType: 'process', type: 'stringArray', not: ['processExecutable'] },
processUserId: { selectorType: 'process', type: 'stringArray' },
sessionLeaderInteractive: { selectorType: 'process', type: 'boolean' },
sessionLeaderName: { selectorType: 'process', type: 'stringArray' },
sessionLeaderName: { selectorType: 'process', type: 'stringArray', maxValueBytes: 16 },
};
export type ResponseAction = 'log' | 'alert' | 'block';
@ -146,6 +151,7 @@ export type ResponseAction = 'log' | 'alert' | 'block';
export interface Selector {
name: string;
operation?: string[];
containerImageFullName?: string[];
containerImageName?: string[];
containerImageTag?: string[];
kubernetesClusterId?: string[];

View file

@ -38070,7 +38070,6 @@
"xpack.cloudDefend.errorConditionRequired": "Au moins une condition par sélecteur est requise.",
"xpack.cloudDefend.errorDuplicateName": "Ce nom est déjà utilisé par un autre sélecteur.",
"xpack.cloudDefend.errorInvalidName": "Les noms des sélecteurs doivent être alphanumériques et ne doivent pas inclure d'espaces.",
"xpack.cloudDefend.errorValueLengthExceeded": "Les valeurs ne doivent pas dépasser 32 caractères.",
"xpack.cloudDefend.errorValueRequired": "Au moins une valeur est requise.",
"xpack.cloudDefend.name": "Nom",
"xpack.cloudLinks.deploymentLinkLabel": "Gérer ce déploiement",

View file

@ -38038,7 +38038,6 @@
"xpack.cloudDefend.errorConditionRequired": "セレクターにつき1つ以上の条件が必要です。",
"xpack.cloudDefend.errorDuplicateName": "この名前はすでに別のセレクターで使用されています。",
"xpack.cloudDefend.errorInvalidName": "セレクター名は英数字でなければならず、スペースは使用できません。",
"xpack.cloudDefend.errorValueLengthExceeded": "値は32文字以下でなければなりません。",
"xpack.cloudDefend.errorValueRequired": "1つ以上の値が必要です。",
"xpack.cloudDefend.name": "名前",
"xpack.cloudLinks.deploymentLinkLabel": "このデプロイの管理",

View file

@ -38065,7 +38065,6 @@
"xpack.cloudDefend.errorConditionRequired": "每个选择器至少需要一个条件。",
"xpack.cloudDefend.errorDuplicateName": "此名称已由其他选择器使用。",
"xpack.cloudDefend.errorInvalidName": "选择器名称必须为字母数字,并且不包含空格。",
"xpack.cloudDefend.errorValueLengthExceeded": "值不能超过 32 个字符。",
"xpack.cloudDefend.errorValueRequired": "至少需要一个值。",
"xpack.cloudDefend.name": "名称",
"xpack.cloudLinks.deploymentLinkLabel": "管理此部署",