From 5ecde4b053d77f86f05f1b04c8417c6a5e4c5d92 Mon Sep 17 00:00:00 2001 From: Tomasz Ciecierski Date: Thu, 19 May 2022 09:21:50 +0200 Subject: [PATCH] [Osquery] Add multiline query support (#131224) --- .../utils/build_query/remove_multilines.ts | 9 + .../plugins/osquery/public/editor/index.tsx | 4 +- .../packs/pack_queries_status_table.tsx | 267 ++++++++++++------ .../queries/ecs_mapping_editor_field.tsx | 5 +- .../server/routes/pack/create_pack_route.ts | 4 +- .../server/routes/pack/update_pack_route.ts | 4 +- .../osquery/server/routes/pack/utils.test.ts | 57 ++++ .../osquery/server/routes/pack/utils.ts | 11 +- .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - x-pack/test/api_integration/apis/index.ts | 1 + .../api_integration/apis/osquery/index.js | 12 + .../api_integration/apis/osquery/packs.ts | 152 ++++++++++ 14 files changed, 428 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts create mode 100644 x-pack/plugins/osquery/server/routes/pack/utils.test.ts create mode 100644 x-pack/test/api_integration/apis/osquery/index.js create mode 100644 x-pack/test/api_integration/apis/osquery/packs.ts diff --git a/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts b/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts new file mode 100644 index 0000000000000..66208a0c7524d --- /dev/null +++ b/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const removeMultilines = (query: string): string => + query.replaceAll('\n', ' ').replaceAll(/ +/g, ' '); diff --git a/x-pack/plugins/osquery/public/editor/index.tsx b/x-pack/plugins/osquery/public/editor/index.tsx index 191fe1e7ea548..9718e80926d06 100644 --- a/x-pack/plugins/osquery/public/editor/index.tsx +++ b/x-pack/plugins/osquery/public/editor/index.tsx @@ -35,9 +35,7 @@ const OsqueryEditorComponent: React.FC = ({ }) => { const [editorValue, setEditorValue] = useState(defaultValue ?? ''); - useDebounce(() => onChange(editorValue.replaceAll('\n', ' ').replaceAll(' ', ' ')), 500, [ - editorValue, - ]); + useDebounce(() => onChange(editorValue), 500, [editorValue]); useEffect(() => setEditorValue(defaultValue), [defaultValue]); diff --git a/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx b/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx index 70282ab0819fd..3aa345f986493 100644 --- a/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx +++ b/x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx @@ -21,7 +21,7 @@ import { EuiPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react'; +import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react'; import moment from 'moment-timezone'; import type { @@ -32,6 +32,7 @@ import type { } from '@kbn/lens-plugin/public'; import { DOCUMENT_FIELD_NAME as RECORDS_FIELD } from '@kbn/lens-plugin/common/constants'; import { FilterStateStore, DataView } from '@kbn/data-plugin/common'; +import { removeMultilines } from '../../common/utils/build_query/remove_multilines'; import { useKibana } from '../common/lib/kibana'; import { OsqueryManagerPackagePolicyInputStream } from '../../common/types'; import { ScheduledQueryErrorsTable } from './scheduled_query_errors_table'; @@ -384,6 +385,13 @@ const ScheduledQueryExpandedContent = React.memo = ({ actionId, - queryId, interval, logsDataView, - toggleErrors, - expanded, }) => { const { data: lastResultsData, isLoading } = usePackQueryLastResults({ actionId, @@ -406,22 +411,11 @@ const ScheduledQueryLastResults: React.FC = ({ logsDataView, }); - const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({ - actionId, - interval, - logsDataView, - }); - - const handleErrorsToggle = useCallback( - () => toggleErrors({ queryId, interval }), - [queryId, interval, toggleErrors] - ); - - if (isLoading || errorsLoading) { + if (isLoading) { return ; } - if (!lastResultsData && !errorsData?.total) { + if (!lastResultsData) { return <>{'-'}; } @@ -448,73 +442,115 @@ const ScheduledQueryLastResults: React.FC = ({ '-' )} - - - - - {lastResultsData?.docCount ?? 0} - - - - - - - + + ); +}; - - - - - {lastResultsData?.uniqueAgentsCount ?? 0} - - - - - - +const DocsColumnResults: React.FC = ({ + actionId, + interval, + logsDataView, +}) => { + const { data: lastResultsData, isLoading } = usePackQueryLastResults({ + actionId, + interval, + logsDataView, + }); + if (isLoading) { + return ; + } + + if (!lastResultsData) { + return <>{'-'}; + } + + return ( + + + + {lastResultsData?.docCount ?? 0} + + + ); +}; - - - - - {errorsData?.total ?? 0} - - - - - {' '} - - - - - - - +const AgentsColumnResults: React.FC = ({ + actionId, + interval, + logsDataView, +}) => { + const { data: lastResultsData, isLoading } = usePackQueryLastResults({ + actionId, + interval, + logsDataView, + }); + if (isLoading) { + return ; + } + + if (!lastResultsData) { + return <>{'-'}; + } + + return ( + + + + {lastResultsData?.uniqueAgentsCount ?? 0} + ); }; +const ErrorsColumnResults: React.FC = ({ + actionId, + interval, + queryId, + toggleErrors, + expanded, + logsDataView, +}) => { + const handleErrorsToggle = useCallback( + () => toggleErrors({ queryId, interval }), + [toggleErrors, queryId, interval] + ); + + const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({ + actionId, + interval, + logsDataView, + }); + if (errorsLoading) { + return ; + } + + if (!errorsData?.total) { + return <>{'-'}; + } + + return ( + + + + + {errorsData?.total ?? 0} + + + + + + + + + ); +}; + const getPackActionId = (actionId: string, packName: string) => `pack_${packName}_${actionId}`; interface PackViewInActionProps { @@ -625,14 +661,18 @@ const PackQueriesStatusTableComponent: React.FC = ( fetchLogsDataView(); }, [dataViews]); - const renderQueryColumn = useCallback( - (query: string) => ( - - {query} - - ), - [] - ); + const renderQueryColumn = useCallback((query: string, item) => { + const singleLine = removeMultilines(query); + const content = singleLine.length > 55 ? `${singleLine.substring(0, 55)}...` : singleLine; + + return ( + {query}}> + + {content} + + + ); + }, []); const toggleErrors = useCallback( ({ queryId, interval }: { queryId: string; interval: number }) => { @@ -658,14 +698,44 @@ const PackQueriesStatusTableComponent: React.FC = ( (item) => ( + ), + [packName, logsDataView] + ); + const renderDocsColumn = useCallback( + (item) => ( + + ), + [logsDataView, packName] + ); + const renderAgentsColumn = useCallback( + (item) => ( + + ), + [logsDataView, packName] + ); + const renderErrorsColumn = useCallback( + (item) => ( + ), - [itemIdToExpandedRowMap, packName, toggleErrors, logsDataView] + [itemIdToExpandedRowMap, logsDataView, packName, toggleErrors] ); const renderDiscoverResultsAction = useCallback( @@ -705,6 +775,7 @@ const PackQueriesStatusTableComponent: React.FC = ( defaultMessage: 'ID', }), width: '15%', + truncateText: true, }, { field: 'interval', @@ -719,13 +790,32 @@ const PackQueriesStatusTableComponent: React.FC = ( defaultMessage: 'Query', }), render: renderQueryColumn, - width: '20%', + width: '40%', }, { name: i18n.translate('xpack.osquery.pack.queriesTable.lastResultsColumnTitle', { defaultMessage: 'Last results', }), render: renderLastResultsColumn, + width: '12%', + }, + { + name: i18n.translate('xpack.osquery.pack.queriesTable.docsResultsColumnTitle', { + defaultMessage: 'Docs', + }), + render: renderDocsColumn, + }, + { + name: i18n.translate('xpack.osquery.pack.queriesTable.agentsResultsColumnTitle', { + defaultMessage: 'Agents', + }), + render: renderAgentsColumn, + }, + { + name: i18n.translate('xpack.osquery.pack.queriesTable.errorsResultsColumnTitle', { + defaultMessage: 'Errors', + }), + render: renderErrorsColumn, }, { name: i18n.translate('xpack.osquery.pack.queriesTable.viewResultsColumnTitle', { @@ -745,6 +835,9 @@ const PackQueriesStatusTableComponent: React.FC = ( [ renderQueryColumn, renderLastResultsColumn, + renderDocsColumn, + renderAgentsColumn, + renderErrorsColumn, renderDiscoverResultsAction, renderLensResultsAction, ] diff --git a/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx b/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx index 5aaab625d3ef5..15dca629821b2 100644 --- a/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/ecs_mapping_editor_field.tsx @@ -59,6 +59,7 @@ import { FormArrayField, } from '../../shared_imports'; import { OsqueryIcon } from '../../components/osquery_icon'; +import { removeMultilines } from '../../../common/utils/build_query/remove_multilines'; export const CommonUseField = getUseField({ component: Field }); @@ -773,11 +774,13 @@ export const ECSMappingEditorField = React.memo( return; } + const oneLineQuery = removeMultilines(query); + // eslint-disable-next-line @typescript-eslint/no-explicit-any let ast: Record | undefined; try { - ast = sqliteParser(query)?.statement?.[0]; + ast = sqliteParser(oneLineQuery)?.statement?.[0]; } catch (e) { return; } diff --git a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts index bf34152078582..67ae97b9af5cd 100644 --- a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts @@ -19,7 +19,7 @@ import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { PLUGIN_ID } from '../../../common'; import { packSavedObjectType } from '../../../common/types'; -import { convertPackQueriesToSO } from './utils'; +import { convertPackQueriesToSO, convertSOQueriesToPack } from './utils'; import { getInternalSavedObjectsClient } from '../../usage/collector'; export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { @@ -138,7 +138,7 @@ export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppConte } set(draft, `inputs[0].config.osquery.value.packs.${packSO.attributes.name}`, { - queries, + queries: convertSOQueriesToPack(queries, { removeMultiLines: true }), }); return draft; diff --git a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts index 82d880c70fbd6..cb79165f3dca1 100644 --- a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts @@ -282,7 +282,9 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte draft, `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, { - queries: updatedPackSO.attributes.queries, + queries: convertSOQueriesToPack(updatedPackSO.attributes.queries, { + removeMultiLines: true, + }), } ); diff --git a/x-pack/plugins/osquery/server/routes/pack/utils.test.ts b/x-pack/plugins/osquery/server/routes/pack/utils.test.ts new file mode 100644 index 0000000000000..97905fde6bf02 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/pack/utils.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { convertSOQueriesToPack } from './utils'; + +const getTestQueries = (additionalFields?: Record, packName = 'default') => ({ + [packName]: { + ...additionalFields, + query: + 'select u.username,\n' + + ' p.pid,\n' + + ' p.name,\n' + + ' pos.local_address,\n' + + ' pos.local_port,\n' + + ' p.path,\n' + + ' p.cmdline,\n' + + ' pos.remote_address,\n' + + ' pos.remote_port\n' + + 'from processes as p\n' + + 'join users as u\n' + + ' on u.uid=p.uid\n' + + 'join process_open_sockets as pos\n' + + ' on pos.pid=p.pid\n' + + "where pos.remote_port !='0'\n" + + 'limit 1000;', + interval: 3600, + }, +}); + +const oneLiner = { + default: { + ecs_mapping: {}, + interval: 3600, + query: `select u.username, p.pid, p.name, pos.local_address, pos.local_port, p.path, p.cmdline, pos.remote_address, pos.remote_port from processes as p join users as u on u.uid=p.uid join process_open_sockets as pos on pos.pid=p.pid where pos.remote_port !='0' limit 1000;`, + }, +}; + +describe('Pack utils', () => { + describe('convertSOQueriesToPack', () => { + test('converts to pack with empty ecs_mapping', () => { + const convertedQueries = convertSOQueriesToPack(getTestQueries()); + expect(convertedQueries).toStrictEqual(getTestQueries({ ecs_mapping: {} })); + }); + test('converts to pack with converting query to single line', () => { + const convertedQueries = convertSOQueriesToPack(getTestQueries(), { removeMultiLines: true }); + expect(convertedQueries).toStrictEqual(oneLiner); + }); + test('converts to object with pack names after query.id', () => { + const convertedQueries = convertSOQueriesToPack(getTestQueries({ id: 'testId' })); + expect(convertedQueries).toStrictEqual(getTestQueries({ ecs_mapping: {} }, 'testId')); + }); + }); +}); diff --git a/x-pack/plugins/osquery/server/routes/pack/utils.ts b/x-pack/plugins/osquery/server/routes/pack/utils.ts index 9edb750263209..84466a6ce4ad1 100644 --- a/x-pack/plugins/osquery/server/routes/pack/utils.ts +++ b/x-pack/plugins/osquery/server/routes/pack/utils.ts @@ -6,6 +6,7 @@ */ import { pick, reduce } from 'lodash'; +import { removeMultilines } from '../../../common/utils/build_query/remove_multilines'; import { convertECSMappingToArray, convertECSMappingToObject } from '../utils'; // @ts-expect-error update types @@ -27,13 +28,15 @@ export const convertPackQueriesToSO = (queries) => ); // @ts-expect-error update types -export const convertSOQueriesToPack = (queries) => +export const convertSOQueriesToPack = (queries, options?: { removeMultiLines?: boolean }) => reduce( queries, // eslint-disable-next-line @typescript-eslint/naming-convention - (acc, { id: queryId, ecs_mapping, ...query }) => { - acc[queryId] = { - ...query, + (acc, { id: queryId, ecs_mapping, query, ...rest }, key) => { + const index = queryId ? queryId : key; + acc[index] = { + ...rest, + query: options?.removeMultiLines ? removeMultilines(query) : query, ecs_mapping: convertECSMappingToObject(ecs_mapping), }; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 258ea8b4bdeea..a9d350146c0d9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21991,9 +21991,6 @@ "xpack.osquery.packUploader.unsupportedFileTypeText": "Le type de fichier {fileType} n'est pas pris en charge, veuillez charger le fichier config {supportedFileTypes}", "xpack.osquery.permissionDeniedErrorMessage": "Vous n'êtes pas autorisé à accéder à cette page.", "xpack.osquery.permissionDeniedErrorTitle": "Autorisation refusée", - "xpack.osquery.queriesStatusTable.agentsLabelText": "{count, plural, one {Agent} other {Agents}}", - "xpack.osquery.queriesStatusTable.documentLabelText": "{count, plural, one {Document} other {Documents}}", - "xpack.osquery.queriesStatusTable.errorsLabelText": "{count, plural, one {Erreur} other {Erreurs}}", "xpack.osquery.queriesTable.osqueryVersionAllLabel": "TOUS", "xpack.osquery.queryFlyoutForm.addFormTitle": "Attacher la recherche suivante", "xpack.osquery.queryFlyoutForm.cancelButtonLabel": "Annuler", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 30b0d5ad9a48b..89813c1104606 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22129,9 +22129,6 @@ "xpack.osquery.packUploader.unsupportedFileTypeText": "ファイルタイプ{fileType}はサポートされていません。{supportedFileTypes}構成ファイルをアップロードしてください", "xpack.osquery.permissionDeniedErrorMessage": "このページへのアクセスが許可されていません。", "xpack.osquery.permissionDeniedErrorTitle": "パーミッションが拒否されました", - "xpack.osquery.queriesStatusTable.agentsLabelText": "{count, plural, other {エージェント}}", - "xpack.osquery.queriesStatusTable.documentLabelText": "{count, plural, other {ドキュメント}}", - "xpack.osquery.queriesStatusTable.errorsLabelText": "{count, plural, other {エラー}}", "xpack.osquery.queriesTable.osqueryVersionAllLabel": "すべて", "xpack.osquery.queryFlyoutForm.addFormTitle": "次のクエリを関連付ける", "xpack.osquery.queryFlyoutForm.cancelButtonLabel": "キャンセル", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index cff374e41bf98..a9278d13031f4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22160,9 +22160,6 @@ "xpack.osquery.packUploader.unsupportedFileTypeText": "文件类型 {fileType} 不受支持,请上传 {supportedFileTypes} 配置文件", "xpack.osquery.permissionDeniedErrorMessage": "您无权访问此页面。", "xpack.osquery.permissionDeniedErrorTitle": "权限被拒绝", - "xpack.osquery.queriesStatusTable.agentsLabelText": "{count, plural, other {代理}}", - "xpack.osquery.queriesStatusTable.documentLabelText": "{count, plural, other {文档}}", - "xpack.osquery.queriesStatusTable.errorsLabelText": "{count, plural, other {错误}}", "xpack.osquery.queriesTable.osqueryVersionAllLabel": "全部", "xpack.osquery.queryFlyoutForm.addFormTitle": "附加下一个查询", "xpack.osquery.queryFlyoutForm.cancelButtonLabel": "取消", diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index b3566ff30aea2..6bec2ebe80a13 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -35,5 +35,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./ml')); loadTestFile(require.resolve('./watcher')); loadTestFile(require.resolve('./logs_ui')); + loadTestFile(require.resolve('./osquery')); }); } diff --git a/x-pack/test/api_integration/apis/osquery/index.js b/x-pack/test/api_integration/apis/osquery/index.js new file mode 100644 index 0000000000000..afe684aa9bd68 --- /dev/null +++ b/x-pack/test/api_integration/apis/osquery/index.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export default function ({ loadTestFile }) { + describe('Osquery Endpoints', () => { + loadTestFile(require.resolve('./packs')); + }); +} diff --git a/x-pack/test/api_integration/apis/osquery/packs.ts b/x-pack/test/api_integration/apis/osquery/packs.ts new file mode 100644 index 0000000000000..543c01ac92c41 --- /dev/null +++ b/x-pack/test/api_integration/apis/osquery/packs.ts @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const getDefaultPack = ({ policyIds = [] }: { policyIds?: string[] }) => ({ + name: 'TestPack', + description: 'TestPack Description', + enabled: true, + policy_ids: policyIds, + queries: { + testQuery: { + query: multiLineQuery, + interval: 600, + platform: 'windows', + version: '1', + }, + }, +}); + +const singleLineQuery = + "select u.username, p.pid, p.name, pos.local_address, pos.local_port, p.path, p.cmdline, pos.remote_address, pos.remote_port from processes as p join users as u on u.uid=p.uid join process_open_sockets as pos on pos.pid=p.pid where pos.remote_port !='0' limit 1000;"; +const multiLineQuery = `select u.username, + p.pid, + p.name, + pos.local_address, + pos.local_port, + p.path, + p.cmdline, + pos.remote_address, + pos.remote_port +from processes as p +join users as u + on u.uid=p.uid +join process_open_sockets as pos + on pos.pid=p.pid +where pos.remote_port !='0' +limit 1000;`; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Packs', () => { + let packId: string = ''; + let hostedPolicy: Record; + let packagePolicyId: string; + before(async () => { + await getService('esArchiver').load('x-pack/test/functional/es_archives/empty_kibana'); + await getService('esArchiver').load( + 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' + ); + }); + after(async () => { + await getService('esArchiver').unload('x-pack/test/functional/es_archives/empty_kibana'); + await getService('esArchiver').unload( + 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' + ); + }); + + after(async function () { + await supertest + .post(`/api/fleet/agent_policies/delete`) + .set('kbn-xsrf', 'xxxx') + .send({ agentPolicyId: hostedPolicy.id }); + }); + + it('create route should return 200 and multi line query, but single line query in packs config', async () => { + const { + body: { item: agentPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: `Hosted policy from ${Date.now()}`, + namespace: 'default', + }); + hostedPolicy = agentPolicy; + + const { + body: { item: packagePolicy }, + } = await supertest + .post('/api/fleet/package_policies') + .set('kbn-xsrf', 'true') + .send({ + enabled: true, + package: { + name: 'osquery_manager', + version: '1.2.1', + title: 'test', + }, + inputs: [], + namespace: 'default', + output_id: '', + policy_id: hostedPolicy.id, + name: 'TEST', + description: '123', + id: '123', + }); + packagePolicyId = packagePolicy.id; + + const createPackResponse = await supertest + .post('/internal/osquery/packs') + .set('kbn-xsrf', 'true') + .send(getDefaultPack({ policyIds: [hostedPolicy.id] })); + + packId = createPackResponse.body.id; + expect(createPackResponse.status).to.be(200); + + const pack = await supertest.get('/internal/osquery/packs/' + packId).set('kbn-xsrf', 'true'); + + expect(pack.status).to.be(200); + expect(pack.body.queries.testQuery.query).to.be(multiLineQuery); + + const { + body: { + item: { inputs }, + }, + } = await supertest.get(`/api/fleet/package_policies/${packagePolicyId}`); + + expect(inputs[0].config.osquery.value.packs.TestPack.queries.testQuery.query).to.be( + singleLineQuery + ); + }); + + it('update route should return 200 and multi line query, but single line query in packs config', async () => { + const updatePackResponse = await supertest + .put('/internal/osquery/packs/' + packId) + .set('kbn-xsrf', 'true') + .send(getDefaultPack({ policyIds: [hostedPolicy.id] })); + + expect(updatePackResponse.status).to.be(200); + expect(updatePackResponse.body.id).to.be(packId); + const pack = await supertest.get('/internal/osquery/packs/' + packId).set('kbn-xsrf', 'true'); + + expect(pack.body.queries.testQuery.query).to.be(multiLineQuery); + const { + body: { + item: { inputs }, + }, + } = await supertest.get(`/api/fleet/package_policies/${packagePolicyId}`); + + expect(inputs[0].config.osquery.value.packs.TestPack.queries.testQuery.query).to.be( + singleLineQuery + ); + }); + }); +}