[8.14] [Fleet] add validation to dataset field in input packages to disallow special characters (#182925) (#183367)

# Backport

This will backport the following commits from `main` to `8.14`:
- [[Fleet] add validation to dataset field in input packages to disallow
special characters
(#182925)](https://github.com/elastic/kibana/pull/182925)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Julia
Bardi","email":"90178898+juliaElastic@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-05-14T08:45:54Z","message":"[Fleet]
add validation to dataset field in input packages to disallow special
characters (#182925)\n\n## Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/181044\r\n\r\nAdded validation
to allow only valid index names as dataset name in\r\ninput packages.
Allowing lowercase letters, numbers, dot and underscore\r\n(except in
the beginning).\r\n\r\nTo verify:\r\n- add Custom Logs integration\r\n-
modfiy dataset to add invalid characters e.g. *\r\n- verify that the
field shows a validation error\r\n- verify that the save button is
disabled\r\n- verify that valid dataset names can be used\r\n\r\n<img
width=\"968\"
alt=\"image\"\r\nsrc=\"e9cd17f1-6f5b-464c-bc8d-83d2ec42bada\">\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"be9b46d911f8648203a4c1d5fe4ccea848020ef7","branchLabelMapping":{"^v8.15.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Fleet","backport:prev-minor","v8.15.0"],"title":"[Fleet]
add validation to dataset field in input packages to disallow special
characters","number":182925,"url":"https://github.com/elastic/kibana/pull/182925","mergeCommit":{"message":"[Fleet]
add validation to dataset field in input packages to disallow special
characters (#182925)\n\n## Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/181044\r\n\r\nAdded validation
to allow only valid index names as dataset name in\r\ninput packages.
Allowing lowercase letters, numbers, dot and underscore\r\n(except in
the beginning).\r\n\r\nTo verify:\r\n- add Custom Logs integration\r\n-
modfiy dataset to add invalid characters e.g. *\r\n- verify that the
field shows a validation error\r\n- verify that the save button is
disabled\r\n- verify that valid dataset names can be used\r\n\r\n<img
width=\"968\"
alt=\"image\"\r\nsrc=\"e9cd17f1-6f5b-464c-bc8d-83d2ec42bada\">\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"be9b46d911f8648203a4c1d5fe4ccea848020ef7"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.15.0","branchLabelMappingKey":"^v8.15.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/182925","number":182925,"mergeCommit":{"message":"[Fleet]
add validation to dataset field in input packages to disallow special
characters (#182925)\n\n## Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/181044\r\n\r\nAdded validation
to allow only valid index names as dataset name in\r\ninput packages.
Allowing lowercase letters, numbers, dot and underscore\r\n(except in
the beginning).\r\n\r\nTo verify:\r\n- add Custom Logs integration\r\n-
modfiy dataset to add invalid characters e.g. *\r\n- verify that the
field shows a validation error\r\n- verify that the save button is
disabled\r\n- verify that valid dataset names can be used\r\n\r\n<img
width=\"968\"
alt=\"image\"\r\nsrc=\"e9cd17f1-6f5b-464c-bc8d-83d2ec42bada\">\r\n\r\n\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios","sha":"be9b46d911f8648203a4c1d5fe4ccea848020ef7"}}]}]
BACKPORT-->

Co-authored-by: Julia Bardi <90178898+juliaElastic@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-05-14 06:26:40 -04:00 committed by GitHub
parent b1f978ea67
commit f956f9d1ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 477 additions and 181 deletions

View file

@ -73,6 +73,7 @@ export {
fleetSetupRouteService,
// Package policy helpers
isValidNamespace,
isValidDataset,
INVALID_NAMESPACE_CHARACTERS,
getFileMetadataIndexName,
getFileDataIndexName,

View file

@ -16,7 +16,11 @@ export {
} from './package_to_package_policy';
export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml';
export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package';
export { isValidNamespace, INVALID_NAMESPACE_CHARACTERS } from './is_valid_namespace';
export {
isValidDataset,
isValidNamespace,
INVALID_NAMESPACE_CHARACTERS,
} from './is_valid_namespace';
export { isDiffPathProtocol } from './is_diff_path_protocol';
export { LicenseService } from './license';
export * from './is_agent_upgradeable';

View file

@ -14,37 +14,71 @@ export function isValidNamespace(
namespace: string,
allowBlankNamespace?: boolean
): { valid: boolean; error?: string } {
if (!namespace.trim() && !allowBlankNamespace) {
return isValidEntity(namespace, 'Namespace', allowBlankNamespace);
}
export function isValidDataset(
dataset: string,
allowBlank?: boolean
): { valid: boolean; error?: string } {
const { valid, error } = isValidEntity(dataset, 'Dataset', allowBlank);
if (!valid) {
return { valid, error };
}
if (dataset.startsWith('_') || dataset.startsWith('.')) {
return {
valid: false,
error: i18n.translate(
'xpack.fleet.datasetValidation.datasetStartsWithUnderscoreErrorMessage',
{
defaultMessage: 'Dataset cannot start with an underscore or dot',
}
),
};
}
return { valid, error };
}
function isValidEntity(
name: string,
type: string,
allowBlank?: boolean
): { valid: boolean; error?: string } {
if (!name.trim() && !allowBlank) {
return {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.requiredErrorMessage', {
defaultMessage: 'Namespace is required',
defaultMessage: '{type} is required',
values: { type },
}),
};
} else if (namespace !== namespace.toLowerCase()) {
} else if (name !== name.toLowerCase()) {
return {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.lowercaseErrorMessage', {
defaultMessage: 'Namespace must be lowercase',
defaultMessage: '{type} must be lowercase',
values: { type },
}),
};
} else if (INVALID_NAMESPACE_CHARACTERS.test(namespace)) {
} else if (INVALID_NAMESPACE_CHARACTERS.test(name)) {
return {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.invalidCharactersErrorMessage', {
defaultMessage: 'Namespace contains invalid characters',
defaultMessage: '{type} contains invalid characters',
values: { type },
}),
};
}
// Node.js doesn't have Blob, and browser doesn't have Buffer :)
else if (
(typeof Blob === 'function' && new Blob([namespace]).size > 100) ||
(typeof Buffer === 'function' && Buffer.from(namespace).length > 100)
(typeof Blob === 'function' && new Blob([name]).size > 100) ||
(typeof Buffer === 'function' && Buffer.from(name).length > 100)
) {
return {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.tooLongErrorMessage', {
defaultMessage: 'Namespace cannot be more than 100 bytes',
defaultMessage: '{type} cannot be more than 100 bytes',
values: { type },
}),
};
}

View file

@ -1079,4 +1079,144 @@ describe('Fleet - validatePackagePolicyConfig', () => {
expect(res).toEqual(['Secret reference is invalid, id must be a string']);
});
});
describe('Dataset', () => {
const datasetError = 'Dataset contains invalid characters';
const validateDataset = (dataset: string) => {
return validatePackagePolicyConfig(
{
type: 'text',
value: { dataset, package: 'log' },
},
{
name: 'data_stream.dataset',
type: 'text',
},
'data_stream.dataset',
safeLoad,
'input'
);
};
it('should return an error message if the value has *', () => {
const res = validateDataset('test*');
expect(res).toEqual([datasetError]);
});
it('should return an error message if the value has uppercase letter', () => {
const res = validateDataset('Test');
expect(res).toEqual(['Dataset must be lowercase']);
});
it('should return an error message if the value has _ in the beginning', () => {
const res = validateDataset('_test');
expect(res).toEqual(['Dataset cannot start with an underscore or dot']);
});
it('should return an error message if the value has . in the beginning', () => {
const res = validateDataset('.test');
expect(res).toEqual(['Dataset cannot start with an underscore or dot']);
});
it('should not return an error message if the value is valid', () => {
const res = validateDataset('fleet_server.test_dataset');
expect(res).toEqual(null);
});
it('should not return an error message if the value is undefined', () => {
const res = validatePackagePolicyConfig(
{
type: 'text',
value: undefined,
},
{
name: 'data_stream.dataset',
type: 'text',
},
'data_stream.dataset',
safeLoad,
'input'
);
expect(res).toEqual(null);
});
it('should not return an error message if the package is not input type', () => {
const res = validatePackagePolicyConfig(
{
type: 'text',
value: { dataset: 'Test', package: 'log' },
},
{
name: 'data_stream.dataset',
type: 'text',
},
'data_stream.dataset',
safeLoad,
'integration'
);
expect(res).toEqual(null);
});
it('should not return an error message if the var is not dataset', () => {
const res = validatePackagePolicyConfig(
{
type: 'text',
value: { dataset: 'Test', package: 'log' },
},
{
name: 'test_field',
type: 'text',
},
'test_field',
safeLoad,
'input'
);
expect(res).toEqual(null);
});
it('should return an error message if the string dataset value has special characters', () => {
const res = validatePackagePolicyConfig(
{
type: 'text',
value: 'test*',
},
{
name: 'data_stream.dataset',
type: 'text',
},
'data_stream.dataset',
safeLoad,
'input'
);
expect(res).toEqual(['Dataset contains invalid characters']);
});
it('should return an error message if the dataset value has special characters', () => {
const res = validatePackagePolicyConfig(
{
type: 'text',
value: { dataset: 'test*', package: 'log' },
},
{
name: 'data_stream.dataset',
type: 'text',
},
'data_stream.dataset',
safeLoad,
'input'
);
expect(res).toEqual(['Dataset contains invalid characters']);
});
});
});

View file

@ -19,6 +19,8 @@ import type {
RegistryVarsEntry,
} from '../types';
import { DATASET_VAR_NAME } from '../constants';
import {
isValidNamespace,
doesPackageHaveIntegrations,
@ -26,6 +28,7 @@ import {
getNormalizedDataStreams,
} from '.';
import { packageHasNoPolicyTemplates } from './policy_template';
import { isValidDataset } from './is_valid_namespace';
type Errors = string[] | null;
@ -173,7 +176,13 @@ export const validatePackagePolicy = (
results[name] =
input.enabled && stream.enabled
? validatePackagePolicyConfig(configEntry, streamVarDefs[name], name, safeLoadYaml)
? validatePackagePolicyConfig(
configEntry,
streamVarDefs[name],
name,
safeLoadYaml,
packageInfo.type
)
: null;
return results;
@ -202,7 +211,8 @@ export const validatePackagePolicyConfig = (
configEntry: PackagePolicyConfigRecordEntry | undefined,
varDef: RegistryVarsEntry,
varName: string,
safeLoadYaml: (yaml: string) => any
safeLoadYaml: (yaml: string) => any,
packageType?: string
): string[] | null => {
const errors = [];
@ -357,6 +367,16 @@ export const validatePackagePolicyConfig = (
}
}
if (varName === DATASET_VAR_NAME && packageType === 'input' && parsedValue !== undefined) {
const { valid, error } = isValidDataset(
parsedValue.dataset ? parsedValue.dataset : parsedValue,
false
);
if (!valid && error) {
errors.push(error);
}
}
return errors.length ? errors : null;
};

View file

@ -1,133 +0,0 @@
/*
* 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, { useEffect, useState } from 'react';
import { EuiComboBox, EuiIcon, EuiLink, EuiSpacer, EuiText, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { DataStream } from '../../../../../../../../../common/types';
import { GENERIC_DATASET_NAME } from '../../../../../../../../../common/constants';
interface SelectedDataset {
dataset: string;
package: string;
}
export const DatasetComboBox: React.FC<{
value?: SelectedDataset | string;
onChange: (newValue: SelectedDataset) => void;
datastreams: DataStream[];
pkgName?: string;
isDisabled?: boolean;
}> = ({ value, onChange, datastreams, isDisabled, pkgName = '' }) => {
const datasetOptions =
datastreams.map((datastream: DataStream) => ({
label: datastream.dataset,
value: datastream,
})) ?? [];
const existingGenericStream = datasetOptions.find((ds) => ds.label === GENERIC_DATASET_NAME);
const valueAsOption = value
? typeof value === 'string'
? { label: value, value: { dataset: value, package: pkgName } }
: { label: value.dataset, value: { dataset: value.dataset, package: value.package } }
: undefined;
const defaultOption = valueAsOption ||
existingGenericStream || {
label: GENERIC_DATASET_NAME,
value: { dataset: GENERIC_DATASET_NAME, package: pkgName },
};
const [selectedOptions, setSelectedOptions] = useState<Array<{ label: string }>>([defaultOption]);
useEffect(() => {
if (!value || typeof value === 'string') onChange(defaultOption.value as SelectedDataset);
}, [value, defaultOption.value, onChange, pkgName]);
const onDatasetChange = (newSelectedOptions: Array<{ label: string; value?: DataStream }>) => {
setSelectedOptions(newSelectedOptions);
const dataStream = newSelectedOptions[0].value;
onChange({
dataset: newSelectedOptions[0].label,
package: !dataStream || typeof dataStream === 'string' ? pkgName : dataStream.package,
});
};
const onCreateOption = (searchValue: string = '') => {
const normalizedSearchValue = searchValue.trim().toLowerCase();
if (!normalizedSearchValue) {
return;
}
const newOption = {
label: searchValue,
value: { dataset: searchValue, package: pkgName },
};
setSelectedOptions([newOption]);
onChange({
dataset: searchValue,
package: pkgName,
});
};
return (
<>
<EuiComboBox
aria-label={i18n.translate('xpack.fleet.datasetCombo.ariaLabel', {
defaultMessage: 'Dataset combo box',
})}
placeholder={i18n.translate('xpack.fleet.datasetCombo.placeholder', {
defaultMessage: 'Select a dataset',
})}
singleSelection={{ asPlainText: true }}
options={datasetOptions}
selectedOptions={selectedOptions}
onCreateOption={onCreateOption}
onChange={onDatasetChange}
customOptionText={i18n.translate('xpack.fleet.datasetCombo.customOptionText', {
defaultMessage: 'Add {searchValue} as a custom option',
values: { searchValue: '{searchValue}' },
})}
isClearable={false}
isDisabled={isDisabled}
data-test-subj="datasetComboBox"
/>
{valueAsOption && valueAsOption.value.package !== pkgName && (
<>
<EuiSpacer size="xs" />
<EuiText size="xs" color="warning">
<EuiIcon type="warning" />
&nbsp;
<FormattedMessage
id="xpack.fleet.datasetCombo.warning"
defaultMessage="This data stream is managed by the {package} integration, {learnMore}."
values={{
package: valueAsOption.value.package,
learnMore: (
<EuiToolTip
position="bottom"
content={
<FormattedMessage
id="xpack.fleet.datasetCombo.warningTooltip"
defaultMessage="The destination data stream may not be designed to receive data from this integration, check that the mappings and ingest pipelines are compatible before sending data."
/>
}
>
<EuiLink target="_blank">
{i18n.translate('xpack.fleet.datasetCombo.learnMoreLink', {
defaultMessage: 'learn more',
})}
</EuiLink>
</EuiToolTip>
),
}}
/>
</EuiText>
</>
)}
</>
);
};

View file

@ -0,0 +1,71 @@
/*
* 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 from 'react';
import { fireEvent } from '@testing-library/react';
import { createFleetTestRendererMock } from '../../../../../../../../mock';
import { DatasetComponent } from './dataset_component';
describe('DatasetComponent', () => {
function render(value = 'generic', datastreams: any = []) {
const renderer = createFleetTestRendererMock();
const mockOnChange = jest.fn();
const fieldLabel = 'Dataset name';
const utils = renderer.render(
<DatasetComponent
pkgName={'log'}
datastreams={datastreams}
value={{
dataset: value,
package: 'log',
}}
onChange={mockOnChange}
isDisabled={false}
fieldLabel={fieldLabel}
/>
);
return { utils, mockOnChange };
}
it('should show validation error if dataset is invalid', () => {
const { utils } = render();
const inputEl = utils.getByTestId('comboBoxSearchInput');
fireEvent.change(inputEl, { target: { value: 'generic*' } });
fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' });
utils.getByText('Dataset contains invalid characters');
});
it('should not show validation error if dataset is valid', () => {
const { utils } = render();
const inputEl = utils.getByTestId('comboBoxSearchInput');
fireEvent.change(inputEl, { target: { value: 'test' } });
fireEvent.keyDown(inputEl, { key: 'Enter', code: 'Enter' });
expect(utils.queryByText('Dataset contains invalid characters')).toBeNull();
});
it('should not show validation error if valid dataset selected from select', () => {
const { utils, mockOnChange } = render(undefined, [
{ dataset: 'fleet_server.test_ds', package: 'log' },
]);
const inputEl = utils.getByTestId('comboBoxSearchInput');
fireEvent.click(inputEl);
const option = utils.getByText('fleet_server.test_ds');
fireEvent.click(option);
expect(utils.queryByText('Dataset contains invalid characters')).toBeNull();
expect(mockOnChange).toHaveBeenCalledWith({ dataset: 'fleet_server.test_ds', package: 'log' });
});
});

View file

@ -0,0 +1,174 @@
/*
* 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, { useEffect, useState } from 'react';
import {
EuiComboBox,
EuiFormRow,
EuiIcon,
EuiLink,
EuiSpacer,
EuiText,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import ReactMarkdown from 'react-markdown';
import { FormattedMessage } from '@kbn/i18n-react';
import type { DataStream } from '../../../../../../../../../common/types';
import { GENERIC_DATASET_NAME } from '../../../../../../../../../common/constants';
import { isValidDataset } from '../../../../../../../../../common';
const FormRow = styled(EuiFormRow)`
.euiFormRow__label {
flex: 1;
}
.euiFormRow__fieldWrapper > .euiPanel {
padding: ${(props) => props.theme.eui?.euiSizeXS};
}
`;
interface SelectedDataset {
dataset: string;
package: string;
}
export const DatasetComponent: React.FC<{
value?: SelectedDataset | string;
onChange: (newValue: SelectedDataset) => void;
datastreams: DataStream[];
pkgName?: string;
isDisabled?: boolean;
fieldLabel: string;
description?: string;
}> = ({ value, onChange, datastreams, isDisabled, pkgName = '', fieldLabel, description }) => {
const datasetOptions =
datastreams.map((datastream: DataStream) => ({
label: datastream.dataset,
value: datastream,
})) ?? [];
const existingGenericStream = datasetOptions.find((ds) => ds.label === GENERIC_DATASET_NAME);
const valueAsOption = value
? typeof value === 'string'
? { label: value, value: { dataset: value, package: pkgName } }
: { label: value.dataset, value: { dataset: value.dataset, package: value.package } }
: undefined;
const defaultOption = valueAsOption ||
existingGenericStream || {
label: GENERIC_DATASET_NAME,
value: { dataset: GENERIC_DATASET_NAME, package: pkgName },
};
const [selectedOptions, setSelectedOptions] = useState<Array<{ label: string }>>([defaultOption]);
const [isInvalid, setIsInvalid] = useState<boolean>(false);
const [error, setError] = useState<string | undefined>(undefined);
useEffect(() => {
if (!value || typeof value === 'string') onChange(defaultOption.value as SelectedDataset);
}, [value, defaultOption.value, onChange, pkgName]);
const onDatasetChange = (newSelectedOptions: Array<{ label: string; value?: DataStream }>) => {
setSelectedOptions(newSelectedOptions);
const dataStream = newSelectedOptions[0].value;
const { valid, error: dsError } = isValidDataset(newSelectedOptions[0].label, false);
setIsInvalid(!valid);
setError(dsError);
onChange({
dataset: newSelectedOptions[0].label,
package: !dataStream || typeof dataStream === 'string' ? pkgName : dataStream.package,
});
};
const onCreateOption = (searchValue: string = '') => {
const normalizedSearchValue = searchValue.trim().toLowerCase();
if (!normalizedSearchValue) {
return;
}
const newOption = {
label: searchValue,
value: { dataset: searchValue, package: pkgName },
};
setSelectedOptions([newOption]);
const { valid, error: dsError } = isValidDataset(searchValue, false);
setIsInvalid(!valid);
setError(dsError);
onChange({
dataset: searchValue,
package: pkgName,
});
};
return (
<FormRow
isInvalid={isInvalid}
error={error}
hasChildLabel={true}
label={fieldLabel}
helpText={description && <ReactMarkdown children={description} />}
fullWidth
>
<>
<EuiComboBox
aria-label={i18n.translate('xpack.fleet.datasetCombo.ariaLabel', {
defaultMessage: 'Dataset combo box',
})}
placeholder={i18n.translate('xpack.fleet.datasetCombo.placeholder', {
defaultMessage: 'Select a dataset',
})}
singleSelection={{ asPlainText: true }}
options={datasetOptions}
selectedOptions={selectedOptions}
onCreateOption={onCreateOption}
onChange={onDatasetChange}
customOptionText={i18n.translate('xpack.fleet.datasetCombo.customOptionText', {
defaultMessage: 'Add {searchValue} as a custom option',
values: { searchValue: '{searchValue}' },
})}
isClearable={false}
isDisabled={isDisabled}
data-test-subj="datasetComboBox"
isInvalid={isInvalid}
/>
{valueAsOption && valueAsOption.value.package !== pkgName && (
<>
<EuiSpacer size="xs" />
<EuiText size="xs" color="warning">
<EuiIcon type="warning" />
&nbsp;
<FormattedMessage
id="xpack.fleet.datasetCombo.warning"
defaultMessage="This data stream is managed by the {package} integration, {learnMore}."
values={{
package: valueAsOption.value.package,
learnMore: (
<EuiToolTip
position="bottom"
content={
<FormattedMessage
id="xpack.fleet.datasetCombo.warningTooltip"
defaultMessage="The destination data stream may not be designed to receive data from this integration, check that the mappings and ingest pipelines are compatible before sending data."
/>
}
>
<EuiLink target="_blank">
{i18n.translate('xpack.fleet.datasetCombo.learnMoreLink', {
defaultMessage: 'learn more',
})}
</EuiLink>
</EuiToolTip>
),
}}
/>
</EuiText>
</>
)}
</>
</FormRow>
);
};

View file

@ -36,7 +36,7 @@ import { DATASET_VAR_NAME } from '../../../../../../../../../common/constants';
import type { DataStream, RegistryVarsEntry } from '../../../../../../types';
import { MultiTextInput } from './multi_text_input';
import { DatasetComboBox } from './dataset_combo';
import { DatasetComponent } from './dataset_component';
const FixedHeightDiv = styled.div`
height: 300px;
@ -88,7 +88,6 @@ export const PackagePolicyInputVarField: React.FunctionComponent<InputFieldProps
isEditPage = false,
}) => {
const fleetStatus = useFleetStatus();
const [isDirty, setIsDirty] = useState<boolean>(false);
const { required, type, title, name, description } = varDef;
const isInvalid = Boolean((isDirty || forceShowErrors) && !!varErrors?.length);
@ -101,6 +100,20 @@ export const PackagePolicyInputVarField: React.FunctionComponent<InputFieldProps
const secretsStorageEnabled = fleetStatus.isReady && fleetStatus.isSecretsStorageEnabled;
const useSecretsUi = secretsStorageEnabled && varDef.secret;
if (name === DATASET_VAR_NAME && packageType === 'input') {
return (
<DatasetComponent
pkgName={packageName}
datastreams={datastreams}
value={value}
onChange={onChange}
isDisabled={isEditPage}
fieldLabel={fieldLabel}
description={description}
/>
);
}
let field: JSX.Element;
if (useSecretsUi) {
@ -127,10 +140,6 @@ export const PackagePolicyInputVarField: React.FunctionComponent<InputFieldProps
value,
onChange,
frozen,
packageName,
packageType,
datastreams,
isEditPage,
isInvalid,
fieldLabel,
fieldTestSelector,
@ -171,16 +180,12 @@ function getInputComponent({
value,
onChange,
frozen,
packageName,
packageType,
datastreams = [],
isEditPage,
isInvalid,
fieldLabel,
fieldTestSelector,
setIsDirty,
}: InputComponentProps) {
const { multi, type, name, options } = varDef;
const { multi, type, options } = varDef;
if (multi) {
return (
<MultiTextInput
@ -193,17 +198,6 @@ function getInputComponent({
/>
);
}
if (name === DATASET_VAR_NAME && packageType === 'input') {
return (
<DatasetComboBox
pkgName={packageName}
datastreams={datastreams}
value={value}
onChange={onChange}
isDisabled={isEditPage}
/>
);
}
switch (type) {
case 'textarea':
return (

View file

@ -18714,10 +18714,6 @@
"xpack.fleet.multiRowInput.addRow": "Ajouter une ligne",
"xpack.fleet.multiRowInput.deleteButton": "Supprimer la ligne",
"xpack.fleet.multiTextInput.addRow": "Ajouter une ligne",
"xpack.fleet.namespaceValidation.invalidCharactersErrorMessage": "L'espace de nom contient des caractères non valides",
"xpack.fleet.namespaceValidation.lowercaseErrorMessage": "L'espace de nom doit être en minuscules",
"xpack.fleet.namespaceValidation.requiredErrorMessage": "L'espace de nom est obligatoire",
"xpack.fleet.namespaceValidation.tooLongErrorMessage": "L'espace de nom ne peut pas dépasser 100 octets",
"xpack.fleet.newEnrollmentKey.cancelButtonLabel": "Annuler",
"xpack.fleet.newEnrollmentKey.helpText": "Un ID de jeton sera utilisé si ce champ est laissé vide.",
"xpack.fleet.newEnrollmentKey.keyCreatedToasts": "Jeton d'enregistrement créé",

View file

@ -18691,10 +18691,6 @@
"xpack.fleet.multiRowInput.addRow": "行の追加",
"xpack.fleet.multiRowInput.deleteButton": "行の削除",
"xpack.fleet.multiTextInput.addRow": "行の追加",
"xpack.fleet.namespaceValidation.invalidCharactersErrorMessage": "名前空間に無効な文字が含まれています",
"xpack.fleet.namespaceValidation.lowercaseErrorMessage": "名前空間は小文字で指定する必要があります",
"xpack.fleet.namespaceValidation.requiredErrorMessage": "名前空間は必須です",
"xpack.fleet.namespaceValidation.tooLongErrorMessage": "名前空間は100バイト以下でなければなりません",
"xpack.fleet.newEnrollmentKey.cancelButtonLabel": "キャンセル",
"xpack.fleet.newEnrollmentKey.helpText": "これを空にすると、トークンIDが使用されます。",
"xpack.fleet.newEnrollmentKey.keyCreatedToasts": "登録トークンが作成されました",

View file

@ -18720,10 +18720,6 @@
"xpack.fleet.multiRowInput.addRow": "添加行",
"xpack.fleet.multiRowInput.deleteButton": "删除行",
"xpack.fleet.multiTextInput.addRow": "添加行",
"xpack.fleet.namespaceValidation.invalidCharactersErrorMessage": "命名空间包含无效字符",
"xpack.fleet.namespaceValidation.lowercaseErrorMessage": "命名空间必须小写",
"xpack.fleet.namespaceValidation.requiredErrorMessage": "“命名空间”必填",
"xpack.fleet.namespaceValidation.tooLongErrorMessage": "命名空间不能超过 100 个字节",
"xpack.fleet.newEnrollmentKey.cancelButtonLabel": "取消",
"xpack.fleet.newEnrollmentKey.helpText": "此项留空时,将使用令牌 ID。",
"xpack.fleet.newEnrollmentKey.keyCreatedToasts": "注册令牌已创建",

View file

@ -79,7 +79,7 @@ export default function (providerContext: FtrProviderContext) {
.send(policy)
.expect(expectStatusCode);
return res.body.item;
return expectStatusCode === 200 ? res.body.item : res;
};
const createAgentPolicy = async (name = 'Input Package Test 3') => {
@ -117,7 +117,10 @@ export default function (providerContext: FtrProviderContext) {
setupFleetAndAgents(providerContext);
it('should rollback package install on package policy create failure', async () => {
await createPackagePolicyWithDataset(agentPolicyId, 'test*', 400);
const res = await createPackagePolicyWithDataset(agentPolicyId, 'test*', 400);
expect(res.body.message).to.eql(
'Package policy is invalid: inputs.logfile.streams.input_package_upgrade.logs.vars.data_stream.dataset: Dataset contains invalid characters'
);
const pkg = await getPackage(PACKAGE_NAME, START_VERSION);
expect(pkg?.status).to.eql('not_installed');