@@ -6,11 +6,13 @@ import os from "node:os";
66import path from "node:path" ;
77import { afterAll , afterEach , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
88import { WebSocketServer } from "ws" ;
9+ import { SsrFBlockedError } from "../infra/net/ssrf.js" ;
910import {
1011 decorateOpenClawProfile ,
1112 ensureProfileCleanExit ,
1213 findChromeExecutableMac ,
1314 findChromeExecutableWindows ,
15+ getChromeWebSocketUrl ,
1416 isChromeCdpReady ,
1517 isChromeReachable ,
1618 resolveBrowserExecutableForPlatform ,
@@ -328,6 +330,39 @@ describe("browser chrome helpers", () => {
328330 expect ( fetchSpy ) . not . toHaveBeenCalled ( ) ;
329331 } ) ;
330332
333+ it ( "blocks cross-host websocket pivots returned by /json/version in strict SSRF mode" , async ( ) => {
334+ const server = createServer ( ( req , res ) => {
335+ if ( req . url === "/json/version" ) {
336+ res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
337+ res . end (
338+ JSON . stringify ( {
339+ webSocketDebuggerUrl : "ws://169.254.169.254:9222/devtools/browser/pivot" ,
340+ } ) ,
341+ ) ;
342+ return ;
343+ }
344+ res . writeHead ( 404 ) ;
345+ res . end ( ) ;
346+ } ) ;
347+
348+ await new Promise < void > ( ( resolve , reject ) => {
349+ server . listen ( 0 , "127.0.0.1" , ( ) => resolve ( ) ) ;
350+ server . once ( "error" , reject ) ;
351+ } ) ;
352+
353+ try {
354+ const addr = server . address ( ) as AddressInfo ;
355+ await expect (
356+ getChromeWebSocketUrl ( `http://127.0.0.1:${ addr . port } ` , 50 , {
357+ dangerouslyAllowPrivateNetwork : false ,
358+ allowedHostnames : [ "127.0.0.1" ] ,
359+ } ) ,
360+ ) . rejects . toBeInstanceOf ( SsrFBlockedError ) ;
361+ } finally {
362+ await new Promise < void > ( ( resolve ) => server . close ( ( ) => resolve ( ) ) ) ;
363+ }
364+ } ) ;
365+
331366 it ( "reports cdpReady only when Browser.getVersion command succeeds" , async ( ) => {
332367 await withMockChromeCdpServer ( {
333368 wsPath : "/devtools/browser/health" ,
0 commit comments