diff --git a/packages/isomorphic/rtti.ts b/packages/isomorphic/rtti.ts index ffef6c1cad4ca..b139d3290fd6e 100644 --- a/packages/isomorphic/rtti.ts +++ b/packages/isomorphic/rtti.ts @@ -20,6 +20,15 @@ export function isRegExp(obj: any): obj is RegExp { return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]'; } +export function isRegexString(value: string): boolean { + try { + new RegExp(value); + return true; + } catch { + return false; + } +} + export function isObject(obj: any): obj is NonNullable { return typeof obj === 'object' && obj !== null; } diff --git a/packages/playwright-core/src/tools/backend/network.ts b/packages/playwright-core/src/tools/backend/network.ts index ce7cd8e959f02..9f33b1540c3c5 100644 --- a/packages/playwright-core/src/tools/backend/network.ts +++ b/packages/playwright-core/src/tools/backend/network.ts @@ -19,6 +19,7 @@ import fs from 'fs'; import * as z from 'zod'; import { getExtensionForMimeType, isTextualMimeType } from '@isomorphic/mimeType'; +import { isRegexString } from '@isomorphic/rtti'; import { defineTool, defineTabTool } from './tool'; @@ -34,7 +35,7 @@ const requests = defineTabTool({ description: 'Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.', inputSchema: z.object({ static: z.boolean().default(false).describe('Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.'), - filter: z.string().optional().describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'), + filter: z.string().optional().refine(v => !v || isRegexString(v), { message: 'Invalid regular expression' }).describe('Only return requests whose URL matches this regexp (e.g. "/api/.*user").'), filename: z.string().optional().describe('Filename to save the network requests to. If not provided, requests are returned as text.'), }), type: 'readOnly', diff --git a/tests/mcp/network.spec.ts b/tests/mcp/network.spec.ts index 7efc31f6f255e..f72045db6ac05 100644 --- a/tests/mcp/network.spec.ts +++ b/tests/mcp/network.spec.ts @@ -94,6 +94,21 @@ test('browser_network_requests filter', async ({ client, server }) => { } }); +test('browser_network_requests rejects invalid regex filter', async ({ client, server }) => { + server.setContent('/', '', 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + const response = await client.callTool({ + name: 'browser_network_requests', + arguments: { filter: '[invalid(' }, + }); + expect(response.isError).toBe(true); +}); + test('browser_network_requests numbers requests with stable indexes', async ({ client, server }) => { server.setContent('/', '', 'text/html');