From 6316fa5407db70d422106a69e413de9350ae6e21 Mon Sep 17 00:00:00 2001 From: ajnart Date: Mon, 24 Aug 2026 09:23:23 +0200 Subject: [PATCH] fix(api): secure spotlight catalogs --- packages/api/src/router/board.ts | 18 +++++----- .../search-engine/search-engine-router.ts | 33 +++++++++++-------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/packages/api/src/router/board.ts b/packages/api/src/router/board.ts index 7a5bb2d69..06887bc76 100644 --- a/packages/api/src/router/board.ts +++ b/packages/api/src/router/board.ts @@ -181,13 +181,7 @@ const searchAccessibleBoardsAsync = async ( where: eq(boardUserPermissions.userId, ctx.session?.user.id ?? ""), }, groupPermissions: { - where: - permissionsOfCurrentUserGroupsWhenPresent.length >= 1 - ? inArray( - boardGroupPermissions.groupId, - permissionsOfCurrentUserGroupsWhenPresent.map((groupMember) => groupMember.groupId), - ) - : undefined, + where: getBoardGroupPermissionWhere(permissionsOfCurrentUserGroupsWhenPresent), }, }, }); @@ -561,9 +555,17 @@ export const boardRouter = createTRPCRouter({ }; }); }), - catalog: publicProcedure.query(async ({ ctx }) => searchAccessibleBoardsAsync(ctx, "")), + catalog: publicProcedure + .meta({ mcp: { enabled: true, description: "List every board the current user can access." } }) + .query(async ({ ctx }) => searchAccessibleBoardsAsync(ctx, "")), search: publicProcedure .input(z.object({ query: z.string(), limit: z.number().min(1).max(100).default(10) })) + .meta({ + mcp: { + enabled: true, + description: "Search accessible boards by name. REQUIRED: query (string). OPTIONAL: limit (number).", + }, + }) .query(async ({ ctx, input }) => searchAccessibleBoardsAsync(ctx, input.query, input.limit)), createBoard: permissionRequiredProcedure .requiresPermission("board-create") diff --git a/packages/api/src/router/search-engine/search-engine-router.ts b/packages/api/src/router/search-engine/search-engine-router.ts index 547ce8544..78e5c5883 100644 --- a/packages/api/src/router/search-engine/search-engine-router.ts +++ b/packages/api/src/router/search-engine/search-engine-router.ts @@ -130,21 +130,28 @@ export const searchEngineRouter = createTRPCRouter({ return null; }), - catalog: publicProcedure.query(async ({ ctx }) => { - return await ctx.db.query.searchEngines.findMany({ - where: ctx.session?.user ? undefined : eq(searchEngines.type, "generic"), - orderBy: asc(searchEngines.name), - with: { - integration: { - columns: { - kind: true, - url: true, - id: true, + catalog: publicProcedure + .meta({ + mcp: { + enabled: true, + description: "List search engines available to the current user.", + }, + }) + .query(async ({ ctx }) => { + return await ctx.db.query.searchEngines.findMany({ + where: ctx.session?.user ? undefined : eq(searchEngines.type, "generic"), + orderBy: asc(searchEngines.name), + with: { + integration: { + columns: { + kind: true, + url: true, + id: true, + }, }, }, - }, - }); - }), + }); + }), search: publicProcedure.input(searchSchema).query(async ({ ctx, input }) => { return await ctx.db.query.searchEngines.findMany({ // Public dashboards have no session: restrict anonymous users to generic