[Ingest Manager] Add namespace validation (#75381)
* Add namespace validation on APIs and UI * Add test coverage * Fix imports * Fix schema * Rename to policy * Fix typo
This commit is contained in:
parent
5308cc7100
commit
3201efe797
|
@ -10,3 +10,4 @@ export { storedPackagePoliciesToAgentInputs } from './package_policies_to_agent_
|
||||||
export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml';
|
export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml';
|
||||||
export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package';
|
export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package';
|
||||||
export { decodeCloudId } from './decode_cloud_id';
|
export { decodeCloudId } from './decode_cloud_id';
|
||||||
|
export { isValidNamespace } from './is_valid_namespace';
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
);
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { NewAgentPolicy, AgentPolicy } from '../../../types';
|
import { NewAgentPolicy, AgentPolicy } from '../../../types';
|
||||||
|
import { isValidNamespace } from '../../../services';
|
||||||
import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider';
|
import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider';
|
||||||
|
|
||||||
interface ValidationResults {
|
interface ValidationResults {
|
||||||
|
@ -57,6 +58,13 @@ export const agentPolicyFormValidation = (
|
||||||
defaultMessage="A namespace is required"
|
defaultMessage="A namespace is required"
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
||||||
|
} else if (!isValidNamespace(agentPolicy.namespace)) {
|
||||||
|
errors.namespace = [
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.ingestManager.agentPolicyForm.namespaceInvalidErrorMessage"
|
||||||
|
defaultMessage="Namespace contains invalid characters"
|
||||||
|
/>,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { safeLoad } from 'js-yaml';
|
import { safeLoad } from 'js-yaml';
|
||||||
import { getFlattenedObject } from '../../../../services';
|
import { getFlattenedObject, isValidNamespace } from '../../../../services';
|
||||||
import {
|
import {
|
||||||
NewPackagePolicy,
|
NewPackagePolicy,
|
||||||
PackagePolicyInput,
|
PackagePolicyInput,
|
||||||
|
@ -65,6 +65,12 @@ export const validatePackagePolicy = (
|
||||||
defaultMessage: 'Namespace is required',
|
defaultMessage: 'Namespace is required',
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
} else if (!isValidNamespace(packagePolicy.namespace)) {
|
||||||
|
validationResults.namespace = [
|
||||||
|
i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceInvalidErrorMessage', {
|
||||||
|
defaultMessage: 'Namespace contains invalid characters',
|
||||||
|
}),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -24,4 +24,5 @@ export {
|
||||||
fullAgentPolicyToYaml,
|
fullAgentPolicyToYaml,
|
||||||
isPackageLimited,
|
isPackageLimited,
|
||||||
doesAgentPolicyAlreadyIncludePackage,
|
doesAgentPolicyAlreadyIncludePackage,
|
||||||
|
isValidNamespace,
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
import { PackagePolicySchema } from './package_policy';
|
import { PackagePolicySchema, NamespaceSchema } from './package_policy';
|
||||||
import { AgentPolicyStatus } from '../../../common';
|
import { AgentPolicyStatus } from '../../../common';
|
||||||
|
|
||||||
const AgentPolicyBaseSchema = {
|
const AgentPolicyBaseSchema = {
|
||||||
name: schema.string({ minLength: 1 }),
|
name: schema.string({ minLength: 1 }),
|
||||||
namespace: schema.string({ minLength: 1 }),
|
namespace: NamespaceSchema,
|
||||||
description: schema.maybe(schema.string()),
|
description: schema.maybe(schema.string()),
|
||||||
monitoring_enabled: schema.maybe(
|
monitoring_enabled: schema.maybe(
|
||||||
schema.arrayOf(schema.oneOf([schema.literal('logs'), schema.literal('metrics')]))
|
schema.arrayOf(schema.oneOf([schema.literal('logs'), schema.literal('metrics')]))
|
||||||
|
|
|
@ -4,6 +4,16 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import { isValidNamespace } from '../../../common';
|
||||||
|
|
||||||
|
export const NamespaceSchema = schema.string({
|
||||||
|
minLength: 1,
|
||||||
|
validate: (value) => {
|
||||||
|
if (!isValidNamespace(value)) {
|
||||||
|
return 'Namespace contains invalid characters';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const ConfigRecordSchema = schema.recordOf(
|
const ConfigRecordSchema = schema.recordOf(
|
||||||
schema.string(),
|
schema.string(),
|
||||||
|
@ -16,7 +26,7 @@ const ConfigRecordSchema = schema.recordOf(
|
||||||
const PackagePolicyBaseSchema = {
|
const PackagePolicyBaseSchema = {
|
||||||
name: schema.string(),
|
name: schema.string(),
|
||||||
description: schema.maybe(schema.string()),
|
description: schema.maybe(schema.string()),
|
||||||
namespace: schema.string({ minLength: 1 }),
|
namespace: NamespaceSchema,
|
||||||
policy_id: schema.string(),
|
policy_id: schema.string(),
|
||||||
enabled: schema.boolean(),
|
enabled: schema.boolean(),
|
||||||
package: schema.maybe(
|
package: schema.maybe(
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
expect(apiResponse.success).to.be(true);
|
expect(apiResponse.success).to.be(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 with an invalid namespace', async () => {
|
it('should return a 400 with an empty namespace', async () => {
|
||||||
await supertest
|
await supertest
|
||||||
.post(`/api/ingest_manager/agent_policies`)
|
.post(`/api/ingest_manager/agent_policies`)
|
||||||
.set('kbn-xsrf', 'xxxx')
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
@ -36,6 +36,17 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
})
|
})
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return a 400 with an invalid namespace', async () => {
|
||||||
|
await supertest
|
||||||
|
.post(`/api/ingest_manager/agent_policies`)
|
||||||
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
.send({
|
||||||
|
name: 'TEST',
|
||||||
|
namespace: 'InvalidNamespace',
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /api/ingest_manager/agent_policies/{agentPolicyId}/copy', () => {
|
describe('POST /api/ingest_manager/agent_policies/{agentPolicyId}/copy', () => {
|
||||||
|
|
|
@ -59,7 +59,7 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a 400 with an invalid namespace', async function () {
|
it('should return a 400 with an empty namespace', async function () {
|
||||||
if (server.enabled) {
|
if (server.enabled) {
|
||||||
await supertest
|
await supertest
|
||||||
.post(`/api/ingest_manager/package_policies`)
|
.post(`/api/ingest_manager/package_policies`)
|
||||||
|
@ -84,6 +84,31 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return a 400 with an invalid namespace', async function () {
|
||||||
|
if (server.enabled) {
|
||||||
|
await supertest
|
||||||
|
.post(`/api/ingest_manager/package_policies`)
|
||||||
|
.set('kbn-xsrf', 'xxxx')
|
||||||
|
.send({
|
||||||
|
name: 'filetest-1',
|
||||||
|
description: '',
|
||||||
|
namespace: 'InvalidNamespace',
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should not allow multiple limited packages on the same agent policy', async function () {
|
it('should not allow multiple limited packages on the same agent policy', async function () {
|
||||||
if (server.enabled) {
|
if (server.enabled) {
|
||||||
await supertest
|
await supertest
|
||||||
|
|
Loading…
Reference in a new issue