Skip to content

Commit 47347ed

Browse files
committed
Redact Gravity API key from upstream errors
1 parent 679501d commit 47347ed

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

web/src/app/api/v1/gravity-index/search/__tests__/gravity-index-search.test.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ describe('/api/v1/gravity-index/search POST endpoint', () => {
1818
let mockTrackEvent: TrackEventFn
1919
let mockGetUserInfoFromApiKey: GetUserInfoFromApiKeyFn
2020
let mockFetch: typeof globalThis.fetch
21+
let mockWarn: ReturnType<typeof mock>
2122

2223
beforeEach(() => {
24+
mockWarn = mock(() => {})
2325
mockLogger = {
2426
error: mock(() => {}),
25-
warn: mock(() => {}),
27+
warn: mockWarn,
2628
info: mock(() => {}),
2729
debug: mock(() => {}),
2830
}
@@ -194,4 +196,48 @@ describe('/api/v1/gravity-index/search POST endpoint', () => {
194196
expect(res.status).toBe(502)
195197
expect(await res.json()).toEqual({ error: 'bad request' })
196198
})
199+
200+
test('redacts Gravity API key from upstream error responses and logs', async () => {
201+
mockFetch = Object.assign(
202+
mock(async () =>
203+
new Response(
204+
JSON.stringify({
205+
detail: [
206+
{
207+
input: {
208+
query: '',
209+
platform_api_key: 'gravity-key',
210+
},
211+
},
212+
],
213+
}),
214+
{ status: 422, headers: { 'Content-Type': 'application/json' } },
215+
),
216+
),
217+
{ preconnect: () => {} },
218+
) as typeof fetch
219+
const req = new NextRequest(
220+
'http://localhost:3000/api/v1/gravity-index/search',
221+
{
222+
method: 'POST',
223+
headers: { Authorization: 'Bearer valid' },
224+
body: JSON.stringify({ query: 'transactional email' }),
225+
},
226+
)
227+
228+
const res = await postGravityIndexSearch({
229+
req,
230+
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
231+
logger: mockLogger,
232+
loggerWithContext: mockLoggerWithContext,
233+
trackEvent: mockTrackEvent,
234+
fetch: mockFetch,
235+
serverEnv: testServerEnv,
236+
})
237+
238+
expect(res.status).toBe(502)
239+
expect(JSON.stringify(await res.json())).not.toContain('gravity-key')
240+
expect(JSON.stringify(mockWarn.mock.calls)).not.toContain('gravity-key')
241+
expect(JSON.stringify(mockWarn.mock.calls)).toContain('[redacted]')
242+
})
197243
})

web/src/app/api/v1/gravity-index/search/_post.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ const getErrorMessage = (value: unknown): string | undefined => {
3434
return typeof message === 'string' ? message : undefined
3535
}
3636

37+
const redactGravityApiKey = (text: string, gravityApiKey: string) =>
38+
text.split(gravityApiKey).join('[redacted]')
39+
3740
export async function postGravityIndexSearch(params: {
3841
req: NextRequest
3942
getUserInfoFromApiKey: GetUserInfoFromApiKeyFn
@@ -116,15 +119,20 @@ export async function postGravityIndexSearch(params: {
116119
})
117120

118121
const text = await response.text()
122+
const redactedText = redactGravityApiKey(text, publisherKey)
119123
const json = tryParseJson(text)
120124

121125
if (!response.ok) {
122-
const error = (getErrorMessage(json) ?? text) || 'Gravity Index failed'
126+
const upstreamError = getErrorMessage(json)
127+
const error =
128+
(upstreamError
129+
? redactGravityApiKey(upstreamError, publisherKey)
130+
: redactedText) || 'Gravity Index failed'
123131
logger.warn(
124132
{
125133
status: response.status,
126134
statusText: response.statusText,
127-
body: text.slice(0, 500),
135+
body: redactedText.slice(0, 500),
128136
},
129137
'Gravity Index upstream request failed',
130138
)
@@ -138,7 +146,10 @@ export async function postGravityIndexSearch(params: {
138146
}
139147

140148
if (!json || typeof json !== 'object' || Array.isArray(json)) {
141-
logger.warn({ body: text.slice(0, 500) }, 'Invalid Gravity Index JSON')
149+
logger.warn(
150+
{ body: redactedText.slice(0, 500) },
151+
'Invalid Gravity Index JSON',
152+
)
142153
return NextResponse.json(
143154
{ error: 'Invalid Gravity Index response' },
144155
{ status: 502 },

0 commit comments

Comments
 (0)