Skip to content

Commit b1b0cbf

Browse files
PickHubCopilotheiskr
authored
Cap AI search query length at 2000 chars (#61479)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Kevin Heis <heiskr@users.noreply.github.com>
1 parent 3435479 commit b1b0cbf

4 files changed

Lines changed: 38 additions & 0 deletions

File tree

src/search/components/input/SearchOverlay.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type { AIReference } from '../types'
2222
import type { AutocompleteSearchHit, GeneralSearchHit } from '@/search/types'
2323

2424
import { sanitizeSearchQuery } from '@/search/lib/sanitize-search-query'
25+
import { MAX_QUERY_LENGTH } from '@/search/lib/ai-search-constants'
2526

2627
import {
2728
SearchContext,
@@ -693,6 +694,7 @@ export function SearchOverlay({
693694
ref={inputRef}
694695
value={urlSearchInputQuery}
695696
onChange={handleSearchQueryChange}
697+
maxLength={MAX_QUERY_LENGTH}
696698
leadingVisual={<SearchIcon />}
697699
role="combobox"
698700
// In AskAI the search input not longer "controls" the suggestions list, because there is no list, so we remove the aria-controls attribute
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Maximum query length (chars) we will forward to cse-copilot. Larger
2+
// payloads are almost always pasted docs pages or unrelated content
3+
// and either time out or return no-answer. See github/cse-copilot#1214.
4+
export const MAX_QUERY_LENGTH = 2000

src/search/lib/ai-search-proxy.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getHmacWithEpoch } from '@/search/lib/helpers/get-cse-copilot-auth'
66
import { getCSECopilotSource } from '@/search/lib/helpers/cse-copilot-docs-versions'
77
import type { ExtendedRequest } from '@/types'
88
import { handleExternalSearchAnalytics } from '@/search/lib/helpers/external-search-analytics'
9+
import { MAX_QUERY_LENGTH } from '@/search/lib/ai-search-constants'
910

1011
const logger = createLogger(import.meta.url)
1112

@@ -26,6 +27,19 @@ export const aiSearchProxy = async (req: ExtendedRequest, res: Response) => {
2627
errors.push({ message: `Invalid 'query' in request body. Must be a string` })
2728
}
2829

30+
if (typeof query === 'string' && query.length > MAX_QUERY_LENGTH) {
31+
statsd.increment('ai-search.query_too_large', 1, [
32+
`version:${version}`,
33+
`language:${req.language}`,
34+
`queryLength:${query.length}`,
35+
])
36+
res.status(413).json({
37+
errors: [{ message: `Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters` }],
38+
upstreamStatus: 413,
39+
})
40+
return
41+
}
42+
2943
let docsSource = ''
3044
try {
3145
docsSource = getCSECopilotSource(version)

src/search/tests/api-ai-search.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect, test, describe, beforeAll, afterAll } from 'vitest'
22

33
import { post } from '@/tests/helpers/e2etest'
44
import { startMockServer, stopMockServer } from '@/tests/mocks/start-mock-server'
5+
import { MAX_QUERY_LENGTH } from '@/search/lib/ai-search-constants'
56

67
describe('AI Search Routes', () => {
78
beforeAll(() => {
@@ -183,6 +184,23 @@ describe('AI Search Routes', () => {
183184
expect(responseBody.errors[0].message).toBe("Invalid 'query' in request body. Must be a string")
184185
})
185186

187+
test('should reject queries longer than the maximum allowed length', async () => {
188+
const longQuery = 'a'.repeat(MAX_QUERY_LENGTH + 1)
189+
const response = await post('/api/ai-search/v1', {
190+
body: JSON.stringify({ query: longQuery, version: 'dotcom' }),
191+
headers: { 'Content-Type': 'application/json' },
192+
})
193+
194+
const responseBody = JSON.parse(response.body)
195+
196+
expect(response.statusCode).toBe(413)
197+
expect(responseBody.upstreamStatus).toBe(413)
198+
expect(responseBody.errors).toBeDefined()
199+
expect(responseBody.errors[0].message).toBe(
200+
`Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`,
201+
)
202+
})
203+
186204
test('should handle malformed JSON in request body', async () => {
187205
const response = await post('/api/ai-search/v1', {
188206
body: '{ invalid json }',

0 commit comments

Comments
 (0)