11// Codex tests cover sandbox exec server.http plugin behavior.
2+ import { spawn } from "node:child_process" ;
23import { afterEach , describe , expect , it , vi } from "vitest" ;
34import {
45 closeCodexSandboxExecServersForTests ,
@@ -13,7 +14,10 @@ import {
1314 rpc ,
1415 waitForHttpBodyDeltas ,
1516} from "./sandbox-exec-server.test-helpers.js" ;
16- import { SANDBOX_HTTP_STREAM_LINE_MAX_CHARS } from "./sandbox-exec-server/http.js" ;
17+ import {
18+ SANDBOX_HTTP_REQUEST_SCRIPT ,
19+ SANDBOX_HTTP_STREAM_LINE_MAX_CHARS ,
20+ } from "./sandbox-exec-server/http.js" ;
1721
1822afterEach ( async ( ) => {
1923 vi . unstubAllEnvs ( ) ;
@@ -26,6 +30,32 @@ function testExecEnv(): NodeJS.ProcessEnv {
2630 } ;
2731}
2832
33+ function runSandboxHttpRequestScript ( input : unknown ) : Promise < {
34+ code : number | null ;
35+ stderr : string ;
36+ stdout : string ;
37+ } > {
38+ return new Promise ( ( resolve , reject ) => {
39+ const child = spawn ( "bash" , [ "-lc" , SANDBOX_HTTP_REQUEST_SCRIPT ] , {
40+ env : testExecEnv ( ) ,
41+ stdio : [ "pipe" , "pipe" , "pipe" ] ,
42+ } ) ;
43+ let stdout = "" ;
44+ let stderr = "" ;
45+ child . stdout . on ( "data" , ( chunk : Buffer ) => {
46+ stdout += chunk . toString ( "utf8" ) ;
47+ } ) ;
48+ child . stderr . on ( "data" , ( chunk : Buffer ) => {
49+ stderr += chunk . toString ( "utf8" ) ;
50+ } ) ;
51+ child . once ( "error" , reject ) ;
52+ child . once ( "close" , ( code ) => {
53+ resolve ( { code, stderr, stdout } ) ;
54+ } ) ;
55+ child . stdin . end ( JSON . stringify ( input ) ) ;
56+ } ) ;
57+ }
58+
2959describe ( "OpenClaw Codex sandbox exec-server HTTP" , ( ) => {
3060 it ( "routes HTTP requests through the sandbox backend" , async ( ) => {
3161 const runShellCommand = vi . fn ( async ( ) => ( {
@@ -126,6 +156,30 @@ describe("OpenClaw Codex sandbox exec-server HTTP", () => {
126156 socket . close ( ) ;
127157 } ) ;
128158
159+ it ( "blocks protected IP classes inside the sandbox Python helper" , async ( ) => {
160+ const blockedUrls = [
161+ "http://100.100.100.200/" ,
162+ "http://[fd00:ec2::254]/" ,
163+ "http://[fec0::1]/" ,
164+ "http://[64:ff9b::100.100.100.200]/" ,
165+ "http://[64:ff9b:1::6464:64c8]/" ,
166+ "http://[2002:6464:64c8::]/" ,
167+ "http://[2001::9b9b:9b37]/" ,
168+ "http://[2001:4860:1::5efe:6464:64c8]/" ,
169+ ] ;
170+
171+ for ( const url of blockedUrls ) {
172+ const result = await runSandboxHttpRequestScript ( {
173+ method : "GET" ,
174+ url,
175+ timeoutMs : 1 ,
176+ } ) ;
177+ expect ( result . code , url ) . not . toBe ( 0 ) ;
178+ expect ( result . stdout , url ) . toBe ( "" ) ;
179+ expect ( result . stderr , url ) . toContain ( "Blocked" ) ;
180+ }
181+ } ) ;
182+
129183 it ( "streams HTTP response body deltas from the sandbox backend" , async ( ) => {
130184 const headerLine = JSON . stringify ( {
131185 type : "headers" ,
0 commit comments