From dbed9cd1ea9a8a2374023e8ba2b34c310eee08f9 Mon Sep 17 00:00:00 2001 From: Cesare de Cal Date: Fri, 20 Mar 2026 17:00:03 +0100 Subject: [PATCH 1/3] Fix osquery pack and saved query APIs returning 500 instead of 404 for missing resources --- .../server/routes/pack/delete_pack_route.ts | 94 +-- .../server/routes/pack/read_pack_route.ts | 94 +-- .../server/routes/pack/update_pack_route.ts | 586 +++++++++--------- .../saved_query/delete_saved_query_route.ts | 54 +- .../saved_query/read_saved_query_route.ts | 162 ++--- .../saved_query/update_saved_query_route.ts | 218 +++---- 6 files changed, 637 insertions(+), 571 deletions(-) diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts index b05a909d772b9..f9e2abddc9cbe 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts @@ -8,7 +8,7 @@ import { filter, unset } from 'lodash'; import { produce } from 'immer'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-utils'; import type { DeletePacksRequestParamsSchema } from '../../../common/api'; @@ -47,56 +47,66 @@ export const deletePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte }, }, async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + try { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const spaceId = osqueryContext?.service?.getActiveSpace - ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID - : DEFAULT_SPACE_ID; + const spaceId = osqueryContext?.service?.getActiveSpace + ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID + : DEFAULT_SPACE_ID; - const currentPackSO = await spaceScopedClient.get<{ name: string }>( - packSavedObjectType, - request.params.id - ); + const currentPackSO = await spaceScopedClient.get<{ name: string }>( + packSavedObjectType, + request.params.id + ); - await spaceScopedClient.delete(packSavedObjectType, request.params.id, { - refresh: 'wait_for', - }); + await spaceScopedClient.delete(packSavedObjectType, request.params.id, { + refresh: 'wait_for', + }); - const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - })) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) - ); + const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + })) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) + ); - await Promise.all( - currentPackagePolicies.map((packagePolicy) => - packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + await Promise.all( + currentPackagePolicies.map((packagePolicy) => + packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - return draft; - }) + return draft; + }) + ) ) - ) - ); + ); - return response.ok({ - body: {}, - }); + return response.ok({ + body: {}, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Pack ${request.params.id} not found` }, + }); + } + + throw err; + } } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts index 49dd64d78c413..ac1558c41c727 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts @@ -7,7 +7,7 @@ import { filter, map, mapValues } from 'lodash'; import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { createInternalSavedObjectsClientForSpaceId } from '../../utils/get_internal_saved_object_client'; import type { ReadPacksRequestParamsSchema } from '../../../common/api'; @@ -47,51 +47,61 @@ export const readPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext }, }, async (context, request, response) => { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + try { + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const { attributes, references, id, ...rest } = - await spaceScopedClient.get(packSavedObjectType, request.params.id); + const { attributes, references, id, ...rest } = + await spaceScopedClient.get(packSavedObjectType, request.params.id); - const policyIds = map( - filter(references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' - ); - const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); + const policyIds = map( + filter(references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); + const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); - const data: ReadPackResponseData = { - type: rest.type, - namespaces: rest.namespaces, - migrationVersion: rest.migrationVersion, - managed: rest.managed, - coreMigrationVersion: rest.coreMigrationVersion, - name: attributes.name, - description: attributes.description, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - created_by_profile_uid: attributes.created_by_profile_uid, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - saved_object_id: id, - queries: mapValues( - convertSOQueriesToPack(attributes.queries), - ({ schedule_id: _s, start_date: _d, ...restQuery }) => restQuery - ), - shards: convertShardsToObject(attributes.shards), - policy_ids: policyIds, - read_only: attributes.version !== undefined && osqueryPackAssetReference, - }; + const data: ReadPackResponseData = { + type: rest.type, + namespaces: rest.namespaces, + migrationVersion: rest.migrationVersion, + managed: rest.managed, + coreMigrationVersion: rest.coreMigrationVersion, + name: attributes.name, + description: attributes.description, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + created_by_profile_uid: attributes.created_by_profile_uid, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + saved_object_id: id, + queries: mapValues( + convertSOQueriesToPack(attributes.queries), + ({ schedule_id: _s, start_date: _d, ...restQuery }) => restQuery + ), + shards: convertShardsToObject(attributes.shards), + policy_ids: policyIds, + read_only: attributes.version !== undefined && osqueryPackAssetReference, + }; - return response.ok({ - body: { - data, - }, - }); + return response.ok({ + body: { + data, + }, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Pack ${request.params.id} not found` }, + }); + } + + throw err; + } } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts index e50e7ee942862..05ad635ba85cb 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts @@ -27,7 +27,7 @@ import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, } from '@kbn/fleet-plugin/common'; -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-utils'; import { createInternalSavedObjectsClientForSpaceId } from '../../utils/get_internal_saved_object_client'; @@ -89,168 +89,259 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte }, }, async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); - - const spaceId = osqueryContext?.service?.getActiveSpace - ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID - : DEFAULT_SPACE_ID; - - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - - const [, startPlugins] = await osqueryContext.getStartServices(); - const currentUser = await getUserInfo({ - request, - security: (startPlugins as StartPlugins).security, - logger: osqueryContext.logFactory.get('pack'), - }); - const username = currentUser?.username ?? undefined; - const profileUid = currentUser?.profile_uid ?? undefined; - - const { - name, - description, - queries: rawQueries, - enabled, - policy_ids, - shards = {}, - } = request.body; - - const currentPackSO = await spaceScopedClient.get( - packSavedObjectType, - request.params.id - ); - - const existingScheduleIds = keyBy( - (currentPackSO.attributes.queries ?? []).filter( - (q: { id: string; schedule_id?: string }) => q.schedule_id - ), - 'id' - ); - const now = moment().toISOString(); - const queries = rawQueries - ? (mapValues(rawQueries, (queryData, queryId) => { - const existing = existingScheduleIds[queryId]; - - return { - ...queryData, - schedule_id: existing?.schedule_id ?? uuidv4(), - start_date: existing?.start_date ?? now, - }; - }) as Record) - : undefined; - - if (name) { - const conflictingEntries = await spaceScopedClient.find({ - type: packSavedObjectType, - filter: `${packSavedObjectType}.attributes.name: "${escapeFilterValue(name)}"`, - }); + try { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; - if ( - some( - filter(conflictingEntries.saved_objects, (packSO) => packSO.id !== currentPackSO.id), - ['attributes.name', name] - ) - ) { - return response.conflict({ body: `Pack with name "${name}" already exists.` }); - } - } + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - })) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) - ); - - const { policiesList, invalidPolicies } = getInitialPolicies( - packagePolicies, - policy_ids, - shards - ); - - if (invalidPolicies?.length) { - return response.badRequest({ - body: `The following policy ids are invalid: ${invalidPolicies.join(', ')}`, + const spaceId = osqueryContext?.service?.getActiveSpace + ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID + : DEFAULT_SPACE_ID; + + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + + const [, startPlugins] = await osqueryContext.getStartServices(); + const currentUser = await getUserInfo({ + request, + security: (startPlugins as StartPlugins).security, + logger: osqueryContext.logFactory.get('pack'), }); - } + const username = currentUser?.username ?? undefined; + const profileUid = currentUser?.profile_uid ?? undefined; - const agentPolicies = await agentPolicyService?.getByIds(spaceScopedClient, policiesList); + const { + name, + description, + queries: rawQueries, + enabled, + policy_ids, + shards = {}, + } = request.body; + + const currentPackSO = await spaceScopedClient.get( + packSavedObjectType, + request.params.id + ); - const policyShards = findMatchingShards(agentPolicies, shards); + const existingScheduleIds = keyBy( + (currentPackSO.attributes.queries ?? []).filter( + (q: { id: string; schedule_id?: string }) => q.schedule_id + ), + 'id' + ); + const now = moment().toISOString(); + const queries = rawQueries + ? (mapValues(rawQueries, (queryData, queryId) => { + const existing = existingScheduleIds[queryId]; + + return { + ...queryData, + schedule_id: existing?.schedule_id ?? uuidv4(), + start_date: existing?.start_date ?? now, + }; + }) as Record) + : undefined; + + if (name) { + const conflictingEntries = await spaceScopedClient.find({ + type: packSavedObjectType, + filter: `${packSavedObjectType}.attributes.name: "${escapeFilterValue(name)}"`, + }); + + if ( + some( + filter( + conflictingEntries.saved_objects, + (packSO) => packSO.id !== currentPackSO.id + ), + ['attributes.name', name] + ) + ) { + return response.conflict({ body: `Pack with name "${name}" already exists.` }); + } + } + + const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + })) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) + ); - const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); + const { policiesList, invalidPolicies } = getInitialPolicies( + packagePolicies, + policy_ids, + shards + ); - const nonAgentPolicyReferences = filter( - currentPackSO.references, - (reference) => reference.type !== LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE - ); - const getUpdatedReferences = () => { - if (!policy_ids && isEmpty(shards)) { - return currentPackSO.references; + if (invalidPolicies?.length) { + return response.badRequest({ + body: `The following policy ids are invalid: ${invalidPolicies.join(', ')}`, + }); } - return [ - ...nonAgentPolicyReferences, - ...policiesList.map((id) => ({ - id, - name: agentPoliciesIdMap[id]?.name, - type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, - })), - ]; - }; - - const references = getUpdatedReferences(); - - await spaceScopedClient.update( - packSavedObjectType, - request.params.id, - { - enabled, - name, - description: description || '', - queries: queries && convertPackQueriesToSO(queries), - updated_at: moment().toISOString(), - updated_by: username, - updated_by_profile_uid: profileUid, - shards: convertShardsToArray(shards), - }, - { - refresh: 'wait_for', - references, + const agentPolicies = await agentPolicyService?.getByIds(spaceScopedClient, policiesList); + + const policyShards = findMatchingShards(agentPolicies, shards); + + const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); + + const nonAgentPolicyReferences = filter( + currentPackSO.references, + (reference) => reference.type !== LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE + ); + const getUpdatedReferences = () => { + if (!policy_ids && isEmpty(shards)) { + return currentPackSO.references; + } + + return [ + ...nonAgentPolicyReferences, + ...policiesList.map((id) => ({ + id, + name: agentPoliciesIdMap[id]?.name, + type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, + })), + ]; + }; + + const references = getUpdatedReferences(); + + await spaceScopedClient.update( + packSavedObjectType, + request.params.id, + { + enabled, + name, + description: description || '', + queries: queries && convertPackQueriesToSO(queries), + updated_at: moment().toISOString(), + updated_by: username, + updated_by_profile_uid: profileUid, + shards: convertShardsToArray(shards), + }, + { + refresh: 'wait_for', + references, + } + ); + + const currentAgentPolicyIds = map( + filter(currentPackSO.references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); + const updatedPackSO = await spaceScopedClient.get( + packSavedObjectType, + request.params.id + ); + + // @ts-expect-error update types + updatedPackSO.attributes.queries = convertSOQueriesToPack( + updatedPackSO.attributes.queries + ); + + if (enabled == null && !currentPackSO.attributes.enabled) { + return response.ok({ body: { data: updatedPackSO } }); } - ); - - const currentAgentPolicyIds = map( - filter(currentPackSO.references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' - ); - const updatedPackSO = await spaceScopedClient.get( - packSavedObjectType, - request.params.id - ); - - // @ts-expect-error update types - updatedPackSO.attributes.queries = convertSOQueriesToPack(updatedPackSO.attributes.queries); - - if (enabled == null && !currentPackSO.attributes.enabled) { - return response.ok({ body: { data: updatedPackSO } }); - } - if (enabled != null && enabled !== currentPackSO.attributes.enabled) { - if (enabled) { - const policyIds = policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; + if (enabled != null && enabled !== currentPackSO.attributes.enabled) { + if (enabled) { + const policyIds = + policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; + + await Promise.all( + policyIds.map((agentPolicyId) => { + const packagePolicy = packagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!has(draft, 'inputs[0].streams')) { + set(draft, 'inputs[0].streams', []); + } + + const pk = makePackKey(updatedPackSO.attributes.name, spaceId); + removePackFromPolicy(draft, updatedPackSO.attributes.name, spaceId); + set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { + shard: policyShards[agentPolicyId] ?? 100, + pack_id: updatedPackSO.id, + queries: convertSOQueriesToPackConfig( + updatedPackSO.attributes.queries, + spaceId + ), + }); + + return draft; + }) + ); + } + }) + ); + } else { + await Promise.all( + currentAgentPolicyIds.map((agentPolicyId) => { + const packagePolicy = currentPackagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + if (!packagePolicy) return; + + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + + return draft; + }) + ); + }) + ); + } + } else { + // TODO double check if policiesList shouldnt be changed into policyIds + const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); + const agentPolicyIdsToUpdate = uniq( + difference(currentAgentPolicyIds, agentPolicyIdsToRemove) + ); + const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); await Promise.all( - policyIds.map((agentPolicyId) => { + agentPolicyIdsToRemove.map((agentPolicyId) => { + const packagePolicy = currentPackagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + + return draft; + }) + ); + } + }) + ); + + await Promise.all( + agentPolicyIdsToUpdate.map((agentPolicyId) => { const packagePolicy = packagePolicies.find((policy) => policy.policy_ids.includes(agentPolicyId) ); @@ -261,8 +352,8 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte packagePolicy.id, produce(packagePolicy, (draft) => { unset(draft, 'id'); - if (!has(draft, 'inputs[0].streams')) { - set(draft, 'inputs[0].streams', []); + if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); } const pk = makePackKey(updatedPackSO.attributes.name, spaceId); @@ -282,148 +373,73 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte } }) ); - } else { + await Promise.all( - currentAgentPolicyIds.map((agentPolicyId) => { - const packagePolicy = currentPackagePolicies.find((policy) => + agentPolicyIdsToAdd.map((agentPolicyId) => { + const packagePolicy = packagePolicies.find((policy) => policy.policy_ids.includes(agentPolicyId) ); - if (!packagePolicy) return; - - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - - return draft; - }) - ); + + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!(draft.inputs.length && draft.inputs[0].streams.length)) { + set(draft, 'inputs[0].streams', []); + } + + const pk = makePackKey(updatedPackSO.attributes.name, spaceId); + set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { + shard: policyShards[agentPolicyId] ?? 100, + pack_id: updatedPackSO.id, + queries: convertSOQueriesToPackConfig( + updatedPackSO.attributes.queries, + spaceId + ), + }); + + return draft; + }) + ); + } }) ); } - } else { - // TODO double check if policiesList shouldnt be changed into policyIds - const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); - const agentPolicyIdsToUpdate = uniq( - difference(currentAgentPolicyIds, agentPolicyIdsToRemove) - ); - const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); - await Promise.all( - agentPolicyIdsToRemove.map((agentPolicyId) => { - const packagePolicy = currentPackagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - - return draft; - }) - ); - } - }) - ); - - await Promise.all( - agentPolicyIdsToUpdate.map((agentPolicyId) => { - const packagePolicy = packagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - } - - const pk = makePackKey(updatedPackSO.attributes.name, spaceId); - removePackFromPolicy(draft, updatedPackSO.attributes.name, spaceId); - set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { - shard: policyShards[agentPolicyId] ?? 100, - pack_id: updatedPackSO.id, - queries: convertSOQueriesToPackConfig( - updatedPackSO.attributes.queries, - spaceId - ), - }); - - return draft; - }) - ); - } - }) - ); - - await Promise.all( - agentPolicyIdsToAdd.map((agentPolicyId) => { - const packagePolicy = packagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); + const { attributes } = updatedPackSO; + + const data: PackResponseData = { + name: attributes.name, + description: attributes.description, + queries: attributes.queries, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + created_by_profile_uid: attributes.created_by_profile_uid, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + policy_ids: attributes.policy_ids, + shards: attributes.shards, + saved_object_id: updatedPackSO.id, + }; + + return response.ok({ + body: { data }, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Pack ${request.params.id} not found` }, + }); + } - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (!(draft.inputs.length && draft.inputs[0].streams.length)) { - set(draft, 'inputs[0].streams', []); - } - - const pk = makePackKey(updatedPackSO.attributes.name, spaceId); - set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { - shard: policyShards[agentPolicyId] ?? 100, - pack_id: updatedPackSO.id, - queries: convertSOQueriesToPackConfig( - updatedPackSO.attributes.queries, - spaceId - ), - }); - - return draft; - }) - ); - } - }) - ); + throw err; } - - const { attributes } = updatedPackSO; - - const data: PackResponseData = { - name: attributes.name, - description: attributes.description, - queries: attributes.queries, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - created_by_profile_uid: attributes.created_by_profile_uid, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - policy_ids: attributes.policy_ids, - shards: attributes.shards, - saved_object_id: updatedPackSO.id, - }; - - return response.ok({ - body: { data }, - }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts index 5965eacb63f77..4ea07a51dc65e 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-utils'; import { createInternalSavedObjectsClientForSpaceId } from '../../utils/get_internal_saved_object_client'; import { buildRouteValidation } from '../../utils/build_validation/route_validation'; @@ -41,31 +41,41 @@ export const deleteSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp }, }, async (context, request, response) => { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + try { + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id, - spaceScopedClient, - spaceId - ); - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); - } + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id, + spaceScopedClient, + spaceId + ); + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); + } + + await spaceScopedClient.delete(savedQuerySavedObjectType, request.params.id, { + refresh: 'wait_for', + }); - await spaceScopedClient.delete(savedQuerySavedObjectType, request.params.id, { - refresh: 'wait_for', - }); + return response.ok({ + body: {}, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Saved query ${request.params.id} not found` }, + }); + } - return response.ok({ - body: {}, - }); + throw err; + } } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts index ec20ada4407ae..2f4685f3d2e3b 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-utils'; import { createInternalSavedObjectsClientForSpaceId } from '../../utils/get_internal_saved_object_client'; import { buildRouteValidation } from '../../utils/build_validation/route_validation'; @@ -44,91 +44,101 @@ export const readSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppC }, }, async (context, request, response) => { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + try { + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); + + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; + const savedQuery = await spaceScopedClient.get( + savedQuerySavedObjectType, + request.params.id + ); - const savedQuery = await spaceScopedClient.get( - savedQuerySavedObjectType, - request.params.id - ); + if (savedQuery.attributes.ecs_mapping) { + // @ts-expect-error update types + savedQuery.attributes.ecs_mapping = convertECSMappingToObject( + savedQuery.attributes.ecs_mapping + ); + } - if (savedQuery.attributes.ecs_mapping) { - // @ts-expect-error update types - savedQuery.attributes.ecs_mapping = convertECSMappingToObject( - savedQuery.attributes.ecs_mapping + const prebuiltById = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + savedQuery.id, + spaceScopedClient, + spaceId ); - } - const prebuiltById = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - savedQuery.id, - spaceScopedClient, - spaceId - ); + const prebuiltByOriginId = + !prebuiltById && savedQuery.originId + ? await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + savedQuery.originId, + spaceScopedClient, + spaceId + ) + : false; - const prebuiltByOriginId = - !prebuiltById && savedQuery.originId - ? await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - savedQuery.originId, - spaceScopedClient, - spaceId - ) - : false; + savedQuery.attributes.prebuilt = prebuiltById || prebuiltByOriginId; - savedQuery.attributes.prebuilt = prebuiltById || prebuiltByOriginId; + const { + created_at: createdAt, + created_by: createdBy, + created_by_profile_uid: createdByProfileUid, + description, + id, + interval, + timeout, + platform, + query, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + updated_at: updatedAt, + updated_by: updatedBy, + updated_by_profile_uid: updatedByProfileUid, + prebuilt, + } = savedQuery.attributes; - const { - created_at: createdAt, - created_by: createdBy, - created_by_profile_uid: createdByProfileUid, - description, - id, - interval, - timeout, - platform, - query, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - updated_at: updatedAt, - updated_by: updatedBy, - updated_by_profile_uid: updatedByProfileUid, - prebuilt, - } = savedQuery.attributes; + const data: SavedQueryResponse = { + created_at: createdAt, + created_by: createdBy, + created_by_profile_uid: createdByProfileUid, + description, + id, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + interval, + timeout, + platform, + query, + updated_at: updatedAt, + updated_by: updatedBy, + updated_by_profile_uid: updatedByProfileUid, + prebuilt, + saved_object_id: savedQuery.id, + }; - const data: SavedQueryResponse = { - created_at: createdAt, - created_by: createdBy, - created_by_profile_uid: createdByProfileUid, - description, - id, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - interval, - timeout, - platform, - query, - updated_at: updatedAt, - updated_by: updatedBy, - updated_by_profile_uid: updatedByProfileUid, - prebuilt, - saved_object_id: savedQuery.id, - }; + return response.ok({ + body: { + data, + }, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Saved query ${request.params.id} not found` }, + }); + } - return response.ok({ - body: { - data, - }, - }); + throw err; + } } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts index c24fe02476980..1b0e4913aa68d 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -7,7 +7,7 @@ import { filter, some } from 'lodash'; -import type { IRouter } from '@kbn/core/server'; +import { type IRouter, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-utils'; import { createInternalSavedObjectsClientForSpaceId } from '../../utils/get_internal_saved_object_client'; import { buildRouteValidation } from '../../utils/build_validation/route_validation'; @@ -57,70 +57,27 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp }, }, async (context, request, response) => { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); - - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; - - const [, startPlugins] = await osqueryContext.getStartServices(); - const currentUser = await getUserInfo({ - request, - security: (startPlugins as StartPlugins).security, - logger: osqueryContext.logFactory.get('savedQuery'), - }); - const username = currentUser?.username ?? undefined; - const profileUid = currentUser?.profile_uid ?? undefined; - - const { - id, - description, - platform, - query, - version, - interval, - timeout, - snapshot, - removed, - ecs_mapping, - } = request.body; - - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id, - spaceScopedClient, - spaceId - ); - - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); - } + try { + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const conflictingEntries = await spaceScopedClient.find<{ id: string }>({ - type: savedQuerySavedObjectType, - filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, - }); - - if ( - some( - filter( - conflictingEntries.saved_objects, - (soObject) => soObject.id !== request.params.id - ), - ['attributes.id', id] - ) - ) { - return response.conflict({ body: `Saved query with id "${id}" already exists.` }); - } + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; + + const [, startPlugins] = await osqueryContext.getStartServices(); + const currentUser = await getUserInfo({ + request, + security: (startPlugins as StartPlugins).security, + logger: osqueryContext.logFactory.get('savedQuery'), + }); + const username = currentUser?.username ?? undefined; + const profileUid = currentUser?.profile_uid ?? undefined; - const updatedSavedQuerySO = await spaceScopedClient.update( - savedQuerySavedObjectType, - request.params.id, - { + const { id, - description: description || '', + description, platform, query, version, @@ -128,50 +85,103 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp timeout, snapshot, removed, - ecs_mapping: convertECSMappingToArray(ecs_mapping), - updated_by: username, - updated_by_profile_uid: profileUid, - updated_at: new Date().toISOString(), - }, - { - refresh: 'wait_for', + ecs_mapping, + } = request.body; + + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id, + spaceScopedClient, + spaceId + ); + + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); } - ); - - if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { - // @ts-expect-error update types - updatedSavedQuerySO.attributes.ecs_mapping = - ecs_mapping || - (updatedSavedQuerySO.attributes.ecs_mapping && - // @ts-expect-error update types - convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || - {}; - } - const { attributes } = updatedSavedQuerySO; - - const data: Partial = { - description: attributes.description, - id: attributes.id, - removed: attributes.removed, - snapshot: attributes.snapshot, - version: attributes.version, - ecs_mapping: attributes.ecs_mapping, - interval: attributes.interval, - timeout: attributes.timeout, - platform: attributes.platform, - query: attributes.query, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - saved_object_id: updatedSavedQuerySO.id, - }; - - return response.ok({ - body: { - data, - }, - }); + const conflictingEntries = await spaceScopedClient.find<{ id: string }>({ + type: savedQuerySavedObjectType, + filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, + }); + + if ( + some( + filter( + conflictingEntries.saved_objects, + (soObject) => soObject.id !== request.params.id + ), + ['attributes.id', id] + ) + ) { + return response.conflict({ body: `Saved query with id "${id}" already exists.` }); + } + + const updatedSavedQuerySO = await spaceScopedClient.update( + savedQuerySavedObjectType, + request.params.id, + { + id, + description: description || '', + platform, + query, + version, + interval, + timeout, + snapshot, + removed, + ecs_mapping: convertECSMappingToArray(ecs_mapping), + updated_by: username, + updated_by_profile_uid: profileUid, + updated_at: new Date().toISOString(), + }, + { + refresh: 'wait_for', + } + ); + + if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { + // @ts-expect-error update types + updatedSavedQuerySO.attributes.ecs_mapping = + ecs_mapping || + (updatedSavedQuerySO.attributes.ecs_mapping && + // @ts-expect-error update types + convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || + {}; + } + + const { attributes } = updatedSavedQuerySO; + + const data: Partial = { + description: attributes.description, + id: attributes.id, + removed: attributes.removed, + snapshot: attributes.snapshot, + version: attributes.version, + ecs_mapping: attributes.ecs_mapping, + interval: attributes.interval, + timeout: attributes.timeout, + platform: attributes.platform, + query: attributes.query, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + saved_object_id: updatedSavedQuerySO.id, + }; + + return response.ok({ + body: { + data, + }, + }); + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Saved query ${request.params.id} not found` }, + }); + } + + throw err; + } } ); }; From 814b10f7e552382ea2eaed8fe28ba3a2519c969d Mon Sep 17 00:00:00 2001 From: Cesare de Cal Date: Fri, 20 Mar 2026 17:17:33 +0100 Subject: [PATCH 2/3] add FTR tests --- .../api_integration/apis/osquery/packs.ts | 23 +++++++++++++++++++ .../apis/osquery/saved_queries.ts | 14 +++++++++++ 2 files changed, 37 insertions(+) diff --git a/x-pack/platform/test/api_integration/apis/osquery/packs.ts b/x-pack/platform/test/api_integration/apis/osquery/packs.ts index cb321eaa56809..ec6265bb0089b 100644 --- a/x-pack/platform/test/api_integration/apis/osquery/packs.ts +++ b/x-pack/platform/test/api_integration/apis/osquery/packs.ts @@ -280,5 +280,28 @@ export default function ({ getService }: FtrProviderContext) { inputs[0].config.osquery.value.packs['default--TestPack'].queries.testQuery.query ).to.be(singleLineQuery); }); + + describe('404 for non-existent resources', () => { + it('returns 404 when reading a non-existent pack', async () => { + await withOsqueryHeaders(supertest.get('/api/osquery/packs/non-existent-id')).expect(404); + }); + + it('returns 404 when updating a non-existent pack', async () => { + await withOsqueryHeaders(supertest.put('/api/osquery/packs/non-existent-id')) + .send({ + name: 'Updated Pack', + description: 'Updated', + enabled: true, + queries: { q1: { query: 'select 1;', interval: 3600 } }, + }) + .expect(404); + }); + + it('returns 404 when deleting a non-existent pack', async () => { + await withOsqueryHeaders(supertest.delete('/api/osquery/packs/non-existent-id')).expect( + 404 + ); + }); + }); }); } diff --git a/x-pack/platform/test/api_integration/apis/osquery/saved_queries.ts b/x-pack/platform/test/api_integration/apis/osquery/saved_queries.ts index 553cdbee36449..fe7cbc3f0c5e2 100644 --- a/x-pack/platform/test/api_integration/apis/osquery/saved_queries.ts +++ b/x-pack/platform/test/api_integration/apis/osquery/saved_queries.ts @@ -175,5 +175,19 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.total).to.be(0); }); }); + + describe('404 for non-existent resources', () => { + it('returns 404 when reading a non-existent saved query', async () => { + await getSavedQuery('non-existent-id').expect(404); + }); + + it('returns 404 when updating a non-existent saved query', async () => { + await updateSavedQuery('non-existent-id', 'updated-name').expect(404); + }); + + it('returns 404 when deleting a non-existent saved query', async () => { + await deleteSavedQuery('non-existent-id').expect(404); + }); + }); }); } From 2b5459ce00436cd0a07ef6d4e29efe901f836342 Mon Sep 17 00:00:00 2001 From: Cesare de Cal Date: Mon, 23 Mar 2026 15:14:39 +0100 Subject: [PATCH 3/3] address feedback --- .../server/routes/pack/delete_pack_route.ts | 91 +-- .../server/routes/pack/read_pack_route.ts | 93 +-- .../server/routes/pack/update_pack_route.ts | 581 +++++++++--------- .../saved_query/delete_saved_query_route.ts | 40 +- .../saved_query/read_saved_query_route.ts | 163 ++--- .../saved_query/update_saved_query_route.ts | 187 +++--- 6 files changed, 579 insertions(+), 576 deletions(-) diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts index f9e2abddc9cbe..21d1e419543e1 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/delete_pack_route.ts @@ -47,57 +47,25 @@ export const deletePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte }, }, async (context, request, response) => { - try { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const spaceId = osqueryContext?.service?.getActiveSpace - ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID - : DEFAULT_SPACE_ID; + const spaceId = osqueryContext?.service?.getActiveSpace + ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID + : DEFAULT_SPACE_ID; - const currentPackSO = await spaceScopedClient.get<{ name: string }>( + let currentPackSO; + try { + currentPackSO = await spaceScopedClient.get<{ name: string }>( packSavedObjectType, request.params.id ); - - await spaceScopedClient.delete(packSavedObjectType, request.params.id, { - refresh: 'wait_for', - }); - - const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - })) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) - ); - - await Promise.all( - currentPackagePolicies.map((packagePolicy) => - packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - - return draft; - }) - ) - ) - ); - - return response.ok({ - body: {}, - }); } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return response.notFound({ @@ -107,6 +75,39 @@ export const deletePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte throw err; } + + await spaceScopedClient.delete(packSavedObjectType, request.params.id, { + refresh: 'wait_for', + }); + + const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + })) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) + ); + + await Promise.all( + currentPackagePolicies.map((packagePolicy) => + packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + + return draft; + }) + ) + ) + ); + + return response.ok({ + body: {}, + }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts index ac1558c41c727..3486e42c2bdd9 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/read_pack_route.ts @@ -47,52 +47,17 @@ export const readPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext }, }, async (context, request, response) => { - try { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); - - const { attributes, references, id, ...rest } = - await spaceScopedClient.get(packSavedObjectType, request.params.id); + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const policyIds = map( - filter(references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' + let packSO; + try { + packSO = await spaceScopedClient.get( + packSavedObjectType, + request.params.id ); - const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); - - const data: ReadPackResponseData = { - type: rest.type, - namespaces: rest.namespaces, - migrationVersion: rest.migrationVersion, - managed: rest.managed, - coreMigrationVersion: rest.coreMigrationVersion, - name: attributes.name, - description: attributes.description, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - created_by_profile_uid: attributes.created_by_profile_uid, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - saved_object_id: id, - queries: mapValues( - convertSOQueriesToPack(attributes.queries), - ({ schedule_id: _s, start_date: _d, ...restQuery }) => restQuery - ), - shards: convertShardsToObject(attributes.shards), - policy_ids: policyIds, - read_only: attributes.version !== undefined && osqueryPackAssetReference, - }; - - return response.ok({ - body: { - data, - }, - }); } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return response.notFound({ @@ -102,6 +67,46 @@ export const readPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext throw err; } + + const { attributes, references, id, ...rest } = packSO; + + const policyIds = map( + filter(references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); + const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); + + const data: ReadPackResponseData = { + type: rest.type, + namespaces: rest.namespaces, + migrationVersion: rest.migrationVersion, + managed: rest.managed, + coreMigrationVersion: rest.coreMigrationVersion, + name: attributes.name, + description: attributes.description, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + created_by_profile_uid: attributes.created_by_profile_uid, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + saved_object_id: id, + queries: mapValues( + convertSOQueriesToPack(attributes.queries), + ({ schedule_id: _s, start_date: _d, ...restQuery }) => restQuery + ), + shards: convertShardsToObject(attributes.shards), + policy_ids: policyIds, + read_only: attributes.version !== undefined && osqueryPackAssetReference, + }; + + return response.ok({ + body: { + data, + }, + }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts index 05ad635ba85cb..0f5800674d47c 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/pack/update_pack_route.ts @@ -89,259 +89,179 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte }, }, async (context, request, response) => { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); + + const spaceId = osqueryContext?.service?.getActiveSpace + ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID + : DEFAULT_SPACE_ID; + + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + + const [, startPlugins] = await osqueryContext.getStartServices(); + const currentUser = await getUserInfo({ + request, + security: (startPlugins as StartPlugins).security, + logger: osqueryContext.logFactory.get('pack'), + }); + const username = currentUser?.username ?? undefined; + const profileUid = currentUser?.profile_uid ?? undefined; + + const { + name, + description, + queries: rawQueries, + enabled, + policy_ids, + shards = {}, + } = request.body; + + let currentPackSO; try { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); - - const spaceId = osqueryContext?.service?.getActiveSpace - ? (await osqueryContext.service.getActiveSpace(request))?.id || DEFAULT_SPACE_ID - : DEFAULT_SPACE_ID; - - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - - const [, startPlugins] = await osqueryContext.getStartServices(); - const currentUser = await getUserInfo({ - request, - security: (startPlugins as StartPlugins).security, - logger: osqueryContext.logFactory.get('pack'), - }); - const username = currentUser?.username ?? undefined; - const profileUid = currentUser?.profile_uid ?? undefined; - - const { - name, - description, - queries: rawQueries, - enabled, - policy_ids, - shards = {}, - } = request.body; - - const currentPackSO = await spaceScopedClient.get( + currentPackSO = await spaceScopedClient.get( packSavedObjectType, request.params.id ); - - const existingScheduleIds = keyBy( - (currentPackSO.attributes.queries ?? []).filter( - (q: { id: string; schedule_id?: string }) => q.schedule_id - ), - 'id' - ); - const now = moment().toISOString(); - const queries = rawQueries - ? (mapValues(rawQueries, (queryData, queryId) => { - const existing = existingScheduleIds[queryId]; - - return { - ...queryData, - schedule_id: existing?.schedule_id ?? uuidv4(), - start_date: existing?.start_date ?? now, - }; - }) as Record) - : undefined; - - if (name) { - const conflictingEntries = await spaceScopedClient.find({ - type: packSavedObjectType, - filter: `${packSavedObjectType}.attributes.name: "${escapeFilterValue(name)}"`, + } catch (err) { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + return response.notFound({ + body: { message: `Pack ${request.params.id} not found` }, }); - - if ( - some( - filter( - conflictingEntries.saved_objects, - (packSO) => packSO.id !== currentPackSO.id - ), - ['attributes.name', name] - ) - ) { - return response.conflict({ body: `Pack with name "${name}" already exists.` }); - } } - const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - })) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) - ); + throw err; + } - const { policiesList, invalidPolicies } = getInitialPolicies( - packagePolicies, - policy_ids, - shards - ); + const existingScheduleIds = keyBy( + (currentPackSO.attributes.queries ?? []).filter( + (q: { id: string; schedule_id?: string }) => q.schedule_id + ), + 'id' + ); + const now = moment().toISOString(); + const queries = rawQueries + ? (mapValues(rawQueries, (queryData, queryId) => { + const existing = existingScheduleIds[queryId]; + + return { + ...queryData, + schedule_id: existing?.schedule_id ?? uuidv4(), + start_date: existing?.start_date ?? now, + }; + }) as Record) + : undefined; + + if (name) { + const conflictingEntries = await spaceScopedClient.find({ + type: packSavedObjectType, + filter: `${packSavedObjectType}.attributes.name: "${escapeFilterValue(name)}"`, + }); - if (invalidPolicies?.length) { - return response.badRequest({ - body: `The following policy ids are invalid: ${invalidPolicies.join(', ')}`, - }); + if ( + some( + filter(conflictingEntries.saved_objects, (packSO) => packSO.id !== currentPackSO.id), + ['attributes.name', name] + ) + ) { + return response.conflict({ body: `Pack with name "${name}" already exists.` }); } + } - const agentPolicies = await agentPolicyService?.getByIds(spaceScopedClient, policiesList); - - const policyShards = findMatchingShards(agentPolicies, shards); - - const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); + const { items: packagePolicies } = (await packagePolicyService?.list(spaceScopedClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + })) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + policyHasPack(packagePolicy, currentPackSO.attributes.name, spaceId) + ); + + const { policiesList, invalidPolicies } = getInitialPolicies( + packagePolicies, + policy_ids, + shards + ); + + if (invalidPolicies?.length) { + return response.badRequest({ + body: `The following policy ids are invalid: ${invalidPolicies.join(', ')}`, + }); + } - const nonAgentPolicyReferences = filter( - currentPackSO.references, - (reference) => reference.type !== LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE - ); - const getUpdatedReferences = () => { - if (!policy_ids && isEmpty(shards)) { - return currentPackSO.references; - } - - return [ - ...nonAgentPolicyReferences, - ...policiesList.map((id) => ({ - id, - name: agentPoliciesIdMap[id]?.name, - type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, - })), - ]; - }; - - const references = getUpdatedReferences(); - - await spaceScopedClient.update( - packSavedObjectType, - request.params.id, - { - enabled, - name, - description: description || '', - queries: queries && convertPackQueriesToSO(queries), - updated_at: moment().toISOString(), - updated_by: username, - updated_by_profile_uid: profileUid, - shards: convertShardsToArray(shards), - }, - { - refresh: 'wait_for', - references, - } - ); + const agentPolicies = await agentPolicyService?.getByIds(spaceScopedClient, policiesList); - const currentAgentPolicyIds = map( - filter(currentPackSO.references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' - ); - const updatedPackSO = await spaceScopedClient.get( - packSavedObjectType, - request.params.id - ); + const policyShards = findMatchingShards(agentPolicies, shards); - // @ts-expect-error update types - updatedPackSO.attributes.queries = convertSOQueriesToPack( - updatedPackSO.attributes.queries - ); + const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); - if (enabled == null && !currentPackSO.attributes.enabled) { - return response.ok({ body: { data: updatedPackSO } }); + const nonAgentPolicyReferences = filter( + currentPackSO.references, + (reference) => reference.type !== LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE + ); + const getUpdatedReferences = () => { + if (!policy_ids && isEmpty(shards)) { + return currentPackSO.references; } - if (enabled != null && enabled !== currentPackSO.attributes.enabled) { - if (enabled) { - const policyIds = - policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; - - await Promise.all( - policyIds.map((agentPolicyId) => { - const packagePolicy = packagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (!has(draft, 'inputs[0].streams')) { - set(draft, 'inputs[0].streams', []); - } - - const pk = makePackKey(updatedPackSO.attributes.name, spaceId); - removePackFromPolicy(draft, updatedPackSO.attributes.name, spaceId); - set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { - shard: policyShards[agentPolicyId] ?? 100, - pack_id: updatedPackSO.id, - queries: convertSOQueriesToPackConfig( - updatedPackSO.attributes.queries, - spaceId - ), - }); - - return draft; - }) - ); - } - }) - ); - } else { - await Promise.all( - currentAgentPolicyIds.map((agentPolicyId) => { - const packagePolicy = currentPackagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); - if (!packagePolicy) return; - - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); - - return draft; - }) - ); - }) - ); - } - } else { - // TODO double check if policiesList shouldnt be changed into policyIds - const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); - const agentPolicyIdsToUpdate = uniq( - difference(currentAgentPolicyIds, agentPolicyIdsToRemove) - ); - const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); - - await Promise.all( - agentPolicyIdsToRemove.map((agentPolicyId) => { - const packagePolicy = currentPackagePolicies.find((policy) => - policy.policy_ids.includes(agentPolicyId) - ); - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + return [ + ...nonAgentPolicyReferences, + ...policiesList.map((id) => ({ + id, + name: agentPoliciesIdMap[id]?.name, + type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, + })), + ]; + }; + + const references = getUpdatedReferences(); + + await spaceScopedClient.update( + packSavedObjectType, + request.params.id, + { + enabled, + name, + description: description || '', + queries: queries && convertPackQueriesToSO(queries), + updated_at: moment().toISOString(), + updated_by: username, + updated_by_profile_uid: profileUid, + shards: convertShardsToArray(shards), + }, + { + refresh: 'wait_for', + references, + } + ); + + const currentAgentPolicyIds = map( + filter(currentPackSO.references, ['type', LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); + const updatedPackSO = await spaceScopedClient.get( + packSavedObjectType, + request.params.id + ); + + // @ts-expect-error update types + updatedPackSO.attributes.queries = convertSOQueriesToPack(updatedPackSO.attributes.queries); + + if (enabled == null && !currentPackSO.attributes.enabled) { + return response.ok({ body: { data: updatedPackSO } }); + } - return draft; - }) - ); - } - }) - ); + if (enabled != null && enabled !== currentPackSO.attributes.enabled) { + if (enabled) { + const policyIds = policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; await Promise.all( - agentPolicyIdsToUpdate.map((agentPolicyId) => { + policyIds.map((agentPolicyId) => { const packagePolicy = packagePolicies.find((policy) => policy.policy_ids.includes(agentPolicyId) ); @@ -352,8 +272,8 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte packagePolicy.id, produce(packagePolicy, (draft) => { unset(draft, 'id'); - if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { - removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + if (!has(draft, 'inputs[0].streams')) { + set(draft, 'inputs[0].streams', []); } const pk = makePackKey(updatedPackSO.attributes.name, spaceId); @@ -373,73 +293,148 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte } }) ); - + } else { await Promise.all( - agentPolicyIdsToAdd.map((agentPolicyId) => { - const packagePolicy = packagePolicies.find((policy) => + currentAgentPolicyIds.map((agentPolicyId) => { + const packagePolicy = currentPackagePolicies.find((policy) => policy.policy_ids.includes(agentPolicyId) ); - - if (packagePolicy) { - return packagePolicyService?.update( - spaceScopedClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (!(draft.inputs.length && draft.inputs[0].streams.length)) { - set(draft, 'inputs[0].streams', []); - } - - const pk = makePackKey(updatedPackSO.attributes.name, spaceId); - set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { - shard: policyShards[agentPolicyId] ?? 100, - pack_id: updatedPackSO.id, - queries: convertSOQueriesToPackConfig( - updatedPackSO.attributes.queries, - spaceId - ), - }); - - return draft; - }) - ); - } + if (!packagePolicy) return; + + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + + return draft; + }) + ); }) ); } + } else { + // TODO double check if policiesList shouldnt be changed into policyIds + const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); + const agentPolicyIdsToUpdate = uniq( + difference(currentAgentPolicyIds, agentPolicyIdsToRemove) + ); + const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); - const { attributes } = updatedPackSO; - - const data: PackResponseData = { - name: attributes.name, - description: attributes.description, - queries: attributes.queries, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - created_by_profile_uid: attributes.created_by_profile_uid, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - policy_ids: attributes.policy_ids, - shards: attributes.shards, - saved_object_id: updatedPackSO.id, - }; - - return response.ok({ - body: { data }, - }); - } catch (err) { - if (SavedObjectsErrorHelpers.isNotFoundError(err)) { - return response.notFound({ - body: { message: `Pack ${request.params.id} not found` }, - }); - } + await Promise.all( + agentPolicyIdsToRemove.map((agentPolicyId) => { + const packagePolicy = currentPackagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + + return draft; + }) + ); + } + }) + ); - throw err; + await Promise.all( + agentPolicyIdsToUpdate.map((agentPolicyId) => { + const packagePolicy = packagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { + removePackFromPolicy(draft, currentPackSO.attributes.name, spaceId); + } + + const pk = makePackKey(updatedPackSO.attributes.name, spaceId); + removePackFromPolicy(draft, updatedPackSO.attributes.name, spaceId); + set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { + shard: policyShards[agentPolicyId] ?? 100, + pack_id: updatedPackSO.id, + queries: convertSOQueriesToPackConfig( + updatedPackSO.attributes.queries, + spaceId + ), + }); + + return draft; + }) + ); + } + }) + ); + + await Promise.all( + agentPolicyIdsToAdd.map((agentPolicyId) => { + const packagePolicy = packagePolicies.find((policy) => + policy.policy_ids.includes(agentPolicyId) + ); + + if (packagePolicy) { + return packagePolicyService?.update( + spaceScopedClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!(draft.inputs.length && draft.inputs[0].streams.length)) { + set(draft, 'inputs[0].streams', []); + } + + const pk = makePackKey(updatedPackSO.attributes.name, spaceId); + set(draft, `inputs[0].config.osquery.value.packs.${pk}`, { + shard: policyShards[agentPolicyId] ?? 100, + pack_id: updatedPackSO.id, + queries: convertSOQueriesToPackConfig( + updatedPackSO.attributes.queries, + spaceId + ), + }); + + return draft; + }) + ); + } + }) + ); } + + const { attributes } = updatedPackSO; + + const data: PackResponseData = { + name: attributes.name, + description: attributes.description, + queries: attributes.queries, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + created_by_profile_uid: attributes.created_by_profile_uid, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + policy_ids: attributes.policy_ids, + shards: attributes.shards, + saved_object_id: updatedPackSO.id, + }; + + return response.ok({ + body: { data }, + }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts index 4ea07a51dc65e..1ca31f1b3511e 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -41,32 +41,28 @@ export const deleteSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp }, }, async (context, request, response) => { - try { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id, - spaceScopedClient, - spaceId - ); - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); - } + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id, + spaceScopedClient, + spaceId + ); + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); + } + try { await spaceScopedClient.delete(savedQuerySavedObjectType, request.params.id, { refresh: 'wait_for', }); - - return response.ok({ - body: {}, - }); } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return response.notFound({ @@ -76,6 +72,10 @@ export const deleteSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp throw err; } + + return response.ok({ + body: {}, + }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts index 2f4685f3d2e3b..34b0b0592b101 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -44,92 +44,20 @@ export const readSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppC }, }, async (context, request, response) => { - try { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; - const savedQuery = await spaceScopedClient.get( + let savedQuery; + try { + savedQuery = await spaceScopedClient.get( savedQuerySavedObjectType, request.params.id ); - - if (savedQuery.attributes.ecs_mapping) { - // @ts-expect-error update types - savedQuery.attributes.ecs_mapping = convertECSMappingToObject( - savedQuery.attributes.ecs_mapping - ); - } - - const prebuiltById = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - savedQuery.id, - spaceScopedClient, - spaceId - ); - - const prebuiltByOriginId = - !prebuiltById && savedQuery.originId - ? await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - savedQuery.originId, - spaceScopedClient, - spaceId - ) - : false; - - savedQuery.attributes.prebuilt = prebuiltById || prebuiltByOriginId; - - const { - created_at: createdAt, - created_by: createdBy, - created_by_profile_uid: createdByProfileUid, - description, - id, - interval, - timeout, - platform, - query, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - updated_at: updatedAt, - updated_by: updatedBy, - updated_by_profile_uid: updatedByProfileUid, - prebuilt, - } = savedQuery.attributes; - - const data: SavedQueryResponse = { - created_at: createdAt, - created_by: createdBy, - created_by_profile_uid: createdByProfileUid, - description, - id, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - interval, - timeout, - platform, - query, - updated_at: updatedAt, - updated_by: updatedBy, - updated_by_profile_uid: updatedByProfileUid, - prebuilt, - saved_object_id: savedQuery.id, - }; - - return response.ok({ - body: { - data, - }, - }); } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return response.notFound({ @@ -139,6 +67,79 @@ export const readSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppC throw err; } + + if (savedQuery.attributes.ecs_mapping) { + // @ts-expect-error update types + savedQuery.attributes.ecs_mapping = convertECSMappingToObject( + savedQuery.attributes.ecs_mapping + ); + } + + const prebuiltById = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + savedQuery.id, + spaceScopedClient, + spaceId + ); + + const prebuiltByOriginId = + !prebuiltById && savedQuery.originId + ? await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + savedQuery.originId, + spaceScopedClient, + spaceId + ) + : false; + + savedQuery.attributes.prebuilt = prebuiltById || prebuiltByOriginId; + + const { + created_at: createdAt, + created_by: createdBy, + created_by_profile_uid: createdByProfileUid, + description, + id, + interval, + timeout, + platform, + query, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + updated_at: updatedAt, + updated_by: updatedBy, + updated_by_profile_uid: updatedByProfileUid, + prebuilt, + } = savedQuery.attributes; + + const data: SavedQueryResponse = { + created_at: createdAt, + created_by: createdBy, + created_by_profile_uid: createdByProfileUid, + description, + id, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + interval, + timeout, + platform, + query, + updated_at: updatedAt, + updated_by: updatedBy, + updated_by_profile_uid: updatedByProfileUid, + prebuilt, + saved_object_id: savedQuery.id, + }; + + return response.ok({ + body: { + data, + }, + }); } ); }; diff --git a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts index 1b0e4913aa68d..5eca74aaff052 100644 --- a/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/platform/plugins/shared/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -57,66 +57,67 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp }, }, async (context, request, response) => { - try { - const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( - osqueryContext, - request - ); - - const space = await osqueryContext.service.getActiveSpace(request); - const spaceId = space?.id ?? DEFAULT_SPACE_ID; - - const [, startPlugins] = await osqueryContext.getStartServices(); - const currentUser = await getUserInfo({ - request, - security: (startPlugins as StartPlugins).security, - logger: osqueryContext.logFactory.get('savedQuery'), - }); - const username = currentUser?.username ?? undefined; - const profileUid = currentUser?.profile_uid ?? undefined; - - const { - id, - description, - platform, - query, - version, - interval, - timeout, - snapshot, - removed, - ecs_mapping, - } = request.body; - - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id, - spaceScopedClient, - spaceId - ); - - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); - } + const spaceScopedClient = await createInternalSavedObjectsClientForSpaceId( + osqueryContext, + request + ); + + const space = await osqueryContext.service.getActiveSpace(request); + const spaceId = space?.id ?? DEFAULT_SPACE_ID; + + const [, startPlugins] = await osqueryContext.getStartServices(); + const currentUser = await getUserInfo({ + request, + security: (startPlugins as StartPlugins).security, + logger: osqueryContext.logFactory.get('savedQuery'), + }); + const username = currentUser?.username ?? undefined; + const profileUid = currentUser?.profile_uid ?? undefined; + + const { + id, + description, + platform, + query, + version, + interval, + timeout, + snapshot, + removed, + ecs_mapping, + } = request.body; + + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id, + spaceScopedClient, + spaceId + ); + + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); + } - const conflictingEntries = await spaceScopedClient.find<{ id: string }>({ - type: savedQuerySavedObjectType, - filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, - }); - - if ( - some( - filter( - conflictingEntries.saved_objects, - (soObject) => soObject.id !== request.params.id - ), - ['attributes.id', id] - ) - ) { - return response.conflict({ body: `Saved query with id "${id}" already exists.` }); - } + const conflictingEntries = await spaceScopedClient.find<{ id: string }>({ + type: savedQuerySavedObjectType, + filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, + }); + + if ( + some( + filter( + conflictingEntries.saved_objects, + (soObject) => soObject.id !== request.params.id + ), + ['attributes.id', id] + ) + ) { + return response.conflict({ body: `Saved query with id "${id}" already exists.` }); + } - const updatedSavedQuerySO = await spaceScopedClient.update( + let updatedSavedQuerySO; + try { + updatedSavedQuerySO = await spaceScopedClient.update( savedQuerySavedObjectType, request.params.id, { @@ -138,41 +139,6 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp refresh: 'wait_for', } ); - - if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { - // @ts-expect-error update types - updatedSavedQuerySO.attributes.ecs_mapping = - ecs_mapping || - (updatedSavedQuerySO.attributes.ecs_mapping && - // @ts-expect-error update types - convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || - {}; - } - - const { attributes } = updatedSavedQuerySO; - - const data: Partial = { - description: attributes.description, - id: attributes.id, - removed: attributes.removed, - snapshot: attributes.snapshot, - version: attributes.version, - ecs_mapping: attributes.ecs_mapping, - interval: attributes.interval, - timeout: attributes.timeout, - platform: attributes.platform, - query: attributes.query, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - updated_by_profile_uid: attributes.updated_by_profile_uid, - saved_object_id: updatedSavedQuerySO.id, - }; - - return response.ok({ - body: { - data, - }, - }); } catch (err) { if (SavedObjectsErrorHelpers.isNotFoundError(err)) { return response.notFound({ @@ -182,6 +148,41 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp throw err; } + + if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { + // @ts-expect-error update types + updatedSavedQuerySO.attributes.ecs_mapping = + ecs_mapping || + (updatedSavedQuerySO.attributes.ecs_mapping && + // @ts-expect-error update types + convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || + {}; + } + + const { attributes } = updatedSavedQuerySO; + + const data: Partial = { + description: attributes.description, + id: attributes.id, + removed: attributes.removed, + snapshot: attributes.snapshot, + version: attributes.version, + ecs_mapping: attributes.ecs_mapping, + interval: attributes.interval, + timeout: attributes.timeout, + platform: attributes.platform, + query: attributes.query, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + updated_by_profile_uid: attributes.updated_by_profile_uid, + saved_object_id: updatedSavedQuerySO.id, + }; + + return response.ok({ + body: { + data, + }, + }); } ); };