|
1 |
| -import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; |
2 |
| -import { Event, Integration, StackFrame } from '@sentry/types'; |
| 1 | +import { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types'; |
3 | 2 | import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils';
|
4 | 3 |
|
5 | 4 | // "Script error." is hard coded into browsers for errors that it can't read.
|
6 | 5 | // this is the result of a script being pulled in from an external domain and CORS.
|
7 | 6 | const DEFAULT_IGNORE_ERRORS = [/^Script error\.?$/, /^Javascript error: Script error\.? on line 0$/];
|
8 | 7 |
|
9 |
| -/** JSDoc */ |
10 |
| -interface InboundFiltersOptions { |
| 8 | +/** Options for the InboundFilters integration */ |
| 9 | +export interface InboundFiltersOptions { |
11 | 10 | allowUrls: Array<string | RegExp>;
|
12 | 11 | denyUrls: Array<string | RegExp>;
|
13 | 12 | ignoreErrors: Array<string | RegExp>;
|
@@ -36,190 +35,171 @@ export class InboundFilters implements Integration {
|
36 | 35 | /**
|
37 | 36 | * @inheritDoc
|
38 | 37 | */
|
39 |
| - public setupOnce(): void { |
| 38 | + public setupOnce(addGlobalEventProcessor: (processor: EventProcessor) => void, getCurrentHub: () => Hub): void { |
40 | 39 | addGlobalEventProcessor((event: Event) => {
|
41 | 40 | const hub = getCurrentHub();
|
42 |
| - if (!hub) { |
43 |
| - return event; |
44 |
| - } |
45 |
| - const self = hub.getIntegration(InboundFilters); |
46 |
| - if (self) { |
47 |
| - const client = hub.getClient(); |
48 |
| - const clientOptions = client ? client.getOptions() : {}; |
49 |
| - // This checks prevents most of the occurrences of the bug linked below: |
50 |
| - // https://github.com/getsentry/sentry-javascript/issues/2622 |
51 |
| - // The bug is caused by multiple SDK instances, where one is minified and one is using non-mangled code. |
52 |
| - // Unfortunatelly we cannot fix it reliably (thus reserved property in rollup's terser config), |
53 |
| - // as we cannot force people using multiple instances in their apps to sync SDK versions. |
54 |
| - const options = typeof self._mergeOptions === 'function' ? self._mergeOptions(clientOptions) : {}; |
55 |
| - if (typeof self._shouldDropEvent !== 'function') { |
56 |
| - return event; |
| 41 | + if (hub) { |
| 42 | + const self = hub.getIntegration(InboundFilters); |
| 43 | + if (self) { |
| 44 | + const client = hub.getClient(); |
| 45 | + const clientOptions = client ? client.getOptions() : {}; |
| 46 | + const options = _mergeOptions(self._options, clientOptions); |
| 47 | + return _shouldDropEvent(event, options) ? null : event; |
57 | 48 | }
|
58 |
| - return self._shouldDropEvent(event, options) ? null : event; |
59 | 49 | }
|
60 | 50 | return event;
|
61 | 51 | });
|
62 | 52 | }
|
| 53 | +} |
63 | 54 |
|
64 |
| - /** JSDoc */ |
65 |
| - private _shouldDropEvent(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
66 |
| - if (this._isSentryError(event, options)) { |
67 |
| - isDebugBuild() && |
68 |
| - logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); |
69 |
| - return true; |
70 |
| - } |
71 |
| - if (this._isIgnoredError(event, options)) { |
72 |
| - isDebugBuild() && |
73 |
| - logger.warn( |
74 |
| - `Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${getEventDescription(event)}`, |
75 |
| - ); |
76 |
| - return true; |
77 |
| - } |
78 |
| - if (this._isDeniedUrl(event, options)) { |
79 |
| - isDebugBuild() && |
80 |
| - logger.warn( |
81 |
| - `Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription( |
82 |
| - event, |
83 |
| - )}.\nUrl: ${this._getEventFilterUrl(event)}`, |
84 |
| - ); |
85 |
| - return true; |
86 |
| - } |
87 |
| - if (!this._isAllowedUrl(event, options)) { |
88 |
| - isDebugBuild() && |
89 |
| - logger.warn( |
90 |
| - `Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription( |
91 |
| - event, |
92 |
| - )}.\nUrl: ${this._getEventFilterUrl(event)}`, |
93 |
| - ); |
94 |
| - return true; |
95 |
| - } |
96 |
| - return false; |
97 |
| - } |
98 |
| - |
99 |
| - /** JSDoc */ |
100 |
| - private _isSentryError(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
101 |
| - if (!options.ignoreInternal) { |
102 |
| - return false; |
103 |
| - } |
| 55 | +/** JSDoc */ |
| 56 | +export function _mergeOptions( |
| 57 | + internalOptions: Partial<InboundFiltersOptions> = {}, |
| 58 | + clientOptions: Partial<InboundFiltersOptions> = {}, |
| 59 | +): Partial<InboundFiltersOptions> { |
| 60 | + return { |
| 61 | + allowUrls: [ |
| 62 | + // eslint-disable-next-line deprecation/deprecation |
| 63 | + ...(internalOptions.whitelistUrls || []), |
| 64 | + ...(internalOptions.allowUrls || []), |
| 65 | + // eslint-disable-next-line deprecation/deprecation |
| 66 | + ...(clientOptions.whitelistUrls || []), |
| 67 | + ...(clientOptions.allowUrls || []), |
| 68 | + ], |
| 69 | + denyUrls: [ |
| 70 | + // eslint-disable-next-line deprecation/deprecation |
| 71 | + ...(internalOptions.blacklistUrls || []), |
| 72 | + ...(internalOptions.denyUrls || []), |
| 73 | + // eslint-disable-next-line deprecation/deprecation |
| 74 | + ...(clientOptions.blacklistUrls || []), |
| 75 | + ...(clientOptions.denyUrls || []), |
| 76 | + ], |
| 77 | + ignoreErrors: [ |
| 78 | + ...(internalOptions.ignoreErrors || []), |
| 79 | + ...(clientOptions.ignoreErrors || []), |
| 80 | + ...DEFAULT_IGNORE_ERRORS, |
| 81 | + ], |
| 82 | + ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true, |
| 83 | + }; |
| 84 | +} |
104 | 85 |
|
105 |
| - try { |
106 |
| - // @ts-ignore can't be a sentry error if undefined |
107 |
| - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
108 |
| - return event.exception.values[0].type === 'SentryError'; |
109 |
| - } catch (e) { |
110 |
| - // ignore |
111 |
| - } |
| 86 | +/** JSDoc */ |
| 87 | +export function _shouldDropEvent(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
| 88 | + if (options.ignoreInternal && _isSentryError(event)) { |
| 89 | + isDebugBuild() && |
| 90 | + logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); |
| 91 | + return true; |
| 92 | + } |
| 93 | + if (_isIgnoredError(event, options.ignoreErrors)) { |
| 94 | + isDebugBuild() && |
| 95 | + logger.warn( |
| 96 | + `Event dropped due to being matched by \`ignoreErrors\` option.\nEvent: ${getEventDescription(event)}`, |
| 97 | + ); |
| 98 | + return true; |
| 99 | + } |
| 100 | + if (_isDeniedUrl(event, options.denyUrls)) { |
| 101 | + isDebugBuild() && |
| 102 | + logger.warn( |
| 103 | + `Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription( |
| 104 | + event, |
| 105 | + )}.\nUrl: ${_getEventFilterUrl(event)}`, |
| 106 | + ); |
| 107 | + return true; |
| 108 | + } |
| 109 | + if (!_isAllowedUrl(event, options.allowUrls)) { |
| 110 | + isDebugBuild() && |
| 111 | + logger.warn( |
| 112 | + `Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription( |
| 113 | + event, |
| 114 | + )}.\nUrl: ${_getEventFilterUrl(event)}`, |
| 115 | + ); |
| 116 | + return true; |
| 117 | + } |
| 118 | + return false; |
| 119 | +} |
112 | 120 |
|
| 121 | +function _isIgnoredError(event: Event, ignoreErrors?: Array<string | RegExp>): boolean { |
| 122 | + if (!ignoreErrors || !ignoreErrors.length) { |
113 | 123 | return false;
|
114 | 124 | }
|
115 | 125 |
|
116 |
| - /** JSDoc */ |
117 |
| - private _isIgnoredError(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
118 |
| - if (!options.ignoreErrors || !options.ignoreErrors.length) { |
119 |
| - return false; |
120 |
| - } |
| 126 | + return _getPossibleEventMessages(event).some(message => |
| 127 | + ignoreErrors.some(pattern => isMatchingPattern(message, pattern)), |
| 128 | + ); |
| 129 | +} |
121 | 130 |
|
122 |
| - return this._getPossibleEventMessages(event).some(message => |
123 |
| - // Not sure why TypeScript complains here... |
124 |
| - (options.ignoreErrors as Array<RegExp | string>).some(pattern => isMatchingPattern(message, pattern)), |
125 |
| - ); |
| 131 | +function _isDeniedUrl(event: Event, denyUrls?: Array<string | RegExp>): boolean { |
| 132 | + // TODO: Use Glob instead? |
| 133 | + if (!denyUrls || !denyUrls.length) { |
| 134 | + return false; |
126 | 135 | }
|
| 136 | + const url = _getEventFilterUrl(event); |
| 137 | + return !url ? false : denyUrls.some(pattern => isMatchingPattern(url, pattern)); |
| 138 | +} |
127 | 139 |
|
128 |
| - /** JSDoc */ |
129 |
| - private _isDeniedUrl(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
130 |
| - // TODO: Use Glob instead? |
131 |
| - if (!options.denyUrls || !options.denyUrls.length) { |
132 |
| - return false; |
133 |
| - } |
134 |
| - const url = this._getEventFilterUrl(event); |
135 |
| - return !url ? false : options.denyUrls.some(pattern => isMatchingPattern(url, pattern)); |
| 140 | +function _isAllowedUrl(event: Event, allowUrls?: Array<string | RegExp>): boolean { |
| 141 | + // TODO: Use Glob instead? |
| 142 | + if (!allowUrls || !allowUrls.length) { |
| 143 | + return true; |
136 | 144 | }
|
| 145 | + const url = _getEventFilterUrl(event); |
| 146 | + return !url ? true : allowUrls.some(pattern => isMatchingPattern(url, pattern)); |
| 147 | +} |
137 | 148 |
|
138 |
| - /** JSDoc */ |
139 |
| - private _isAllowedUrl(event: Event, options: Partial<InboundFiltersOptions>): boolean { |
140 |
| - // TODO: Use Glob instead? |
141 |
| - if (!options.allowUrls || !options.allowUrls.length) { |
142 |
| - return true; |
| 149 | +function _getPossibleEventMessages(event: Event): string[] { |
| 150 | + if (event.message) { |
| 151 | + return [event.message]; |
| 152 | + } |
| 153 | + if (event.exception) { |
| 154 | + try { |
| 155 | + const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; |
| 156 | + return [`${value}`, `${type}: ${value}`]; |
| 157 | + } catch (oO) { |
| 158 | + isDebugBuild() && logger.error(`Cannot extract message for event ${getEventDescription(event)}`); |
| 159 | + return []; |
143 | 160 | }
|
144 |
| - const url = this._getEventFilterUrl(event); |
145 |
| - return !url ? true : options.allowUrls.some(pattern => isMatchingPattern(url, pattern)); |
146 | 161 | }
|
| 162 | + return []; |
| 163 | +} |
147 | 164 |
|
148 |
| - /** JSDoc */ |
149 |
| - private _mergeOptions(clientOptions: Partial<InboundFiltersOptions> = {}): Partial<InboundFiltersOptions> { |
150 |
| - return { |
151 |
| - allowUrls: [ |
152 |
| - // eslint-disable-next-line deprecation/deprecation |
153 |
| - ...(this._options.whitelistUrls || []), |
154 |
| - ...(this._options.allowUrls || []), |
155 |
| - // eslint-disable-next-line deprecation/deprecation |
156 |
| - ...(clientOptions.whitelistUrls || []), |
157 |
| - ...(clientOptions.allowUrls || []), |
158 |
| - ], |
159 |
| - denyUrls: [ |
160 |
| - // eslint-disable-next-line deprecation/deprecation |
161 |
| - ...(this._options.blacklistUrls || []), |
162 |
| - ...(this._options.denyUrls || []), |
163 |
| - // eslint-disable-next-line deprecation/deprecation |
164 |
| - ...(clientOptions.blacklistUrls || []), |
165 |
| - ...(clientOptions.denyUrls || []), |
166 |
| - ], |
167 |
| - ignoreErrors: [ |
168 |
| - ...(this._options.ignoreErrors || []), |
169 |
| - ...(clientOptions.ignoreErrors || []), |
170 |
| - ...DEFAULT_IGNORE_ERRORS, |
171 |
| - ], |
172 |
| - ignoreInternal: typeof this._options.ignoreInternal !== 'undefined' ? this._options.ignoreInternal : true, |
173 |
| - }; |
| 165 | +function _isSentryError(event: Event): boolean { |
| 166 | + try { |
| 167 | + // @ts-ignore can't be a sentry error if undefined |
| 168 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
| 169 | + return event.exception.values[0].type === 'SentryError'; |
| 170 | + } catch (e) { |
| 171 | + // ignore |
174 | 172 | }
|
| 173 | + return false; |
| 174 | +} |
175 | 175 |
|
176 |
| - /** JSDoc */ |
177 |
| - private _getPossibleEventMessages(event: Event): string[] { |
178 |
| - if (event.message) { |
179 |
| - return [event.message]; |
180 |
| - } |
181 |
| - if (event.exception) { |
182 |
| - try { |
183 |
| - const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; |
184 |
| - return [`${value}`, `${type}: ${value}`]; |
185 |
| - } catch (oO) { |
186 |
| - isDebugBuild() && logger.error(`Cannot extract message for event ${getEventDescription(event)}`); |
187 |
| - return []; |
188 |
| - } |
| 176 | +function _getLastValidUrl(frames: StackFrame[] = []): string | null { |
| 177 | + for (let i = frames.length - 1; i >= 0; i--) { |
| 178 | + const frame = frames[i]; |
| 179 | + |
| 180 | + if (frame && frame.filename !== '<anonymous>' && frame.filename !== '[native code]') { |
| 181 | + return frame.filename || null; |
189 | 182 | }
|
190 |
| - return []; |
191 | 183 | }
|
192 | 184 |
|
193 |
| - /** JSDoc */ |
194 |
| - private _getLastValidUrl(frames: StackFrame[] = []): string | null { |
195 |
| - for (let i = frames.length - 1; i >= 0; i--) { |
196 |
| - const frame = frames[i]; |
| 185 | + return null; |
| 186 | +} |
197 | 187 |
|
198 |
| - if (frame && frame.filename !== '<anonymous>' && frame.filename !== '[native code]') { |
199 |
| - return frame.filename || null; |
200 |
| - } |
| 188 | +function _getEventFilterUrl(event: Event): string | null { |
| 189 | + try { |
| 190 | + if (event.stacktrace) { |
| 191 | + return _getLastValidUrl(event.stacktrace.frames); |
201 | 192 | }
|
202 |
| - |
203 |
| - return null; |
204 |
| - } |
205 |
| - |
206 |
| - /** JSDoc */ |
207 |
| - private _getEventFilterUrl(event: Event): string | null { |
| 193 | + let frames; |
208 | 194 | try {
|
209 |
| - if (event.stacktrace) { |
210 |
| - return this._getLastValidUrl(event.stacktrace.frames); |
211 |
| - } |
212 |
| - let frames; |
213 |
| - try { |
214 |
| - // @ts-ignore we only care about frames if the whole thing here is defined |
215 |
| - frames = event.exception.values[0].stacktrace.frames; |
216 |
| - } catch (e) { |
217 |
| - // ignore |
218 |
| - } |
219 |
| - return frames ? this._getLastValidUrl(frames) : null; |
220 |
| - } catch (oO) { |
221 |
| - isDebugBuild() && logger.error(`Cannot extract url for event ${getEventDescription(event)}`); |
222 |
| - return null; |
| 195 | + // @ts-ignore we only care about frames if the whole thing here is defined |
| 196 | + frames = event.exception.values[0].stacktrace.frames; |
| 197 | + } catch (e) { |
| 198 | + // ignore |
223 | 199 | }
|
| 200 | + return frames ? _getLastValidUrl(frames) : null; |
| 201 | + } catch (oO) { |
| 202 | + isDebugBuild() && logger.error(`Cannot extract url for event ${getEventDescription(event)}`); |
| 203 | + return null; |
224 | 204 | }
|
225 | 205 | }
|
0 commit comments