Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions packages/browser/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,32 @@ function applyDefaultOptions(optionsArg: BrowserOptions = {}): BrowserOptions {
return { ...defaultOptions, ...optionsArg };
}

type ExtensionProperties = {
chrome?: Runtime;
browser?: Runtime;
};
type Runtime = {
runtime?: {
id?: string;
};
};

function shouldShowBrowserExtensionError(): boolean {
const windowWithMaybeChrome = WINDOW as typeof WINDOW & { chrome?: { runtime?: { id?: string } } };
const isInsideChromeExtension =
windowWithMaybeChrome &&
windowWithMaybeChrome.chrome &&
windowWithMaybeChrome.chrome.runtime &&
windowWithMaybeChrome.chrome.runtime.id;

const windowWithMaybeBrowser = WINDOW as typeof WINDOW & { browser?: { runtime?: { id?: string } } };
const isInsideBrowserExtension =
windowWithMaybeBrowser &&
windowWithMaybeBrowser.browser &&
windowWithMaybeBrowser.browser.runtime &&
windowWithMaybeBrowser.browser.runtime.id;

return !!isInsideBrowserExtension || !!isInsideChromeExtension;
const windowWithMaybeExtension = WINDOW as typeof WINDOW & ExtensionProperties;

const extensionKey = windowWithMaybeExtension.chrome ? 'chrome' : 'browser';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify, when would you have window.browser.xxx, and would that mean this is an extension??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we introduced this in the original browser extension check (https://github.com/getsentry/sentry-javascript/pull/10844/files)

const extensionObject = windowWithMaybeExtension[extensionKey];

const runtimeId = extensionObject && extensionObject.runtime && extensionObject.runtime.id;
const href = (WINDOW.location && WINDOW.location.href) || '';

const extensionProtocols = ['chrome-extension:', 'moz-extension:', 'ms-browser-extension:'];

// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
const isDedicatedExtensionPage =
!!runtimeId && WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}//`));

return !!runtimeId && !isDedicatedExtensionPage;
}

/**
Expand Down
31 changes: 28 additions & 3 deletions packages/browser/test/unit/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,16 @@ describe('init', () => {
new MockIntegration('MockIntegration 0.2'),
];

const originalLocation = WINDOW.location || {};

const options = getDefaultBrowserOptions({ dsn: PUBLIC_DSN, defaultIntegrations: DEFAULT_INTEGRATIONS });

afterEach(() => {
Object.defineProperty(WINDOW, 'chrome', { value: undefined, writable: true });
Object.defineProperty(WINDOW, 'browser', { value: undefined, writable: true });
});

it('should log a browser extension error if executed inside a Chrome extension', () => {
it('logs a browser extension error if executed inside a Chrome extension', () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

Object.defineProperty(WINDOW, 'chrome', {
Expand All @@ -160,7 +162,7 @@ describe('init', () => {
consoleErrorSpy.mockRestore();
});

it('should log a browser extension error if executed inside a Firefox/Safari extension', () => {
it('logs a browser extension error if executed inside a Firefox/Safari extension', () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

Object.defineProperty(WINDOW, 'browser', { value: { runtime: { id: 'mock-extension-id' } }, writable: true });
Expand All @@ -175,7 +177,30 @@ describe('init', () => {
consoleErrorSpy.mockRestore();
});

it('should not log a browser extension error if executed inside regular browser environment', () => {
it.each(['chrome-extension', 'moz-extension', 'ms-browser-extension'])(
"doesn't log a browser extension error if executed inside an extension running in a dedicated page (%s)",
extensionProtocol => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

// @ts-expect-error - this is a hack to simulate a dedicated page in a browser extension
delete WINDOW.location;
// @ts-expect-error - this is a hack to simulate a dedicated page in a browser extension
WINDOW.location = {
href: `${extensionProtocol}://mock-extension-id/dedicated-page.html`,
};

Object.defineProperty(WINDOW, 'browser', { value: { runtime: { id: 'mock-extension-id' } }, writable: true });

init(options);

expect(consoleErrorSpy).toBeCalledTimes(0);

consoleErrorSpy.mockRestore();
WINDOW.location = originalLocation;
},
);

it("doesn't log a browser extension error if executed inside regular browser environment", () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

init(options);
Expand Down