Skip to content

Commit 288bd87

Browse files
authored
chore(mcp): unguessable mdb url (#37677)
1 parent 1eea72f commit 288bd87

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

packages/playwright/src/mcp/sdk/http.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ export function httpAddressToString(address: string | net.AddressInfo | null): s
5959
return `http://${resolvedHost}:${resolvedPort}`;
6060
}
6161

62-
export async function installHttpTransport(httpServer: http.Server, serverBackendFactory: ServerBackendFactory, allowedHosts?: string[]) {
62+
export async function installHttpTransport(httpServer: http.Server, serverBackendFactory: ServerBackendFactory, unguessableUrl: boolean, allowedHosts?: string[]) {
6363
const url = httpAddressToString(httpServer.address());
6464
const host = new URL(url).host;
6565
allowedHosts = (allowedHosts || [host]).map(h => h.toLowerCase());
6666
const allowAnyHost = allowedHosts.includes('*');
67+
const pathPrefix = unguessableUrl ? `/${crypto.randomUUID()}` : '';
6768

6869
const sseSessions = new Map();
6970
const streamableSessions = new Map();
@@ -83,7 +84,13 @@ export async function installHttpTransport(httpServer: http.Server, serverBacken
8384
}
8485
}
8586

86-
const url = new URL(`http://localhost${req.url}`);
87+
if (!req.url?.startsWith(pathPrefix)) {
88+
res.statusCode = 404;
89+
return res.end('Not found');
90+
}
91+
92+
const path = req.url?.slice(pathPrefix.length);
93+
const url = new URL(`http://localhost${path}`);
8794
if (url.pathname === '/killkillkill' && req.method === 'GET') {
8895
res.statusCode = 200;
8996
res.end('Killing process');
@@ -96,6 +103,8 @@ export async function installHttpTransport(httpServer: http.Server, serverBacken
96103
else
97104
await handleStreamable(serverBackendFactory, req, res, streamableSessions);
98105
});
106+
107+
return `${url}${pathPrefix}`;
99108
}
100109

101110
async function handleSSE(serverBackendFactory: ServerBackendFactory, req: http.IncomingMessage, res: http.ServerResponse, url: URL, sessions: Map<string, SSEServerTransport>) {

packages/playwright/src/mcp/sdk/mdb.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ export async function runOnPauseBackendLoop(backend: mcpServer.ServerBackend, in
167167
};
168168

169169
const httpServer = await mcpHttp.startHttpServer({ port: 0 });
170-
await mcpHttp.installHttpTransport(httpServer, factory);
171-
const url = mcpHttp.httpAddressToString(httpServer.address());
170+
const url = await mcpHttp.installHttpTransport(httpServer, factory, true);
172171

173172
const client = new mcpBundle.Client({ name: 'Pushing client', version: '0.0.0' });
174173
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
@@ -193,8 +192,7 @@ export async function runOnPauseBackendLoop(backend: mcpServer.ServerBackend, in
193192

194193
async function startAsHttp(backendFactory: mcpServer.ServerBackendFactory, options: { port: number }) {
195194
const httpServer = await mcpHttp.startHttpServer(options);
196-
await mcpHttp.installHttpTransport(httpServer, backendFactory);
197-
return mcpHttp.httpAddressToString(httpServer.address());
195+
return await mcpHttp.installHttpTransport(httpServer, backendFactory, true);
198196
}
199197

200198

packages/playwright/src/mcp/sdk/server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { fileURLToPath } from 'url';
1919
import { debug } from 'playwright-core/lib/utilsBundle';
2020

2121
import * as mcpBundle from './bundle';
22-
import { httpAddressToString, installHttpTransport, startHttpServer } from './http';
22+
import { installHttpTransport, startHttpServer } from './http';
2323
import { InProcessTransport } from './inProcessTransport';
2424

2525
import type { Tool, CallToolResult, CallToolRequest, Root } from '@modelcontextprotocol/sdk/types.js';
@@ -166,8 +166,7 @@ export async function start(serverBackendFactory: ServerBackendFactory, options:
166166
}
167167

168168
const httpServer = await startHttpServer(options);
169-
const url = httpAddressToString(httpServer.address());
170-
await installHttpTransport(httpServer, serverBackendFactory, options.allowedHosts);
169+
const url = await installHttpTransport(httpServer, serverBackendFactory, false, options.allowedHosts);
171170

172171
const mcpConfig: any = { mcpServers: { } };
173172
mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = {

tests/mcp/mdb.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,26 @@ test('reset on pause tools', async () => {
156156
await mdbClient.close();
157157
});
158158

159+
test('mdb has unguessable url', async () => {
160+
let firstPathname: string | undefined;
161+
let secondPathname: string | undefined;
162+
{
163+
const { mdbUrl } = await startMDBAndCLI();
164+
firstPathname = new URL(mdbUrl).pathname;
165+
const mdbClient = await createMDBClient(mdbUrl);
166+
await mdbClient.close();
167+
}
168+
{
169+
const { mdbUrl } = await startMDBAndCLI();
170+
secondPathname = new URL(mdbUrl).pathname;
171+
const mdbClient = await createMDBClient(mdbUrl);
172+
await mdbClient.close();
173+
}
174+
expect(firstPathname.length).toBe(37);
175+
expect(secondPathname.length).toBe(37);
176+
expect(firstPathname).not.toBe(secondPathname);
177+
});
178+
159179
async function startMDBAndCLI(): Promise<{ mdbUrl: string, log: string[] }> {
160180
const mdbUrlBox = { mdbUrl: undefined as string | undefined };
161181
const log: string[] = [];

0 commit comments

Comments
 (0)