diff --git a/.changeset/twelve-snakes-sell.md b/.changeset/twelve-snakes-sell.md new file mode 100644 index 0000000000..7f91b2910f --- /dev/null +++ b/.changeset/twelve-snakes-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend': patch +--- + +Set the default length limit to search query to 100. To override it, define `search.maxTermLength` in the config file. diff --git a/plugins/search-backend/config.d.ts b/plugins/search-backend/config.d.ts index 3dbdf78dfe..25ae59d273 100644 --- a/plugins/search-backend/config.d.ts +++ b/plugins/search-backend/config.d.ts @@ -22,6 +22,11 @@ export interface Config { */ maxPageLimit?: number; + /** + * Sets the maximum term length for the search string. Defaults to 100. + */ + maxTermLength?: number; + /** * Options related to the search integration with the Backstage permissions system */ diff --git a/plugins/search-backend/src/service/router.test.ts b/plugins/search-backend/src/service/router.test.ts index 7ec8603307..73523f2b62 100644 --- a/plugins/search-backend/src/service/router.test.ts +++ b/plugins/search-backend/src/service/router.test.ts @@ -60,7 +60,7 @@ describe('createRouter', () => { }, config: new ConfigReader({ permissions: { enabled: false }, - search: { maxPageLimit: 200 }, + search: { maxPageLimit: 200, maxTermLength: 20 }, }), permissions: mockPermissionEvaluator, logger, @@ -162,6 +162,19 @@ describe('createRouter', () => { }); }); + it('should reject term length over configured max', async () => { + const response = await request(app).get( + `/query?term=HelloWorld1234567890!`, + ); + + expect(response.status).toEqual(400); + expect(response.body).toMatchObject({ + error: { + message: /The term length "21" is greater than "20"/i, + }, + }); + }); + it('removes backend-only properties from search documents', async () => { mockSearchEngine.query.mockResolvedValue({ results: [ diff --git a/plugins/search-backend/src/service/router.ts b/plugins/search-backend/src/service/router.ts index dca632dfaf..876cb5d103 100644 --- a/plugins/search-backend/src/service/router.ts +++ b/plugins/search-backend/src/service/router.ts @@ -63,6 +63,7 @@ export type RouterOptions = { }; const defaultMaxPageLimit = 100; +const defaultMaxTermLength = 100; const allowedLocationProtocols = ['http:', 'https:']; /** @@ -77,8 +78,19 @@ export async function createRouter( const maxPageLimit = config.getOptionalNumber('search.maxPageLimit') ?? defaultMaxPageLimit; + const maxTermLength = + config.getOptionalNumber('search.maxTermLength') ?? defaultMaxTermLength; + const requestSchema = z.object({ - term: z.string().default(''), + term: z + .string() + .refine( + term => term.length <= maxTermLength, + term => ({ + message: `The term length "${term.length}" is greater than "${maxTermLength}"`, + }), + ) + .default(''), filters: jsonObjectSchema.optional(), types: z .array(z.string().refine(type => Object.keys(types).includes(type)))