diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.test.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.test.ts index 0659b8d43a38f..085cf9388d45a 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.test.ts @@ -100,6 +100,52 @@ describe('Create conversation route', () => { expect(result.badRequest).toHaveBeenCalled(); }); + + test('escapes colons when querying for existing titles', async () => { + const request = requestMock.create({ + method: 'post', + path: ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL, + body: { + ...getCreateConversationSchemaMock(), + title: 'test: Malware infection: with credential theft attempt - 2875e', // <-- contains colons + }, + }); + + await server.inject(request, requestContextMock.convertContext(context)); + + expect( + clients.elasticAssistant.getAIAssistantConversationsDataClient.findDocuments + ).toHaveBeenCalledWith({ + fields: ['title'], + filter: + 'users:{ name: "my_username" } AND title:test\\: Malware infection\\: with credential theft attempt - 2875e', + page: 1, + perPage: 100, + }); + }); + + test('escapes quotes when querying for existing titles', async () => { + const request = requestMock.create({ + method: 'post', + path: ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL, + body: { + ...getCreateConversationSchemaMock(), + title: '"Malware infection with credential theft attempt - 2875e"', // <-- contains quotes + }, + }); + + await server.inject(request, requestContextMock.convertContext(context)); + + expect( + clients.elasticAssistant.getAIAssistantConversationsDataClient.findDocuments + ).toHaveBeenCalledWith({ + fields: ['title'], + filter: + 'users:{ name: "my_username" } AND title:\\"Malware infection with credential theft attempt - 2875e\\"', + page: 1, + perPage: 100, + }); + }); }); describe('conversation containing messages', () => { const getMessage = (role: string = 'user') => ({ diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts index f997121f95aa8..a0dcbe04ba1d1 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/create_route.ts @@ -14,6 +14,8 @@ import { API_VERSIONS, } from '@kbn/elastic-assistant-common'; import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; +import { escapeKuery } from '@kbn/es-query'; + import { ElasticAssistantPluginRouter } from '../../types'; import { buildResponse } from '../utils'; import { performChecks } from '../helpers'; @@ -58,10 +60,13 @@ export const createConversationRoute = (router: ElasticAssistantPluginRouter): v const userFilter = currentUser?.username ? `name: "${currentUser?.username}"` : `id: "${currentUser?.profile_uid}"`; + + const escapedTitle = escapeKuery(request.body.title); + const result = await dataClient?.findDocuments({ perPage: 100, page: 1, - filter: `users:{ ${userFilter} } AND title:${request.body.title}`, + filter: `users:{ ${userFilter} } AND title:${escapedTitle}`, fields: ['title'], }); if (result?.data != null && result.total > 0) {