Skip to content

Commit 9b230da

Browse files
committed
fix: bugbot
1 parent cbb3ec4 commit 9b230da

2 files changed

Lines changed: 95 additions & 8 deletions

File tree

‎packages/deno/src/wrap-deno-request-handler.ts‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ function captureServeError(e: unknown): void {
4545
});
4646
}
4747

48-
// A user hook that throws must not fail the request or leave the span open.
49-
function callHook(name: string, hook: () => void): void {
48+
// A user callback that throws must not fail the request or leave the span open.
49+
function callUserCallback<T>(name: string, callback: () => T, fallback: T): T {
5050
try {
51-
hook();
51+
return callback();
5252
} catch (e) {
53-
debug.warn(`Error in the Deno.serve ${name} hook.`, e);
53+
debug.error(`Error in the Deno.serve ${name} callback.`, e);
54+
return fallback;
5455
}
5556
}
5657

@@ -66,7 +67,11 @@ function shouldCreateSpan(
6667
if (integration?.ignoreStaticAssets && request.method === 'GET' && isStaticAssetRequest(urlPath)) {
6768
return false;
6869
}
69-
return !integration?.ignoreIncomingRequests?.(urlPath, request);
70+
const ignoreIncomingRequests = integration?.ignoreIncomingRequests;
71+
return !(
72+
ignoreIncomingRequests &&
73+
callUserCallback('ignoreIncomingRequests', () => ignoreIncomingRequests(urlPath, request), false)
74+
);
7075
}
7176

7277
const assignIfSet = <T extends Record<string, unknown>, K extends keyof T>(
@@ -156,10 +161,12 @@ export const wrapDenoRequestHandler = <Addr extends Deno.Addr = Deno.Addr>(
156161

157162
const effectiveBodySize =
158163
integration?.maxRequestBodySize ?? (dataCollection.httpBodies.includes('incomingRequest') ? 'medium' : 'none');
164+
const ignoreRequestBody = integration?.ignoreRequestBody;
159165
if (
160166
request.method !== 'GET' &&
161167
effectiveBodySize !== 'none' &&
162-
!integration?.ignoreRequestBody?.(request.url, request)
168+
// If the filter throws, skip the body so it cannot leak data the filter was meant to hide.
169+
!(ignoreRequestBody && callUserCallback('ignoreRequestBody', () => ignoreRequestBody(request.url, request), true))
163170
) {
164171
await captureBodyFromWinterCGRequest(request, isolationScope, effectiveBodySize);
165172
}
@@ -189,7 +196,7 @@ export const wrapDenoRequestHandler = <Addr extends Deno.Addr = Deno.Addr>(
189196
return startSpanManual({ name, attributes }, async span => {
190197
const { onSpanCreated, onSpanEnd } = integration ?? {};
191198
if (onSpanCreated) {
192-
callHook('onSpanCreated', () => onSpanCreated(span, request));
199+
callUserCallback('onSpanCreated', () => onSpanCreated(span, request), undefined);
193200
}
194201
let res: Response;
195202

@@ -211,7 +218,7 @@ export const wrapDenoRequestHandler = <Addr extends Deno.Addr = Deno.Addr>(
211218
return streamResponse(
212219
span,
213220
res,
214-
onSpanEnd && (() => callHook('onSpanEnd', () => onSpanEnd(span, request, res))),
221+
onSpanEnd && (() => callUserCallback('onSpanEnd', () => onSpanEnd(span, request, res), undefined)),
215222
);
216223
});
217224
},

‎packages/deno/test/deno-serve.test.ts‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,3 +1330,83 @@ for (const [label, makeResponse] of [
13301330
);
13311331
});
13321332
}
1333+
1334+
Deno.test('Deno.serve should not capture the body and still respond when ignoreRequestBody throws', async () => {
1335+
resetGlobals();
1336+
const transactionEvents: TransactionEvent[] = [];
1337+
const errorEvents: ErrorEvent[] = [];
1338+
1339+
const client = init({
1340+
dsn: 'https://username@domain/123',
1341+
tracesSampleRate: 1,
1342+
traceLifecycle: 'static',
1343+
integrations: integrations => [
1344+
...integrations.filter(integration => integration.name !== 'DenoServe'),
1345+
denoServeIntegration({
1346+
ignoreRequestBody: () => {
1347+
throw new Error('ignoreRequestBody failed');
1348+
},
1349+
}),
1350+
],
1351+
beforeSend: (event: ErrorEvent) => {
1352+
errorEvents.push(event);
1353+
return null;
1354+
},
1355+
beforeSendTransaction: (event: TransactionEvent) => {
1356+
transactionEvents.push(event);
1357+
return null;
1358+
},
1359+
}) as DenoClient;
1360+
1361+
const server = await serveForTest(async request => new Response(await request.text()));
1362+
const response = await fetch(`${server.url}/upload`, { method: 'POST', body: 'secret' });
1363+
assertEquals(response.status, 200);
1364+
assertEquals(await response.text(), 'secret');
1365+
await server.close();
1366+
await client.flush();
1367+
1368+
assertEquals(errorEvents.length, 0);
1369+
assertEquals(transactionEvents.length, 1);
1370+
assertEquals(transactionEvents[0]?.request?.data, undefined);
1371+
});
1372+
1373+
Deno.test('Deno.serve should create the span and still respond when ignoreIncomingRequests throws', async () => {
1374+
resetGlobals();
1375+
const transactionEvents: TransactionEvent[] = [];
1376+
const errorEvents: ErrorEvent[] = [];
1377+
1378+
const client = init({
1379+
dsn: 'https://username@domain/123',
1380+
tracesSampleRate: 1,
1381+
traceLifecycle: 'static',
1382+
integrations: integrations => [
1383+
...integrations.filter(integration => integration.name !== 'DenoServe'),
1384+
denoServeIntegration({
1385+
ignoreIncomingRequests: () => {
1386+
throw new Error('ignoreIncomingRequests failed');
1387+
},
1388+
}),
1389+
],
1390+
beforeSend: (event: ErrorEvent) => {
1391+
errorEvents.push(event);
1392+
return null;
1393+
},
1394+
beforeSendTransaction: (event: TransactionEvent) => {
1395+
transactionEvents.push(event);
1396+
return null;
1397+
},
1398+
}) as DenoClient;
1399+
1400+
const server = await serveForTest(() => new Response('OK'));
1401+
const response = await fetch(`${server.url}/page`);
1402+
assertEquals(response.status, 200);
1403+
assertEquals(await response.text(), 'OK');
1404+
await server.close();
1405+
await client.flush();
1406+
1407+
assertEquals(errorEvents.length, 0);
1408+
assertEquals(
1409+
transactionEvents.map(event => event.transaction),
1410+
['GET /page'],
1411+
);
1412+
});

0 commit comments

Comments
 (0)