Skip to content

Commit

Permalink
[Fleet] add validation to dataset field in input packages to disallow…
Browse files Browse the repository at this point in the history
… special characters (#182925)

## Summary

Closes #181044

Added validation to allow only valid index names as dataset name in
input packages. Allowing lowercase letters, numbers, dot and underscore
(except in the beginning).

To verify:
- add Custom Logs integration
- modfiy dataset to add invalid characters e.g. *
- verify that the field shows a validation error
- verify that the save button is disabled
- verify that valid dataset names can be used

<img width="968" alt="image"
src="https://github.com/elastic/kibana/assets/90178898/e9cd17f1-6f5b-464c-bc8d-83d2ec42bada">



### 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
  • Loading branch information
juliaElastic authored May 14, 2024
1 parent 230e789 commit be9b46d
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 181 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export {
fleetSetupRouteService,
// Package policy helpers
isValidNamespace,
isValidDataset,
INVALID_NAMESPACE_CHARACTERS,
getFileMetadataIndexName,
getFileDataIndexName,
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/fleet/common/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
52 changes: 43 additions & 9 deletions x-pack/plugins/fleet/common/services/is_valid_namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
}),
};
}
Expand Down
140 changes: 140 additions & 0 deletions x-pack/plugins/fleet/common/services/validate_package_policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
});
});
});
24 changes: 22 additions & 2 deletions x-pack/plugins/fleet/common/services/validate_package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ import type {
RegistryVarsEntry,
} from '../types';

import { DATASET_VAR_NAME } from '../constants';

import {
isValidNamespace,
doesPackageHaveIntegrations,
getNormalizedInputs,
getNormalizedDataStreams,
} from '.';
import { packageHasNoPolicyTemplates } from './policy_template';
import { isValidDataset } from './is_valid_namespace';

type Errors = string[] | null;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = [];

Expand Down Expand Up @@ -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;
};

Expand Down
Loading

0 comments on commit be9b46d

Please sign in to comment.