From f41a1545a623080a179aa089cdff17d4fbafba25 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 10 Mar 2026 12:53:20 -0700 Subject: [PATCH] fix(mcp): use protocol blocklist instead of allowlist for URL checks Switch from allowlist (_allowedProtocols) to blocklist (_disallowedProtocols) so that chrome-extension:// and other non-file protocols are no longer blocked by default. Fixes: https://github.com/microsoft/playwright/issues/39569 --- .../playwright-core/src/client/browserContext.ts | 12 ++++++------ packages/playwright-core/src/tools/context.ts | 2 +- tests/mcp/core.spec.ts | 2 +- tests/mcp/run-code.spec.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 5aa032103618f..99c006eb819d3 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -79,7 +79,7 @@ export class BrowserContext extends ChannelOwner private _closeReason: string | undefined; private _harRouters: HarRouter[] = []; private _onRecorderEventSink: RecorderEventSink | undefined; - private _allowedProtocols: string[] | undefined; + private _disallowedProtocols: string[] | undefined; private _allowedDirectories: string[] | undefined; static from(context: channels.BrowserContextChannel): BrowserContext { @@ -555,12 +555,12 @@ export class BrowserContext extends ChannelOwner await this._channel.exposeConsoleApi(); } - _setAllowedProtocols(protocols: string[]) { - this._allowedProtocols = protocols; + _setDisallowedProtocols(protocols: string[]) { + this._disallowedProtocols = protocols; } _checkUrlAllowed(url: string) { - if (!this._allowedProtocols) + if (!this._disallowedProtocols) return; let parsedURL; try { @@ -568,8 +568,8 @@ export class BrowserContext extends ChannelOwner } catch (e) { throw new Error(`Access to ${url} is blocked. Invalid URL: ${e.message}`); } - if (!this._allowedProtocols.includes(parsedURL.protocol)) - throw new Error(`Access to "${parsedURL.protocol}" URL is blocked. Allowed protocols: ${this._allowedProtocols.join(', ')}. Attempted URL: ${url}`); + if (this._disallowedProtocols.includes(parsedURL.protocol)) + throw new Error(`Access to "${parsedURL.protocol}" protocol is blocked. Attempted URL: "${url}"`); } _setAllowedDirectories(rootDirectories: string[]) { diff --git a/packages/playwright-core/src/tools/context.ts b/packages/playwright-core/src/tools/context.ts index 992d7f7ecef53..730d252b658ec 100644 --- a/packages/playwright-core/src/tools/context.ts +++ b/packages/playwright-core/src/tools/context.ts @@ -287,7 +287,7 @@ export class Context { selectors.setTestIdAttribute(this.config.testIdAttribute); const browserContext = this._rawBrowserContext; if (!this.config.allowUnrestrictedFileAccess) { - (browserContext as any)._setAllowedProtocols(['http:', 'https:', 'about:', 'data:']); + (browserContext as any)._setDisallowedProtocols(['file:']); (browserContext as any)._setAllowedDirectories([this.options.cwd]); } await this._setupRequestInterception(browserContext); diff --git a/tests/mcp/core.spec.ts b/tests/mcp/core.spec.ts index 87c7ed97721b8..731ac32ed2406 100644 --- a/tests/mcp/core.spec.ts +++ b/tests/mcp/core.spec.ts @@ -37,7 +37,7 @@ test('browser_navigate blocks file:// URLs by default', async ({ client }) => { name: 'browser_navigate', arguments: { url: 'file:///etc/passwd' }, })).toHaveResponse({ - error: expect.stringContaining('Error: Access to "file:" URL is blocked. Allowed protocols: http:, https:, about:, data:. Attempted URL: file:///etc/passwd'), + error: expect.stringContaining('Error: Access to "file:" protocol is blocked. Attempted URL: "file:///etc/passwd"'), isError: true, }); }); diff --git a/tests/mcp/run-code.spec.ts b/tests/mcp/run-code.spec.ts index bc673f2d888a5..b9ab561e9b51e 100644 --- a/tests/mcp/run-code.spec.ts +++ b/tests/mcp/run-code.spec.ts @@ -90,7 +90,7 @@ test('browser_run_code blocks fetch of file:// URLs by default', async ({ client code: `async (page) => { await page.request.get('file:///etc/passwd'); }`, }, })).toHaveResponse({ - error: expect.stringContaining('Error: apiRequestContext.get: Access to "file:" URL is blocked. Allowed protocols: http:, https:, about:, data:. Attempted URL: file:///etc/passwd'), + error: expect.stringContaining('Error: apiRequestContext.get: Access to "file:" protocol is blocked. Attempted URL: "file:///etc/passwd"'), isError: true, }); });