diff --git a/x-pack/plugins/ingest_manager/common/services/index.ts b/x-pack/plugins/ingest_manager/common/services/index.ts index 28fd15b5ea700..ad739bf9ff844 100644 --- a/x-pack/plugins/ingest_manager/common/services/index.ts +++ b/x-pack/plugins/ingest_manager/common/services/index.ts @@ -10,3 +10,4 @@ export { storedPackagePoliciesToAgentInputs } from './package_policies_to_agent_ export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml'; export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package'; export { decodeCloudId } from './decode_cloud_id'; +export { isValidNamespace } from './is_valid_namespace'; diff --git a/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts b/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts new file mode 100644 index 0000000000000..40f37cc456f94 --- /dev/null +++ b/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts @@ -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, |, space character, comma, #, : + /^[^\*\\/\?"<>|\s,#:]+$/.test(namespace) + ); +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx index a858a53c485b7..b216270aa08f0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx @@ -24,6 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { NewAgentPolicy, AgentPolicy } from '../../../types'; +import { isValidNamespace } from '../../../services'; import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; interface ValidationResults { @@ -57,6 +58,13 @@ export const agentPolicyFormValidation = ( defaultMessage="A namespace is required" />, ]; + } else if (!isValidNamespace(agentPolicy.namespace)) { + errors.namespace = [ + , + ]; } return errors; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts index f375352c5a055..2714f1fe2e6e5 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; import { safeLoad } from 'js-yaml'; -import { getFlattenedObject } from '../../../../services'; +import { getFlattenedObject, isValidNamespace } from '../../../../services'; import { NewPackagePolicy, PackagePolicyInput, @@ -65,6 +65,12 @@ export const validatePackagePolicy = ( defaultMessage: 'Namespace is required', }), ]; + } else if (!isValidNamespace(packagePolicy.namespace)) { + validationResults.namespace = [ + i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceInvalidErrorMessage', { + defaultMessage: 'Namespace contains invalid characters', + }), + ]; } if ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts index 7d426c11c0aab..56179b02c5ba2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts @@ -24,4 +24,5 @@ export { fullAgentPolicyToYaml, isPackageLimited, doesAgentPolicyAlreadyIncludePackage, + isValidNamespace, } from '../../../../common'; diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent_policy.ts b/x-pack/plugins/ingest_manager/server/types/models/agent_policy.ts index 877d8e9e91714..5fff9247d78d9 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent_policy.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent_policy.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { PackagePolicySchema } from './package_policy'; +import { PackagePolicySchema, NamespaceSchema } from './package_policy'; import { AgentPolicyStatus } from '../../../common'; const AgentPolicyBaseSchema = { name: schema.string({ minLength: 1 }), - namespace: schema.string({ minLength: 1 }), + namespace: NamespaceSchema, description: schema.maybe(schema.string()), monitoring_enabled: schema.maybe( schema.arrayOf(schema.oneOf([schema.literal('logs'), schema.literal('metrics')])) diff --git a/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts b/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts index 81bfb599b4c9a..c23918210114e 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts @@ -4,6 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ 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( schema.string(), @@ -16,7 +26,7 @@ const ConfigRecordSchema = schema.recordOf( const PackagePolicyBaseSchema = { name: schema.string(), description: schema.maybe(schema.string()), - namespace: schema.string({ minLength: 1 }), + namespace: NamespaceSchema, policy_id: schema.string(), enabled: schema.boolean(), package: schema.maybe( diff --git a/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts index 5253b19b24d65..b55da0798c5f0 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts @@ -26,7 +26,7 @@ export default function ({ getService }: FtrProviderContext) { 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 .post(`/api/ingest_manager/agent_policies`) .set('kbn-xsrf', 'xxxx') @@ -36,6 +36,17 @@ export default function ({ getService }: FtrProviderContext) { }) .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', () => { diff --git a/x-pack/test/ingest_manager_api_integration/apis/package_policy/create.ts b/x-pack/test/ingest_manager_api_integration/apis/package_policy/create.ts index 2043faa07b1e1..c88f03de59615 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/package_policy/create.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/package_policy/create.ts @@ -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) { await supertest .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 () { if (server.enabled) { await supertest