Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 76b7f3a

Browse files
authored
feat(browser): Disable client when browser extension is detected in init() (#16354)
Similar to #16353, this changes how default options for BrowserClient are handled. Instead of building this in `init`, we now do (some) of it in BrowserClient. Additionally, this also adjusts what we do if we detect a browser extension: Instead of skipping the setup, we now just disable the client. This streamlines this a bit and also ensures that we actually always return a `Client` from init. It also fixes the type for `BrowserOption` to actually allow to configure `cdnBaseUrl`, which was actually only set for the ClientOptions, oops. The reason for this is to streamline the `init` code, making it easier to extend/adjust it. Right now, there is a lot going on in different places there. By moving as much as we can (and makes sense) to `BrowserClient` this becomes a bit easier. While playing with different ways to handle the browser extension stuff, I ended up landing on just disabling the SDK in this scenario.
1 parent 349d7d0 commit 76b7f3a

File tree

7 files changed

+192
-232
lines changed

7 files changed

+192
-232
lines changed

‎dev-packages/browser-integration-tests/suites/manual-client/skip-init-browser-extension/test.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ sentryTest(
1818
return !!(window as any).Sentry.isInitialized();
1919
});
2020

21-
expect(isInitialized).toEqual(false);
21+
const isEnabled = await page.evaluate(() => {
22+
return !!(window as any).Sentry.getClient()?.getOptions().enabled;
23+
});
24+
25+
expect(isInitialized).toEqual(true);
26+
expect(isEnabled).toEqual(false);
2227

2328
if (hasDebugLogs()) {
2429
expect(errorLogs.length).toEqual(1);

‎dev-packages/browser-integration-tests/suites/manual-client/skip-init-chrome-extension/test.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ sentryTest('should not initialize when inside a Chrome browser extension', async
1616
return !!(window as any).Sentry.isInitialized();
1717
});
1818

19-
expect(isInitialized).toEqual(false);
19+
const isEnabled = await page.evaluate(() => {
20+
return !!(window as any).Sentry.getClient()?.getOptions().enabled;
21+
});
22+
23+
expect(isInitialized).toEqual(true);
24+
expect(isEnabled).toEqual(false);
2025

2126
if (hasDebugLogs()) {
2227
expect(errorLogs.length).toEqual(1);

‎packages/browser/src/client.ts‎

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,24 @@ import { eventFromException, eventFromMessage } from './eventbuilder';
2121
import { WINDOW } from './helpers';
2222
import type { BrowserTransportOptions } from './transports/types';
2323

24+
/**
25+
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
26+
*/
27+
declare const __SENTRY_RELEASE__: string | undefined;
28+
2429
const DEFAULT_FLUSH_INTERVAL = 5000;
2530

31+
type BrowserSpecificOptions = BrowserClientReplayOptions &
32+
BrowserClientProfilingOptions & {
33+
/** If configured, this URL will be used as base URL for lazy loading integration. */
34+
cdnBaseUrl?: string;
35+
};
2636
/**
2737
* Configuration options for the Sentry Browser SDK.
2838
* @see @sentry/core Options for more information.
2939
*/
3040
export type BrowserOptions = Options<BrowserTransportOptions> &
31-
BrowserClientReplayOptions &
32-
BrowserClientProfilingOptions & {
41+
BrowserSpecificOptions & {
3342
/**
3443
* Important: Only set this option if you know what you are doing!
3544
*
@@ -54,12 +63,7 @@ export type BrowserOptions = Options<BrowserTransportOptions> &
5463
* Configuration options for the Sentry Browser SDK Client class
5564
* @see BrowserClient for more information.
5665
*/
57-
export type BrowserClientOptions = ClientOptions<BrowserTransportOptions> &
58-
BrowserClientReplayOptions &
59-
BrowserClientProfilingOptions & {
60-
/** If configured, this URL will be used as base URL for lazy loading integration. */
61-
cdnBaseUrl?: string;
62-
};
66+
export type BrowserClientOptions = ClientOptions<BrowserTransportOptions> & BrowserSpecificOptions;
6367

6468
/**
6569
* The Sentry Browser SDK Client.
@@ -75,11 +79,7 @@ export class BrowserClient extends Client<BrowserClientOptions> {
7579
* @param options Configuration options for this SDK.
7680
*/
7781
public constructor(options: BrowserClientOptions) {
78-
const opts = {
79-
// We default this to true, as it is the safer scenario
80-
parentSpanIsAlwaysRootSpan: true,
81-
...options,
82-
};
82+
const opts = applyDefaultOptions(options);
8383
const sdkSource = WINDOW.SENTRY_SDK_SOURCE || getSDKSource();
8484
applySdkMetadata(opts, 'browser', ['browser'], sdkSource);
8585

@@ -155,3 +155,17 @@ export class BrowserClient extends Client<BrowserClientOptions> {
155155
return super._prepareEvent(event, hint, currentScope, isolationScope);
156156
}
157157
}
158+
159+
/** Exported only for tests. */
160+
export function applyDefaultOptions<T extends Partial<BrowserClientOptions>>(optionsArg: T): T {
161+
return {
162+
release:
163+
typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value
164+
? __SENTRY_RELEASE__
165+
: WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects
166+
sendClientReports: true,
167+
// We default this to true, as it is the safer scenario
168+
parentSpanIsAlwaysRootSpan: true,
169+
...optionsArg,
170+
};
171+
}

‎packages/browser/src/sdk.ts‎

Lines changed: 10 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
import type { Client, Integration, Options } from '@sentry/core';
22
import {
3-
consoleSandbox,
43
dedupeIntegration,
54
functionToStringIntegration,
65
getIntegrationsToSetup,
7-
getLocationHref,
86
inboundFiltersIntegration,
97
initAndBind,
108
stackParserFromStackParserOptions,
119
} from '@sentry/core';
1210
import type { BrowserClientOptions, BrowserOptions } from './client';
1311
import { BrowserClient } from './client';
14-
import { DEBUG_BUILD } from './debug-build';
15-
import { WINDOW } from './helpers';
1612
import { breadcrumbsIntegration } from './integrations/breadcrumbs';
1713
import { browserApiErrorsIntegration } from './integrations/browserapierrors';
1814
import { browserSessionIntegration } from './integrations/browsersession';
@@ -21,22 +17,7 @@ import { httpContextIntegration } from './integrations/httpcontext';
2117
import { linkedErrorsIntegration } from './integrations/linkederrors';
2218
import { defaultStackParser } from './stack-parsers';
2319
import { makeFetchTransport } from './transports/fetch';
24-
25-
type ExtensionProperties = {
26-
chrome?: Runtime;
27-
browser?: Runtime;
28-
nw?: unknown;
29-
};
30-
type Runtime = {
31-
runtime?: {
32-
id?: string;
33-
};
34-
};
35-
36-
/**
37-
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
38-
*/
39-
declare const __SENTRY_RELEASE__: string | undefined;
20+
import { checkAndWarnIfIsEmbeddedBrowserExtension } from './utils/detectBrowserExtension';
4021

4122
/** Get the default integrations for the browser SDK. */
4223
export function getDefaultIntegrations(_options: Options): Integration[] {
@@ -59,40 +40,6 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
5940
];
6041
}
6142

62-
/** Exported only for tests. */
63-
export function applyDefaultOptions(optionsArg: BrowserOptions = {}): BrowserOptions {
64-
const defaultOptions: BrowserOptions = {
65-
defaultIntegrations: getDefaultIntegrations(optionsArg),
66-
release:
67-
typeof __SENTRY_RELEASE__ === 'string' // This allows build tooling to find-and-replace __SENTRY_RELEASE__ to inject a release value
68-
? __SENTRY_RELEASE__
69-
: WINDOW.SENTRY_RELEASE?.id, // This supports the variable that sentry-webpack-plugin injects
70-
sendClientReports: true,
71-
};
72-
73-
return {
74-
...defaultOptions,
75-
...dropTopLevelUndefinedKeys(optionsArg),
76-
};
77-
}
78-
79-
/**
80-
* In contrast to the regular `dropUndefinedKeys` method,
81-
* this one does not deep-drop keys, but only on the top level.
82-
*/
83-
function dropTopLevelUndefinedKeys<T extends object>(obj: T): Partial<T> {
84-
const mutatetedObj: Partial<T> = {};
85-
86-
for (const k of Object.getOwnPropertyNames(obj)) {
87-
const key = k as keyof T;
88-
if (obj[key] !== undefined) {
89-
mutatetedObj[key] = obj[key];
90-
}
91-
}
92-
93-
return mutatetedObj;
94-
}
95-
9643
/**
9744
* The Sentry Browser SDK Client.
9845
*
@@ -139,19 +86,21 @@ function dropTopLevelUndefinedKeys<T extends object>(obj: T): Partial<T> {
13986
*
14087
* @see {@link BrowserOptions} for documentation on configuration options.
14188
*/
142-
export function init(browserOptions: BrowserOptions = {}): Client | undefined {
143-
if (!browserOptions.skipBrowserExtensionCheck && _checkForBrowserExtension()) {
144-
return;
145-
}
89+
export function init(options: BrowserOptions = {}): Client | undefined {
90+
const shouldDisableBecauseIsBrowserExtenstion =
91+
!options.skipBrowserExtensionCheck && checkAndWarnIfIsEmbeddedBrowserExtension();
14692

147-
const options = applyDefaultOptions(browserOptions);
14893
const clientOptions: BrowserClientOptions = {
14994
...options,
95+
enabled: shouldDisableBecauseIsBrowserExtenstion ? false : options.enabled,
15096
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
151-
integrations: getIntegrationsToSetup(options),
97+
integrations: getIntegrationsToSetup({
98+
integrations: options.integrations,
99+
defaultIntegrations:
100+
options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations,
101+
}),
152102
transport: options.transport || makeFetchTransport,
153103
};
154-
155104
return initAndBind(BrowserClient, clientOptions);
156105
}
157106

@@ -170,48 +119,3 @@ export function forceLoad(): void {
170119
export function onLoad(callback: () => void): void {
171120
callback();
172121
}
173-
174-
function _isEmbeddedBrowserExtension(): boolean {
175-
if (typeof WINDOW.window === 'undefined') {
176-
// No need to show the error if we're not in a browser window environment (e.g. service workers)
177-
return false;
178-
}
179-
180-
const _window = WINDOW as typeof WINDOW & ExtensionProperties;
181-
182-
// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
183-
// see: https://github.com/getsentry/sentry-javascript/issues/12668
184-
if (_window.nw) {
185-
return false;
186-
}
187-
188-
const extensionObject = _window['chrome'] || _window['browser'];
189-
190-
if (!extensionObject?.runtime?.id) {
191-
return false;
192-
}
193-
194-
const href = getLocationHref();
195-
const extensionProtocols = ['chrome-extension', 'moz-extension', 'ms-browser-extension', 'safari-web-extension'];
196-
197-
// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
198-
const isDedicatedExtensionPage =
199-
WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}://`));
200-
201-
return !isDedicatedExtensionPage;
202-
}
203-
204-
function _checkForBrowserExtension(): true | void {
205-
if (_isEmbeddedBrowserExtension()) {
206-
if (DEBUG_BUILD) {
207-
consoleSandbox(() => {
208-
// eslint-disable-next-line no-console
209-
console.error(
210-
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
211-
);
212-
});
213-
}
214-
215-
return true;
216-
}
217-
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { consoleSandbox, getLocationHref } from '@sentry/core';
2+
import { DEBUG_BUILD } from '../debug-build';
3+
import { WINDOW } from '../helpers';
4+
5+
type ExtensionRuntime = {
6+
runtime?: {
7+
id?: string;
8+
};
9+
};
10+
type ExtensionProperties = {
11+
chrome?: ExtensionRuntime;
12+
browser?: ExtensionRuntime;
13+
nw?: unknown;
14+
};
15+
16+
/**
17+
* Returns true if the SDK is running in an embedded browser extension.
18+
* Stand-alone browser extensions (which do not share the same data as the main browser page) are fine.
19+
*/
20+
export function checkAndWarnIfIsEmbeddedBrowserExtension(): boolean {
21+
if (_isEmbeddedBrowserExtension()) {
22+
if (DEBUG_BUILD) {
23+
consoleSandbox(() => {
24+
// eslint-disable-next-line no-console
25+
console.error(
26+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
27+
);
28+
});
29+
}
30+
31+
return true;
32+
}
33+
34+
return false;
35+
}
36+
37+
function _isEmbeddedBrowserExtension(): boolean {
38+
if (typeof WINDOW.window === 'undefined') {
39+
// No need to show the error if we're not in a browser window environment (e.g. service workers)
40+
return false;
41+
}
42+
43+
const _window = WINDOW as typeof WINDOW & ExtensionProperties;
44+
45+
// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
46+
// see: https://github.com/getsentry/sentry-javascript/issues/12668
47+
if (_window.nw) {
48+
return false;
49+
}
50+
51+
const extensionObject = _window['chrome'] || _window['browser'];
52+
53+
if (!extensionObject?.runtime?.id) {
54+
return false;
55+
}
56+
57+
const href = getLocationHref();
58+
const extensionProtocols = ['chrome-extension', 'moz-extension', 'ms-browser-extension', 'safari-web-extension'];
59+
60+
// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
61+
const isDedicatedExtensionPage =
62+
WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}://`));
63+
64+
return !isDedicatedExtensionPage;
65+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /