Skip to content

Commit bfe6dd3

Browse files
krissdingclaude
andcommitted
fix(browser): return JSON-RPC errors for malformed CDP client frames in relay bridge
Silently dropping malformed JSON or invalid CDP request objects leaves automation clients waiting for their own timeout. Return standard JSON-RPC parse error (-32700) and invalid-request error (-32600) at the CDP client socket boundary, matching the precedent in gateway MCP HTTP. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent fc4626c commit bfe6dd3

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

extensions/browser/src/browser/extension-relay/relay-bridge.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,44 @@ describe("ExtensionRelayBridge", () => {
368368
expect(socket.closed).toBe(true);
369369
expect(bridge.extensionConnected).toBe(false);
370370
});
371+
372+
it("responds with JSON-RPC parse error on malformed JSON from CDP client", () => {
373+
const bridge = new ExtensionRelayBridge();
374+
const client = new FakeSocket();
375+
const cdp = bridge.attachCdpClientSocket(client);
376+
cdp.onMessage("{");
377+
const frames = client.frames();
378+
expect(frames).toHaveLength(1);
379+
expect(frames[0]).toMatchObject({
380+
id: null,
381+
error: { code: -32700, message: "Parse error" },
382+
});
383+
});
384+
385+
it("responds with JSON-RPC invalid request error when CDP client frame lacks id or method", () => {
386+
const bridge = new ExtensionRelayBridge();
387+
const client = new FakeSocket();
388+
const cdp = bridge.attachCdpClientSocket(client);
389+
// Missing id and method
390+
cdp.onMessage(JSON.stringify({ params: {} }));
391+
const frames = client.frames();
392+
expect(frames).toHaveLength(1);
393+
expect(frames[0]).toMatchObject({
394+
id: null,
395+
error: { code: -32600, message: "Invalid Request" },
396+
});
397+
});
398+
399+
it("responds with invalid request error when CDP client frame has id but no method", () => {
400+
const bridge = new ExtensionRelayBridge();
401+
const client = new FakeSocket();
402+
const cdp = bridge.attachCdpClientSocket(client);
403+
cdp.onMessage(JSON.stringify({ id: 7 }));
404+
const frames = client.frames();
405+
expect(frames).toHaveLength(1);
406+
expect(frames[0]).toMatchObject({
407+
id: 7,
408+
error: { code: -32600, message: "Invalid Request" },
409+
});
410+
});
371411
});

extensions/browser/src/browser/extension-relay/relay-bridge.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@ type ExtensionIdentity = {
7474
extensionVersion: string;
7575
};
7676

77-
function toErrorPayload(id: number, sessionId: string | undefined, message: string, code = -32000) {
77+
function toErrorPayload(
78+
id: number | null,
79+
sessionId: string | undefined,
80+
message: string,
81+
code = -32000,
82+
) {
7883
return JSON.stringify({ id, ...(sessionId ? { sessionId } : {}), error: { code, message } });
7984
}
8085

@@ -498,9 +503,18 @@ export class ExtensionRelayBridge {
498503
try {
499504
request = JSON.parse(raw) as CdpRequest;
500505
} catch {
506+
client.socket.send(toErrorPayload(null, undefined, "Parse error", -32700));
501507
return;
502508
}
503509
if (typeof request?.id !== "number" || typeof request?.method !== "string") {
510+
client.socket.send(
511+
toErrorPayload(
512+
typeof request?.id === "number" ? request.id : null,
513+
undefined,
514+
"Invalid Request",
515+
-32600,
516+
),
517+
);
504518
return;
505519
}
506520
void this.handleCdpRequest(client, request);

0 commit comments

Comments
 (0)