Skip to content

Commit cb925e3

Browse files
authored
fix(browser): Set : as a part of gecko protocol regex group. (#4153)
Fixes: #4138 The original issue about `file` keyword also happens for everything in that group, such as `http`, `blob` and so on. And the problem originates from : `.*` between that group and `:`. It seems that `:` follows those keywords without anything in between, as far as I have seen from the tests. Removing that from the regex solved the issue without breaking any tests other than `safari-extension` and `safari-web-extension`, which are special-cased in https://github.com/getsentry/sentry-javascript/blob/d2e0cc4d683364dc500a681631b5be7df40ffec6/packages/browser/src/stack-parsers.ts#L160, and adding those two to the matching group also solved the issue of an extra `:` coming from https://github.com/getsentry/sentry-javascript/blob/d2e0cc4d683364dc500a681631b5be7df40ffec6/packages/browser/src/stack-parsers.ts#L165.
1 parent 57dee8e commit cb925e3

File tree

11 files changed

+373
-7
lines changed

11 files changed

+373
-7
lines changed

packages/browser/src/stack-parsers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const chromeStackLineParser: StackLineParser = [CHROME_PRIORITY, chrome];
6767
// generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
6868
// We need this specific case for now because we want no other regex to match.
6969
const geckoREgex =
70-
/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|capacitor).*?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
70+
/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|safari-extension|safari-web-extension|capacitor)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
7171
const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
7272

7373
const gecko: StackLineParserFn = line => {

packages/browser/test/unit/tracekit/firefox.test.ts

+46
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,50 @@ describe('Tracekit - Firefox Tests', () => {
310310
},
311311
});
312312
});
313+
314+
it('should parse Firefox errors with `file` inside an identifier', () => {
315+
const FIREFOX_FILE_IN_IDENTIFIER = {
316+
stack:
317+
'us@https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js:1:296021\n' +
318+
'detectChanges@https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js:1:333807\n' +
319+
'handleProfileResult@https://www.random_website.com/main.4a4119c3cdfd10266d84.js:146:1018410\n',
320+
fileName: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js',
321+
lineNumber: 5529,
322+
columnNumber: 16,
323+
message: 'this.props.raw[this.state.dataSource].rows is undefined',
324+
name: 'TypeError',
325+
};
326+
327+
const stacktrace = exceptionFromError(parser, FIREFOX_FILE_IN_IDENTIFIER);
328+
329+
expect(stacktrace).toEqual({
330+
stacktrace: {
331+
frames: [
332+
{
333+
colno: 1018410,
334+
filename: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js',
335+
function: 'handleProfileResult',
336+
in_app: true,
337+
lineno: 146,
338+
},
339+
{
340+
colno: 333807,
341+
filename: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js',
342+
function: 'detectChanges',
343+
in_app: true,
344+
lineno: 1,
345+
},
346+
{
347+
colno: 296021,
348+
filename: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js',
349+
function: 'us',
350+
in_app: true,
351+
lineno: 1,
352+
},
353+
],
354+
},
355+
type: 'TypeError',
356+
value: 'this.props.raw[this.state.dataSource].rows is undefined',
357+
});
358+
});
313359
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function httpsCall() {
2+
webpackDevServer();
3+
}
4+
5+
const webpackDevServer = () => {
6+
Response.httpCode();
7+
};
8+
9+
class Response {
10+
constructor() {}
11+
12+
static httpCode(params) {
13+
throw new Error('test_err');
14+
}
15+
}
16+
17+
const decodeBlob = function() {
18+
(function readFile() {
19+
httpsCall();
20+
})();
21+
};
22+
23+
try {
24+
decodeBlob();
25+
} catch (err) {
26+
Sentry.captureException(err);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { expect } from '@playwright/test';
2+
import { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest(
8+
'should parse function identifiers that contain protocol names correctly',
9+
async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => {
10+
const url = await getLocalTestPath({ testDir: __dirname });
11+
12+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
13+
const frames = eventData.exception?.values?.[0].stacktrace?.frames;
14+
15+
runInChromium(() => {
16+
expect(frames).toMatchObject([
17+
{ function: '?' },
18+
{ function: '?' },
19+
{ function: 'decodeBlob' },
20+
{ function: 'readFile' },
21+
{ function: 'httpsCall' },
22+
{ function: 'webpackDevServer' },
23+
{ function: 'Function.httpCode' },
24+
]);
25+
});
26+
27+
runInFirefox(() => {
28+
expect(frames).toMatchObject([
29+
{ function: '?' },
30+
{ function: '?' },
31+
{ function: 'decodeBlob' },
32+
{ function: 'readFile' },
33+
{ function: 'httpsCall' },
34+
{ function: 'webpackDevServer' },
35+
{ function: 'httpCode' },
36+
]);
37+
});
38+
39+
runInWebkit(() => {
40+
expect(frames).toMatchObject([
41+
{ function: 'global code' },
42+
{ function: '?' },
43+
{ function: 'decodeBlob' },
44+
{ function: 'readFile' },
45+
{ function: 'httpsCall' },
46+
{ function: 'webpackDevServer' },
47+
{ function: 'httpCode' },
48+
]);
49+
});
50+
},
51+
);
52+
53+
sentryTest(
54+
'should not add any part of the function identifier to beginning of filename',
55+
async ({ getLocalTestPath, page }) => {
56+
const url = await getLocalTestPath({ testDir: __dirname });
57+
58+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
59+
60+
expect(eventData.exception?.values?.[0].stacktrace?.frames).toMatchObject(
61+
// specifically, we're trying to avoid values like `Blob@file://path/to/file` in frames with function names like `makeBlob`
62+
Array(7).fill({ filename: expect.stringMatching(/^file:\/?/) }),
63+
);
64+
},
65+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function https() {
2+
webpack();
3+
}
4+
5+
const webpack = () => {
6+
File.http();
7+
};
8+
9+
class File {
10+
constructor() {}
11+
12+
static http(params) {
13+
throw new Error('test_err');
14+
}
15+
}
16+
17+
const blob = function() {
18+
(function file() {
19+
https();
20+
})();
21+
};
22+
23+
try {
24+
blob();
25+
} catch (err) {
26+
Sentry.captureException(err);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { expect } from '@playwright/test';
2+
import { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest(
8+
'should parse function identifiers that are protocol names correctly',
9+
async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => {
10+
const url = await getLocalTestPath({ testDir: __dirname });
11+
12+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
13+
const frames = eventData.exception?.values?.[0].stacktrace?.frames;
14+
15+
runInChromium(() => {
16+
expect(frames).toMatchObject([
17+
{ function: '?' },
18+
{ function: '?' },
19+
{ function: 'blob' },
20+
{ function: 'file' },
21+
{ function: 'https' },
22+
{ function: 'webpack' },
23+
{ function: 'Function.http' },
24+
]);
25+
});
26+
27+
runInFirefox(() => {
28+
expect(frames).toMatchObject([
29+
{ function: '?' },
30+
{ function: '?' },
31+
{ function: 'blob' },
32+
{ function: 'file' },
33+
{ function: 'https' },
34+
{ function: 'webpack' },
35+
{ function: 'http' },
36+
]);
37+
});
38+
39+
runInWebkit(() => {
40+
expect(frames).toMatchObject([
41+
{ function: 'global code' },
42+
{ function: '?' },
43+
{ function: 'blob' },
44+
{ function: 'file' },
45+
{ function: 'https' },
46+
{ function: 'webpack' },
47+
{ function: 'http' },
48+
]);
49+
});
50+
},
51+
);
52+
53+
sentryTest(
54+
'should not add any part of the function identifier to beginning of filename',
55+
async ({ getLocalTestPath, page }) => {
56+
const url = await getLocalTestPath({ testDir: __dirname });
57+
58+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
59+
60+
expect(eventData.exception?.values?.[0].stacktrace?.frames).toMatchObject(
61+
Array(7).fill({ filename: expect.stringMatching(/^file:\/?/) }),
62+
);
63+
},
64+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
function foo() {
2+
bar();
3+
}
4+
5+
const bar = () => {
6+
Test.baz();
7+
};
8+
9+
class Test {
10+
constructor() {}
11+
12+
static baz(params) {
13+
throw new Error('test_err');
14+
}
15+
}
16+
17+
const qux = function() {
18+
(() => {
19+
(function() {
20+
foo();
21+
})();
22+
})();
23+
};
24+
25+
try {
26+
qux();
27+
} catch (err) {
28+
Sentry.captureException(err);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { expect } from '@playwright/test';
2+
import { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest(
8+
'should parse function identifiers correctly',
9+
async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => {
10+
const url = await getLocalTestPath({ testDir: __dirname });
11+
12+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
13+
const frames = eventData.exception?.values?.[0].stacktrace?.frames;
14+
15+
runInChromium(() => {
16+
expect(frames).toMatchObject([
17+
{ function: '?' },
18+
{ function: '?' },
19+
{ function: 'qux' },
20+
{ function: '?' },
21+
{ function: '?' },
22+
{ function: 'foo' },
23+
{ function: 'bar' },
24+
{ function: 'Function.baz' },
25+
]);
26+
});
27+
28+
runInFirefox(() => {
29+
expect(frames).toMatchObject([
30+
{ function: '?' },
31+
{ function: '?' },
32+
{ function: 'qux' },
33+
{ function: 'qux/<' },
34+
{ function: 'qux/</<' },
35+
{ function: 'foo' },
36+
{ function: 'bar' },
37+
{ function: 'baz' },
38+
]);
39+
});
40+
41+
runInWebkit(() => {
42+
expect(frames).toMatchObject([
43+
{ function: 'global code' },
44+
{ function: '?' },
45+
{ function: 'qux' },
46+
{ function: '?' },
47+
{ function: '?' },
48+
{ function: 'foo' },
49+
{ function: 'bar' },
50+
{ function: 'baz' },
51+
]);
52+
});
53+
},
54+
);
55+
56+
sentryTest(
57+
'should not add any part of the function identifier to beginning of filename',
58+
async ({ getLocalTestPath, page }) => {
59+
const url = await getLocalTestPath({ testDir: __dirname });
60+
61+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
62+
63+
expect(eventData.exception?.values?.[0].stacktrace?.frames).toMatchObject(
64+
// specifically, we're trying to avoid values like `Blob@file://path/to/file` in frames with function names like `makeBlob`
65+
Array(8).fill({ filename: expect.stringMatching(/^file:\/?/) }),
66+
);
67+
},
68+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title></title>
6+
<script src="{{htmlWebpackPlugin.options.initialization}}"></script>
7+
</head>
8+
<body>
9+
<script src="{{htmlWebpackPlugin.options.subject}}"></script>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)