diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx index 94b1f092e1ede8..cb7a95b4271e78 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx @@ -44,7 +44,7 @@ const EditSavedQueryPageComponent = () => { useBreadcrumbs('saved_query_edit', { savedQueryName: savedQueryDetails?.attributes?.id ?? '' }); const elasticPrebuiltQuery = useMemo( - () => savedQueryDetails?.attributes?.version, + () => savedQueryDetails?.attributes?.prebuilt, [savedQueryDetails] ); const viewMode = useMemo( diff --git a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts index c2a2ad7fa8619a..a27c4a09530980 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -9,8 +9,10 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; +import { isSavedQueryPrebuilt } from './utils'; -export const deleteSavedQueryRoute = (router: IRouter) => { +export const deleteSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.delete( { path: '/internal/osquery/saved_query/{id}', @@ -25,6 +27,11 @@ export const deleteSavedQueryRoute = (router: IRouter) => { const coreContext = await context.core; const savedObjectsClient = coreContext.savedObjects.client; + const isPrebuilt = await isSavedQueryPrebuilt(osqueryContext, request.params.id); + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); + } + await savedObjectsClient.delete(savedQuerySavedObjectType, request.params.id, { refresh: 'wait_for', }); diff --git a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts index a2b85dbf539d9d..abf62ca782daa5 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts @@ -7,11 +7,14 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; + +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; import { convertECSMappingToObject } from '../utils'; +import { getInstalledSavedQueriesMap } from './utils'; -export const findSavedQueryRoute = (router: IRouter) => { +export const findSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.get( { path: '/internal/osquery/saved_query', @@ -34,6 +37,7 @@ export const findSavedQueryRoute = (router: IRouter) => { const savedQueries = await savedObjectsClient.find<{ ecs_mapping: Array<{ field: string; value: string }>; + prebuilt: boolean; }>({ type: savedQuerySavedObjectType, page: parseInt(request.query.pageIndex ?? '0', 10) + 1, @@ -43,10 +47,13 @@ export const findSavedQueryRoute = (router: IRouter) => { sortOrder: request.query.sortDirection ?? 'desc', }); + const prebuiltSavedQueriesMap = await getInstalledSavedQueriesMap(osqueryContext); const savedObjects = savedQueries.saved_objects.map((savedObject) => { // eslint-disable-next-line @typescript-eslint/naming-convention const ecs_mapping = savedObject.attributes.ecs_mapping; + savedObject.attributes.prebuilt = !!prebuiltSavedQueriesMap[savedObject.id]; + if (ecs_mapping) { // @ts-expect-error update types savedObject.attributes.ecs_mapping = convertECSMappingToObject(ecs_mapping); diff --git a/x-pack/plugins/osquery/server/routes/saved_query/index.ts b/x-pack/plugins/osquery/server/routes/saved_query/index.ts index e0bf4f622c42c7..025199dcba6b63 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/index.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/index.ts @@ -16,8 +16,8 @@ import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const initSavedQueryRoutes = (router: IRouter, context: OsqueryAppContext) => { createSavedQueryRoute(router, context); - deleteSavedQueryRoute(router); - findSavedQueryRoute(router); - readSavedQueryRoute(router); + deleteSavedQueryRoute(router, context); + findSavedQueryRoute(router, context); + readSavedQueryRoute(router, context); updateSavedQueryRoute(router, context); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts index 1c206464d1f650..d1627d220682ad 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -7,11 +7,13 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; +import { isSavedQueryPrebuilt } from './utils'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; import { convertECSMappingToObject } from '../utils'; -export const readSavedQueryRoute = (router: IRouter) => { +export const readSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.get( { path: '/internal/osquery/saved_query/{id}', @@ -28,6 +30,7 @@ export const readSavedQueryRoute = (router: IRouter) => { const savedQuery = await savedObjectsClient.get<{ ecs_mapping: Array<{ key: string; value: Record }>; + prebuilt: boolean; }>(savedQuerySavedObjectType, request.params.id); if (savedQuery.attributes.ecs_mapping) { @@ -37,6 +40,8 @@ export const readSavedQueryRoute = (router: IRouter) => { ); } + savedQuery.attributes.prebuilt = await isSavedQueryPrebuilt(osqueryContext, savedQuery.id); + return response.ok({ body: savedQuery, }); diff --git a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts index 1d2bf153afd7fb..e2686868b7effd 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -9,6 +9,7 @@ import { filter } from 'lodash'; import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; +import { isSavedQueryPrebuilt } from './utils'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -63,6 +64,12 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp ecs_mapping, } = request.body; + const isPrebuilt = await isSavedQueryPrebuilt(osqueryContext, request.params.id); + + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); + } + const conflictingEntries = await savedObjectsClient.find<{ id: string }>({ type: savedQuerySavedObjectType, filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, diff --git a/x-pack/plugins/osquery/server/routes/saved_query/utils.ts b/x-pack/plugins/osquery/server/routes/saved_query/utils.ts new file mode 100644 index 00000000000000..d99d5b70f0dab0 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/saved_query/utils.ts @@ -0,0 +1,54 @@ +/* + * 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 { find, reduce } from 'lodash'; +import { KibanaAssetReference } from '@kbn/fleet-plugin/common'; + +import { OSQUERY_INTEGRATION_NAME } from '../../../common'; +import { savedQuerySavedObjectType } from '../../../common/types'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +const getInstallation = async (osqueryContext: OsqueryAppContext) => + await osqueryContext.service + .getPackageService() + ?.asInternalUser?.getInstallation(OSQUERY_INTEGRATION_NAME); + +export const getInstalledSavedQueriesMap = async (osqueryContext: OsqueryAppContext) => { + const installation = await getInstallation(osqueryContext); + if (installation) { + return reduce( + installation.installed_kibana, + // @ts-expect-error not sure why it shouts, but still it's properly typed + (acc: Record, item: KibanaAssetReference) => { + if (item.type === savedQuerySavedObjectType) { + return { ...acc, [item.id]: item }; + } + }, + {} + ); + } + + return {}; +}; + +export const isSavedQueryPrebuilt = async ( + osqueryContext: OsqueryAppContext, + savedQueryId: string +) => { + const installation = await getInstallation(osqueryContext); + + if (installation) { + const installationSavedQueries = find( + installation.installed_kibana, + (item) => item.type === savedQuerySavedObjectType && item.id === savedQueryId + ); + + return !!installationSavedQueries; + } + + return false; +};