From 7ac3fbfc18c6d05b98b8e757cb09be3d68f453fb Mon Sep 17 00:00:00 2001 From: Mason Huang Date: Tue, 14 Apr 2026 15:47:51 +0800 Subject: [PATCH] test(browser): cover loopback CDP SSRF seam --- .../chrome.loopback-ssrf.integration.test.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 extensions/browser/src/browser/chrome.loopback-ssrf.integration.test.ts diff --git a/extensions/browser/src/browser/chrome.loopback-ssrf.integration.test.ts b/extensions/browser/src/browser/chrome.loopback-ssrf.integration.test.ts new file mode 100644 index 00000000000..3ad6966959c --- /dev/null +++ b/extensions/browser/src/browser/chrome.loopback-ssrf.integration.test.ts @@ -0,0 +1,70 @@ +import { createServer, type Server } from "node:http"; +import type { AddressInfo } from "node:net"; +import { afterEach, describe, expect, it } from "vitest"; +import { getChromeWebSocketUrl, isChromeReachable } from "./chrome.js"; + +type RunningServer = { + server: Server; + baseUrl: string; +}; + +const runningServers: Server[] = []; + +async function startLoopbackCdpServer(): Promise { + const server = createServer((req, res) => { + if (req.url !== "/json/version") { + res.statusCode = 404; + res.end("not found"); + return; + } + const address = server.address() as AddressInfo; + res.setHeader("content-type", "application/json"); + res.end( + JSON.stringify({ + Browser: "Chrome/999.0.0.0", + webSocketDebuggerUrl: `ws://127.0.0.1:${address.port}/devtools/browser/TEST`, + }), + ); + }); + + await new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", () => resolve()); + }); + + runningServers.push(server); + const address = server.address() as AddressInfo; + return { + server, + baseUrl: `http://127.0.0.1:${address.port}`, + }; +} + +afterEach(async () => { + await Promise.all( + runningServers + .splice(0) + .map( + (server) => + new Promise((resolve, reject) => + server.close((err) => (err ? reject(err) : resolve())), + ), + ), + ); +}); + +describe("chrome loopback SSRF integration", () => { + it("keeps loopback CDP HTTP reachability working under strict default SSRF policy", async () => { + const { baseUrl } = await startLoopbackCdpServer(); + + await expect(isChromeReachable(baseUrl, 500, {})).resolves.toBe(true); + }); + + it("returns the loopback websocket URL under strict default SSRF policy", async () => { + const { baseUrl } = await startLoopbackCdpServer(); + + await expect(getChromeWebSocketUrl(baseUrl, 500, {})).resolves.toMatch( + /\/devtools\/browser\/TEST$/, + ); + }); +});