[Ingest Manager] Add namespace max length limit (#78522) (#78818)

* Add namespace limit validation rule

* Fix tests

* Revise error message

* Remove console.log
This commit is contained in:
Jen Huang 2020-09-29 11:17:26 -07:00 committed by GitHub
parent 63cfbdba5a
commit d9dfc5f39f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 51 deletions

View file

@ -7,22 +7,32 @@ import { isValidNamespace } from './is_valid_namespace';
describe('Ingest Manager - isValidNamespace', () => {
it('returns true for valid namespaces', () => {
expect(isValidNamespace('default')).toBe(true);
expect(isValidNamespace('namespace-with-dash')).toBe(true);
expect(isValidNamespace('123')).toBe(true);
expect(isValidNamespace('default').valid).toBe(true);
expect(isValidNamespace('namespace-with-dash').valid).toBe(true);
expect(isValidNamespace('123').valid).toBe(true);
expect(isValidNamespace('testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀').valid).toBe(
true
);
});
it('returns false for invalid namespaces', () => {
expect(isValidNamespace('Default')).toBe(false);
expect(isValidNamespace('namespace with spaces')).toBe(false);
expect(isValidNamespace('foo/bar')).toBe(false);
expect(isValidNamespace('foo\\bar')).toBe(false);
expect(isValidNamespace('foo*bar')).toBe(false);
expect(isValidNamespace('foo?bar')).toBe(false);
expect(isValidNamespace('foo"bar')).toBe(false);
expect(isValidNamespace('foo<bar')).toBe(false);
expect(isValidNamespace('foo|bar')).toBe(false);
expect(isValidNamespace('foo,bar')).toBe(false);
expect(isValidNamespace('foo#bar')).toBe(false);
expect(isValidNamespace('').valid).toBe(false);
expect(isValidNamespace(' ').valid).toBe(false);
expect(isValidNamespace('Default').valid).toBe(false);
expect(isValidNamespace('namespace with spaces').valid).toBe(false);
expect(isValidNamespace('foo/bar').valid).toBe(false);
expect(isValidNamespace('foo\\bar').valid).toBe(false);
expect(isValidNamespace('foo*bar').valid).toBe(false);
expect(isValidNamespace('foo?bar').valid).toBe(false);
expect(isValidNamespace('foo"bar').valid).toBe(false);
expect(isValidNamespace('foo<bar').valid).toBe(false);
expect(isValidNamespace('foo|bar').valid).toBe(false);
expect(isValidNamespace('foo,bar').valid).toBe(false);
expect(isValidNamespace('foo#bar').valid).toBe(false);
expect(
isValidNamespace(
'testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀'
).valid
).toBe(false);
});
});

View file

@ -3,15 +3,49 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
// Namespace string eventually becomes part of an index name. This method partially implements index name rules from
// https://github.com/elastic/elasticsearch/blob/master/docs/reference/indices/create-index.asciidoc
export function isValidNamespace(namespace: string) {
return (
typeof namespace === 'string' &&
// Lowercase only
namespace === namespace.toLowerCase() &&
// Cannot include \, /, *, ?, ", <, >, |, space character, comma, #, :
/^[^\*\\/\?"<>|\s,#:]+$/.test(namespace)
);
// and implements a limit based on https://github.com/elastic/kibana/issues/75846
export function isValidNamespace(namespace: string): { valid: boolean; error?: string } {
if (!namespace.trim()) {
return {
valid: false,
error: i18n.translate('xpack.ingestManager.namespaceValidation.requiredErrorMessage', {
defaultMessage: 'Namespace is required',
}),
};
} else if (namespace !== namespace.toLowerCase()) {
return {
valid: false,
error: i18n.translate('xpack.ingestManager.namespaceValidation.lowercaseErrorMessage', {
defaultMessage: 'Namespace must be lowercase',
}),
};
} else if (/[\*\\/\?"<>|\s,#:]+/.test(namespace)) {
return {
valid: false,
error: i18n.translate(
'xpack.ingestManager.namespaceValidation.invalidCharactersErrorMessage',
{
defaultMessage: 'Namespace contains invalid characters',
}
),
};
}
// 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)
) {
return {
valid: false,
error: i18n.translate('xpack.ingestManager.namespaceValidation.tooLongErrorMessage', {
defaultMessage: 'Namespace cannot be more than 100 bytes',
}),
};
}
return { valid: true };
}

View file

@ -28,7 +28,7 @@ import { isValidNamespace } from '../../../services';
import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider';
interface ValidationResults {
[key: string]: JSX.Element[];
[key: string]: Array<JSX.Element | string>;
}
const StyledEuiAccordion = styled(EuiAccordion)`
@ -41,6 +41,7 @@ export const agentPolicyFormValidation = (
agentPolicy: Partial<NewAgentPolicy | AgentPolicy>
): ValidationResults => {
const errors: ValidationResults = {};
const namespaceValidation = isValidNamespace(agentPolicy.namespace || '');
if (!agentPolicy.name?.trim()) {
errors.name = [
@ -51,20 +52,8 @@ export const agentPolicyFormValidation = (
];
}
if (!agentPolicy.namespace?.trim()) {
errors.namespace = [
<FormattedMessage
id="xpack.ingestManager.agentPolicyForm.namespaceRequiredErrorMessage"
defaultMessage="A namespace is required"
/>,
];
} else if (!isValidNamespace(agentPolicy.namespace)) {
errors.namespace = [
<FormattedMessage
id="xpack.ingestManager.agentPolicyForm.namespaceInvalidErrorMessage"
defaultMessage="Namespace contains invalid characters"
/>,
];
if (!namespaceValidation.valid && namespaceValidation.error) {
errors.namespace = [namespaceValidation.error];
}
return errors;

View file

@ -50,6 +50,7 @@ export const validatePackagePolicy = (
namespace: null,
inputs: {},
};
const namespaceValidation = isValidNamespace(packagePolicy.namespace);
if (!packagePolicy.name.trim()) {
validationResults.name = [
@ -59,18 +60,8 @@ export const validatePackagePolicy = (
];
}
if (!packagePolicy.namespace.trim()) {
validationResults.namespace = [
i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceRequiredErrorMessage', {
defaultMessage: 'Namespace is required',
}),
];
} else if (!isValidNamespace(packagePolicy.namespace)) {
validationResults.namespace = [
i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceInvalidErrorMessage', {
defaultMessage: 'Namespace contains invalid characters',
}),
];
if (!namespaceValidation.valid && namespaceValidation.error) {
validationResults.namespace = [namespaceValidation.error];
}
if (

View file

@ -9,8 +9,9 @@ import { isValidNamespace } from '../../../common';
export const NamespaceSchema = schema.string({
minLength: 1,
validate: (value) => {
if (!isValidNamespace(value)) {
return 'Namespace contains invalid characters';
const namespaceValidation = isValidNamespace(value || '');
if (!namespaceValidation.valid && namespaceValidation.error) {
return namespaceValidation.error;
}
},
});

View file

@ -101,6 +101,25 @@ export default function ({ getService }: FtrProviderContext) {
},
})
.expect(400);
await supertest
.post(`/api/ingest_manager/package_policies`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-1',
description: '',
namespace:
'testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀',
policy_id: agentPolicyId,
enabled: true,
output_id: '',
inputs: [],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(400);
} else {
warnAndSkipTest(this, log);
}