8000 browser: InboundFilters integration implementation · phthhieu/sentry-javascript@ddc6d04 · GitHub
[go: up one dir, main page]

Skip to content

Commit ddc6d04

Browse files
committed
browser: InboundFilters integration implementation
1 parent 30342ae commit ddc6d04

File tree

6 files changed

+167
-42
lines changed

6 files changed

+167
-42
lines changed

packages/browser/src/backend.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,18 @@ export interface BrowserOptions extends Options {
1818
ignoreErrors?: Array<string | RegExp>;
1919

2020
/**
21-
* A pattern for error URLs which should not be sent to Sentry. To whitelist
22-
* certain errors instead, use {@link Options.whitelistUrls}. By default, all
23-
* errors will be sent.
21+
* A pattern for error URLs which should not be sent to Sentry.
22+
* To whitelist certain errors instead, use {@link Options.whitelistUrls}.
23+
* By default, all errors will be sent.
2424
*/
25-
ignoreUrls?: Array<string | RegExp>;
25+
blacklistUrls?: Array<string | RegExp>;
2626

2727
/**
28-
* A pattern for error URLs which should exclusively be sent to Sentry. This
29-
* is the opposite of {@link Options.ignoreUrls}. By default, all errors will
30-
* be sent.
28+
* A pattern for error URLs which should exclusively be sent to Sentry.
29+
* This is the opposite of {@link Options.blacklistUrls}.
30+
* By default, all errors will be sent.
3131
*/
3232
whitelistUrls?: Array<string | RegExp>;
33-
34-
/**
35-
* Defines a list source code file paths. Only errors including these paths in
36-
* their stack traces will be sent to Sentry. By default, all errors will be
37-
* sent.
38-
*/
39-
includePaths?: Array<string | RegExp>;
4033
}
4134

4235
/** The Sentry Browser SDK Backend. */
@@ -99,8 +92,6 @@ export class BrowserBackend implements Backend {
9992
return this.eventFromMessage(ex);
10093
}
10194

102-
// TODO: Create `shouldDropEvent` method to gather all user-options
103-
10495
const event = eventFromStacktrace(computeStackTrace(exception as Error));
10596

10697
return {
@@ -146,28 +137,6 @@ export class BrowserBackend implements Backend {
146137
frames,
147138
},
148139
};
149-
150-
// TODO: Revisit ignoreUrl behavior
151-
152-
// Since we know this is a synthetic trace, the top frame (this function call)
153-
// MUST be from Raven.js, so mark it for trimming
154-
// We add to the trim counter so that callers can choose to trim extra frames, such
155-
// as utility functions.
156-
157-
// stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
158-
// let initialCall = Array.isArray(stack.stack) && stack.stack[1];
159-
160-
// if stack[1] is `eventFromException`, it means that someone passed a string to it and we redirected that call
161-
// to be handled by `eventFromMessage`, thus `initialCall` is the 3rd one, not 2nd
162-
// initialCall => captureException(string) => captureMessage(string)
163-
// TODO: Verify if this is actually a correct name
164-
// if (initialCall && initialCall.func === 'eventFromException') {
165-
// initialCall = stack.stack[2];
166-
// }
167-
168-
// const fileurl = (initialCall && initialCall.url) || '';
169-
170-
// TODO: Create `shouldDropEvent` method to gather all user-options
171140
}
172141

173142
/**
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { logger } from '@sentry/core';
2+
import { getDefaultHub } from '@sentry/hub';
3+
import { Integration, SentryEvent } from '@sentry/types';
4+
import { joinRegExp } from '@sentry/utils/string';
5+
import { BrowserOptions } from '../backend';
6+
7+
// "Script error." is hard coded into browsers for errors that it can't read.
8+
// this is the result of a script being pulled in from an external domain and CORS.
9+
const DEFAULT_IGNORE_ERRORS = [/^Script error\.?$/, /^Javascript error: Script error\.? on line 0$/];
10+
const MATCH_NOTHING = new RegExp(/.^/);
11+
const MATCH_EVERYTHING = new RegExp('');
12+
13+
/** Inbound filters configurable by the user */
14+
export class InboundFilters implements Integration {
15+
/** TODO */
16+
private ignoreErrors: RegExp = joinRegExp(DEFAULT_IGNORE_ERRORS);
17+
/** TODO */
18+
private blacklistUrls: RegExp = MATCH_NOTHING;
19+
/** TODO */
20+
private whitelistUrls: RegExp = MATCH_EVERYTHING;
21+
22+
/**
23+
* @inheritDoc
24+
*/
25+
public name: string = 'InboundFilters';
26+
/**
27+
* @inheritDoc
28+
*/
29+
public install(options: BrowserOptions = {}): void {
30+
this.configureOptions(options);
31+
32+
getDefaultHub().addEventProcessor(async (event: SentryEvent) => {
33+
if (this.shouldDropEvent(event)) {
34+
return null;
35+
}
36+
return event;
37+
});
38+
}
39+
40+
/** TODO */
41+
private configureOptions(options: BrowserOptions): void {
42+
if (options.ignoreErrors) {
43+
// TODO: Afair people wanted an option to disable defaults. Should we do it?
44+
this.ignoreErrors = joinRegExp([...DEFAULT_IGNORE_ERRORS, ...options.ignoreErrors]);
45+
}
46+
if (options.blacklistUrls) {
47+
this.blacklistUrls = joinRegExp([...options.blacklistUrls]);
48+
}
49+
if (options.whitelistUrls) {
50+
this.whitelistUrls = joinRegExp([...options.whitelistUrls]);
51+
}
52+
}
53+
54+
/** TODO */
55+
private shouldDropEvent(event: SentryEvent): boolean {
56+
if (this.isIgnoredError(event)) {
57+
logger.warn(`Event dropped due to being ignored.\n Event: ${event.event_id}`);
58+
return true;
59+
}
60+
if (this.isBlacklistedUrl(event)) {
61+
logger.warn(`Event dropped due to being blacklisted.\n Event: ${event.event_id}`);
62+
return true;
63+ }
64+
if (!this.isWhitelistedUrl(event)) {
65+
logger.warn(`Event dropped due to not being whitelisted.\n Event: ${event.event_id}`);
66+
return true;
67+
}
68+
return false;
69+
}
70+
71+
/** TODO */
72+
private isIgnoredError(event: SentryEvent): boolean {
73+
return this.getPossibleEventMessages(event).some(message => this.ignoreErrors.test(message));
74+
}
75+
76+
/** TODO */
77+
private isWhitelistedUrl(event: SentryEvent): boolean {
78+
return this.whitelistUrls.test(this.getEventFilterUrl(event));
79+
}
80+
81+
/** TODO */
82+
private isBlacklistedUrl(event: SentryEvent): boolean {
83+
return this.blacklistUrls.test(this.getEventFilterUrl(event));
84+
}
85+
86+
/** TODO */
87+
private getPossibleEventMessages(event: SentryEvent): string[] {
88+
const evt = event as any;
89+
90+
if (evt.message) {
91+
return [evt.message];
92+
} else if (evt.exception) {
93+
try {
94+
const { type, value } = evt.exception.values[0];
95+
return [`${value}`, `${type}: ${value}`];
96+
} catch (oO) {
97+
logger.error(`Cannot extract message for event ${event.event_id}`);
98+
return [];
99+
}
100+
} else {
101+
return [];
102+
}
103+
}
104+
105+
/** TODO */
106+
private getEventFilterUrl(event: SentryEvent): string {
107+
const evt = event as any;
108+
109+
if (evt.stacktrace) {
110+
return evt.stacktrace.frames[0].filename;
111+
} else if (evt.exception) {
112+
try {
113+
return evt.exception.values[0].stacktrace.frames[0].filename;
114+
} catch (oO) {
115+
logger.error(`Cannot extract url for event ${event.event_id}`);
116+
return '';
117+
}
118+
} else {
119+
return '';
120+
}
121+
}
122+
}

packages/browser/src/integrations/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export { FunctionToString } from './functiontostring';
33
export { TryCatch } from './trycatch';
44
export { Breadcrumbs } from './breadcrumbs';
55
export { SDKInformation } from './sdkinformation';
6+
export { InboundFilters } from './inboundfilters';

packages/browser/src/parsers.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ export function getEventOptionsFromPlainObject(exception: {}): {
3030
/** TODO */
3131
export function eventFromStacktrace(stacktrace: TraceKitStackTrace): SentryEvent {
3232
const frames = prepareFramesForEvent(stacktrace.stack);
33-
// const prefixedMessage =
34-
// (stack.name ? stack.name + ': ' : '') + (stack.message || '');
3533
const transaction = stacktrace.url || (stacktrace.stack && stacktrace.stack[0].url) || '<unknown>';
3634

3735
const ex = {

packages/browser/src/sdk.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import { initAndBind } from '@sentry/core';
22
import { BrowserOptions } from './backend';
33
import { BrowserClient } from './client';
4-
import { Breadcrumbs, FunctionToString, GlobalHandlers, SDKInformation, TryCatch } from './integrations';
4+
import {
5+
Breadcrumbs,
6+
FunctionToString,
7+
GlobalHandlers,
8+
InboundFilters,
9+
SDKInformation,
10+
// TryCatch,
11+
} from './integrations';
512

613
export const defaultIntegrations = [
714
new FunctionToString(),
8-
new TryCatch(),
15+
// new TryCatch(),
916
new Breadcrumbs(),
1017
new SDKInformation(),
1118
new GlobalHandlers(),
19+
new InboundFilters(),
1220
];
1321

1422
/**

packages/utils/src/string.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isString } from './is';
2+
13
/**
24
* Encodes given object into url-friendly format
35
*
@@ -79,3 +81,28 @@ export function safeJoin(input: any[], delimiter?: string): string {
7981

8082
return output.join(delimiter);
8183
}
84+
85+
/**
86+
* Combine an array of regular expressions and strings into one large regexp
87+
*/
88+
export function joinRegExp(patterns: Array<RegExp | string>): RegExp {
89+
const joinedPattern = Array.isArray(patterns)
90+
? patterns
91+
.map(pattern => {
92+
if (isString(pattern)) {
93+
// If it's a string, we need to escape it
94+
// Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
95+
return (pattern as string).replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
96+
} else if (pattern && (pattern as RegExp).source) {
97+
// If it's a regexp already, we want to extract the source
98+
return (pattern as RegExp).source;
99+
}
100+
// Intentionally skip other cases
101+
return undefined;
102+
})
103+
.filter(x => !!x)
104+
.join('|')
105+
: '';
106+
107+
return new RegExp(joinedPattern, 'i');
108+
}

0 commit comments

Comments
 (0)
0