Skip to content

Commit f25a327

Browse files
authored
fix(browser): Handle data urls in errors caught by globalHandlersIntegration (#17216)
[#17218](#17218) adjusted data URI stack line parsing for most errors that go through the stack parsers (fully for node, line truncation in general). This patch now applies a similar logic to `globalHandlersIntegration` which in some conditions applies a stack frame that doesn't go through the same stack parser.
1 parent 08fb932 commit f25a327

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const workerCode = `
2+
self.addEventListener('message', (event) => {
3+
if (event.data.type === 'error') {
4+
throw new Error('Error thrown in worker');
5+
}
6+
});
7+
`;
8+
9+
const worker = new Worker(`data:text/javascript;base64,${btoa(workerCode)}`);
10+
11+
worker.postMessage({ type: 'error' });
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../../utils/fixtures';
3+
import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers';
4+
5+
/**
6+
* Tests a special case where the `globalHandlersIntegration` itself creates a stack frame instead of using
7+
* stack parsers. This is necessary because we don't always get an `error` object passed to `window.onerror`.
8+
* @see `globalhandlers.ts#_enhanceEventWithInitialFrame`
9+
*/
10+
sentryTest('detects and handles data urls on first stack frame', async ({ getLocalTestUrl, page }) => {
11+
const url = await getLocalTestUrl({ testDir: __dirname });
12+
13+
const errorEventPromise = waitForErrorRequest(page, e => {
14+
return !!e.exception?.values;
15+
});
16+
17+
await page.goto(url);
18+
19+
const errorEvent = envelopeRequestParser(await errorEventPromise);
20+
21+
expect(errorEvent?.exception?.values?.[0]).toEqual({
22+
mechanism: {
23+
handled: false,
24+
synthetic: true,
25+
type: 'auto.browser.global_handlers.onerror',
26+
},
27+
stacktrace: {
28+
frames: [
29+
{
30+
colno: expect.any(Number), // webkit reports different colno than chromium
31+
filename: '<data:text/javascript,base64>',
32+
function: '?',
33+
in_app: true,
34+
lineno: 4,
35+
},
36+
],
37+
},
38+
type: 'Error',
39+
value: expect.stringMatching(/(Uncaught )?Error: Error thrown in worker/), // webikt throws without "Uncaught "
40+
});
41+
});

packages/browser/src/integrations/globalhandlers.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ function _enhanceEventWithInitialFrame(
171171

172172
const colno = column;
173173
const lineno = line;
174-
const filename = isString(url) && url.length > 0 ? url : getLocationHref();
174+
const filename = getFilenameFromUrl(url) ?? getLocationHref();
175175

176176
// event.exception.values[0].stacktrace.frames
177177
if (ev0sf.length === 0) {
@@ -199,3 +199,20 @@ function getOptions(): { stackParser: StackParser; attachStacktrace?: boolean }
199199
};
200200
return options;
201201
}
202+
203+
function getFilenameFromUrl(url: string | undefined): string | undefined {
204+
if (!isString(url) || url.length === 0) {
205+
return undefined;
206+
}
207+
208+
// stack frame urls can be data urls, for example when initializing a Worker with a base64 encoded script
209+
// in this case we just show the data prefix and mime type to avoid too long raw data urls
210+
if (url.startsWith('data:')) {
211+
const match = url.match(/^data:([^;]+)/);
212+
const mimeType = match ? match[1] : 'text/javascript';
213+
const isBase64 = url.includes('base64,');
214+
return `<data:${mimeType}${isBase64 ? ',base64' : ''}>`;
215+
}
216+
217+
return url.slice(0, 1024);
218+
}

0 commit comments

Comments
 (0)