|
1 | 1 | import { createServer } from "node:http"; |
2 | | -import { afterEach, describe, expect, it } from "vitest"; |
| 2 | +import { afterEach, describe, expect, it, vi } from "vitest"; |
3 | 3 | import { type WebSocket, WebSocketServer } from "ws"; |
| 4 | +import { SsrFBlockedError } from "../infra/net/ssrf.js"; |
4 | 5 | import { rawDataToString } from "../infra/ws.js"; |
5 | 6 | import { createTargetViaCdp, evaluateJavaScript, normalizeCdpWsUrl, snapshotAria } from "./cdp.js"; |
6 | 7 |
|
@@ -92,6 +93,61 @@ describe("cdp", () => { |
92 | 93 | expect(created.targetId).toBe("TARGET_123"); |
93 | 94 | }); |
94 | 95 |
|
| 96 | + it("blocks private navigation targets by default", async () => { |
| 97 | + const fetchSpy = vi.spyOn(globalThis, "fetch"); |
| 98 | + try { |
| 99 | + await expect( |
| 100 | + createTargetViaCdp({ |
| 101 | + cdpUrl: "http://127.0.0.1:9222", |
| 102 | + url: "http://127.0.0.1:8080", |
| 103 | + }), |
| 104 | + ).rejects.toBeInstanceOf(SsrFBlockedError); |
| 105 | + expect(fetchSpy).not.toHaveBeenCalled(); |
| 106 | + } finally { |
| 107 | + fetchSpy.mockRestore(); |
| 108 | + } |
| 109 | + }); |
| 110 | + |
| 111 | + it("allows private navigation targets when explicitly configured", async () => { |
| 112 | + const wsPort = await startWsServerWithMessages((msg, socket) => { |
| 113 | + if (msg.method !== "Target.createTarget") { |
| 114 | + return; |
| 115 | + } |
| 116 | + expect(msg.params?.url).toBe("http://127.0.0.1:8080"); |
| 117 | + socket.send( |
| 118 | + JSON.stringify({ |
| 119 | + id: msg.id, |
| 120 | + result: { targetId: "TARGET_LOCAL" }, |
| 121 | + }), |
| 122 | + ); |
| 123 | + }); |
| 124 | + |
| 125 | + httpServer = createServer((req, res) => { |
| 126 | + if (req.url === "/json/version") { |
| 127 | + res.setHeader("content-type", "application/json"); |
| 128 | + res.end( |
| 129 | + JSON.stringify({ |
| 130 | + webSocketDebuggerUrl: `ws://127.0.0.1:${wsPort}/devtools/browser/TEST`, |
| 131 | + }), |
| 132 | + ); |
| 133 | + return; |
| 134 | + } |
| 135 | + res.statusCode = 404; |
| 136 | + res.end("not found"); |
| 137 | + }); |
| 138 | + |
| 139 | + await new Promise<void>((resolve) => httpServer?.listen(0, "127.0.0.1", resolve)); |
| 140 | + const httpPort = (httpServer.address() as { port: number }).port; |
| 141 | + |
| 142 | + const created = await createTargetViaCdp({ |
| 143 | + cdpUrl: `http://127.0.0.1:${httpPort}`, |
| 144 | + url: "http://127.0.0.1:8080", |
| 145 | + ssrfPolicy: { allowPrivateNetwork: true }, |
| 146 | + }); |
| 147 | + |
| 148 | + expect(created.targetId).toBe("TARGET_LOCAL"); |
| 149 | + }); |
| 150 | + |
95 | 151 | it("evaluates javascript via CDP", async () => { |
96 | 152 | const wsPort = await startWsServerWithMessages((msg, socket) => { |
97 | 153 | if (msg.method === "Runtime.enable") { |
|
0 commit comments