@cloudflare/sandbox 0.3.7 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +44 -0
- package/CHANGELOG.md +8 -10
- package/Dockerfile +82 -18
- package/README.md +89 -824
- package/dist/chunk-53JFOF7F.js +2352 -0
- package/dist/chunk-53JFOF7F.js.map +1 -0
- package/dist/chunk-BFVUNTP4.js +104 -0
- package/dist/chunk-BFVUNTP4.js.map +1 -0
- package/dist/{chunk-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
- package/dist/chunk-EKSWCBCA.js.map +1 -0
- package/dist/chunk-JXZMAU2C.js +559 -0
- package/dist/chunk-JXZMAU2C.js.map +1 -0
- package/dist/{chunk-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
- package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
- package/dist/file-stream.d.ts +16 -38
- package/dist/file-stream.js +1 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +45 -38
- package/dist/interpreter.d.ts +3 -3
- package/dist/interpreter.js +2 -2
- package/dist/request-handler.d.ts +4 -3
- package/dist/request-handler.js +4 -7
- package/dist/sandbox-D9K2ypln.d.ts +583 -0
- package/dist/sandbox.d.ts +3 -3
- package/dist/sandbox.js +4 -7
- package/dist/security.d.ts +4 -3
- package/dist/security.js +3 -3
- package/dist/sse-parser.js +1 -1
- package/package.json +12 -4
- package/src/clients/base-client.ts +280 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +269 -0
- package/src/clients/git-client.ts +92 -0
- package/src/clients/index.ts +63 -0
- package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +177 -0
- package/src/clients/sandbox-client.ts +41 -0
- package/src/clients/types.ts +84 -0
- package/src/clients/utility-client.ts +94 -0
- package/src/errors/adapter.ts +180 -0
- package/src/errors/classes.ts +469 -0
- package/src/errors/index.ts +105 -0
- package/src/file-stream.ts +119 -117
- package/src/index.ts +81 -69
- package/src/interpreter.ts +17 -8
- package/src/request-handler.ts +69 -43
- package/src/sandbox.ts +694 -533
- package/src/security.ts +14 -23
- package/src/sse-parser.ts +4 -8
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +328 -0
- package/tests/command-client.test.ts +407 -0
- package/tests/file-client.test.ts +643 -0
- package/tests/file-stream.test.ts +306 -0
- package/tests/git-client.test.ts +328 -0
- package/tests/port-client.test.ts +301 -0
- package/tests/process-client.test.ts +658 -0
- package/tests/sandbox.test.ts +465 -0
- package/tests/sse-parser.test.ts +290 -0
- package/tests/utility-client.test.ts +266 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/vitest.config.ts +31 -0
- package/container_src/bun.lock +0 -76
- package/container_src/circuit-breaker.ts +0 -121
- package/container_src/control-process.ts +0 -784
- package/container_src/handler/exec.ts +0 -185
- package/container_src/handler/file.ts +0 -457
- package/container_src/handler/git.ts +0 -130
- package/container_src/handler/ports.ts +0 -314
- package/container_src/handler/process.ts +0 -568
- package/container_src/handler/session.ts +0 -92
- package/container_src/index.ts +0 -601
- package/container_src/interpreter-service.ts +0 -276
- package/container_src/isolation.ts +0 -1213
- package/container_src/mime-processor.ts +0 -255
- package/container_src/package.json +0 -18
- package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
- package/container_src/runtime/executors/python/ipython_executor.py +0 -338
- package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
- package/container_src/runtime/process-pool.ts +0 -464
- package/container_src/shell-escape.ts +0 -42
- package/container_src/startup.sh +0 -11
- package/container_src/types.ts +0 -131
- package/dist/chunk-32UDXUPC.js +0 -671
- package/dist/chunk-32UDXUPC.js.map +0 -1
- package/dist/chunk-5DILEXGY.js +0 -85
- package/dist/chunk-5DILEXGY.js.map +0 -1
- package/dist/chunk-D3U63BZP.js +0 -240
- package/dist/chunk-D3U63BZP.js.map +0 -1
- package/dist/chunk-FXYPFGOZ.js +0 -129
- package/dist/chunk-FXYPFGOZ.js.map +0 -1
- package/dist/chunk-JTKON2SH.js +0 -113
- package/dist/chunk-JTKON2SH.js.map +0 -1
- package/dist/chunk-NNGBXDMY.js.map +0 -1
- package/dist/chunk-SQLJNZ3K.js +0 -674
- package/dist/chunk-SQLJNZ3K.js.map +0 -1
- package/dist/chunk-W7TVRPBG.js +0 -108
- package/dist/chunk-W7TVRPBG.js.map +0 -1
- package/dist/client-B3RUab0s.d.ts +0 -225
- package/dist/client.d.ts +0 -4
- package/dist/client.js +0 -7
- package/dist/client.js.map +0 -1
- package/dist/errors.d.ts +0 -95
- package/dist/errors.js +0 -27
- package/dist/errors.js.map +0 -1
- package/dist/interpreter-client.d.ts +0 -4
- package/dist/interpreter-client.js +0 -9
- package/dist/interpreter-client.js.map +0 -1
- package/dist/interpreter-types.d.ts +0 -259
- package/dist/interpreter-types.js +0 -9
- package/dist/interpreter-types.js.map +0 -1
- package/dist/types.d.ts +0 -453
- package/dist/types.js +0 -45
- package/dist/types.js.map +0 -1
- package/src/client.ts +0 -1048
- package/src/errors.ts +0 -219
- package/src/interpreter-types.ts +0 -390
- package/src/types.ts +0 -571
package/dist/chunk-SQLJNZ3K.js
DELETED
|
@@ -1,674 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SecurityError,
|
|
3
|
-
logSecurityEvent,
|
|
4
|
-
sanitizeSandboxId,
|
|
5
|
-
validatePort
|
|
6
|
-
} from "./chunk-6UAWTJ5S.js";
|
|
7
|
-
import {
|
|
8
|
-
InterpreterClient
|
|
9
|
-
} from "./chunk-D3U63BZP.js";
|
|
10
|
-
import {
|
|
11
|
-
CodeInterpreter
|
|
12
|
-
} from "./chunk-JTKON2SH.js";
|
|
13
|
-
|
|
14
|
-
// src/sandbox.ts
|
|
15
|
-
import { Container, getContainer } from "@cloudflare/containers";
|
|
16
|
-
|
|
17
|
-
// src/request-handler.ts
|
|
18
|
-
async function proxyToSandbox(request, env) {
|
|
19
|
-
try {
|
|
20
|
-
const url = new URL(request.url);
|
|
21
|
-
const routeInfo = extractSandboxRoute(url);
|
|
22
|
-
if (!routeInfo) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
const { sandboxId, port, path } = routeInfo;
|
|
26
|
-
const sandbox = getSandbox(env.Sandbox, sandboxId);
|
|
27
|
-
let proxyUrl;
|
|
28
|
-
if (port !== 3e3) {
|
|
29
|
-
proxyUrl = `http://localhost:${port}${path}${url.search}`;
|
|
30
|
-
} else {
|
|
31
|
-
proxyUrl = `http://localhost:3000${path}${url.search}`;
|
|
32
|
-
}
|
|
33
|
-
const proxyRequest = new Request(proxyUrl, {
|
|
34
|
-
method: request.method,
|
|
35
|
-
headers: {
|
|
36
|
-
...Object.fromEntries(request.headers),
|
|
37
|
-
"X-Original-URL": request.url,
|
|
38
|
-
"X-Forwarded-Host": url.hostname,
|
|
39
|
-
"X-Forwarded-Proto": url.protocol.replace(":", ""),
|
|
40
|
-
"X-Sandbox-Name": sandboxId
|
|
41
|
-
// Pass the friendly name
|
|
42
|
-
},
|
|
43
|
-
body: request.body
|
|
44
|
-
});
|
|
45
|
-
return sandbox.containerFetch(proxyRequest, port);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error("[Sandbox] Proxy routing error:", error);
|
|
48
|
-
return new Response("Proxy routing error", { status: 500 });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function extractSandboxRoute(url) {
|
|
52
|
-
const subdomainMatch = url.hostname.match(/^(\d{4,5})-([^.-][^.]*[^.-]|[^.-])\.(.+)$/);
|
|
53
|
-
if (!subdomainMatch) {
|
|
54
|
-
if (url.hostname.includes("-") && url.hostname.includes(".")) {
|
|
55
|
-
logSecurityEvent("MALFORMED_SUBDOMAIN_ATTEMPT", {
|
|
56
|
-
hostname: url.hostname,
|
|
57
|
-
url: url.toString()
|
|
58
|
-
}, "medium");
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const portStr = subdomainMatch[1];
|
|
63
|
-
const sandboxId = subdomainMatch[2];
|
|
64
|
-
const domain = subdomainMatch[3];
|
|
65
|
-
const port = parseInt(portStr, 10);
|
|
66
|
-
if (!validatePort(port)) {
|
|
67
|
-
logSecurityEvent("INVALID_PORT_IN_SUBDOMAIN", {
|
|
68
|
-
port,
|
|
69
|
-
portStr,
|
|
70
|
-
sandboxId,
|
|
71
|
-
hostname: url.hostname,
|
|
72
|
-
url: url.toString()
|
|
73
|
-
}, "high");
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
let sanitizedSandboxId;
|
|
77
|
-
try {
|
|
78
|
-
sanitizedSandboxId = sanitizeSandboxId(sandboxId);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
logSecurityEvent("INVALID_SANDBOX_ID_IN_SUBDOMAIN", {
|
|
81
|
-
sandboxId,
|
|
82
|
-
port,
|
|
83
|
-
hostname: url.hostname,
|
|
84
|
-
url: url.toString(),
|
|
85
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
86
|
-
}, "high");
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
if (sandboxId.length > 63) {
|
|
90
|
-
logSecurityEvent("SANDBOX_ID_LENGTH_VIOLATION", {
|
|
91
|
-
sandboxId,
|
|
92
|
-
length: sandboxId.length,
|
|
93
|
-
port,
|
|
94
|
-
hostname: url.hostname
|
|
95
|
-
}, "medium");
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
logSecurityEvent("SANDBOX_ROUTE_EXTRACTED", {
|
|
99
|
-
port,
|
|
100
|
-
sandboxId: sanitizedSandboxId,
|
|
101
|
-
domain,
|
|
102
|
-
path: url.pathname || "/",
|
|
103
|
-
hostname: url.hostname
|
|
104
|
-
}, "low");
|
|
105
|
-
return {
|
|
106
|
-
port,
|
|
107
|
-
sandboxId: sanitizedSandboxId,
|
|
108
|
-
path: url.pathname || "/"
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function isLocalhostPattern(hostname) {
|
|
112
|
-
const hostPart = hostname.split(":")[0];
|
|
113
|
-
return hostPart === "localhost" || hostPart === "127.0.0.1" || hostPart === "::1" || hostPart === "[::1]" || hostPart === "0.0.0.0";
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// src/sandbox.ts
|
|
117
|
-
function getSandbox(ns, id) {
|
|
118
|
-
const stub = getContainer(ns, id);
|
|
119
|
-
stub.setSandboxName?.(id);
|
|
120
|
-
return stub;
|
|
121
|
-
}
|
|
122
|
-
var Sandbox = class extends Container {
|
|
123
|
-
defaultPort = 3e3;
|
|
124
|
-
// Default port for the container's Bun server
|
|
125
|
-
sleepAfter = "20m";
|
|
126
|
-
// Keep container warm for 20 minutes to avoid cold starts
|
|
127
|
-
client;
|
|
128
|
-
sandboxName = null;
|
|
129
|
-
codeInterpreter;
|
|
130
|
-
defaultSession = null;
|
|
131
|
-
constructor(ctx, env) {
|
|
132
|
-
super(ctx, env);
|
|
133
|
-
this.client = new InterpreterClient({
|
|
134
|
-
onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {
|
|
135
|
-
console.log(
|
|
136
|
-
`[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`
|
|
137
|
-
);
|
|
138
|
-
},
|
|
139
|
-
onCommandStart: (command) => {
|
|
140
|
-
console.log(`[Container] Command started: ${command}`);
|
|
141
|
-
},
|
|
142
|
-
onError: (error, _command) => {
|
|
143
|
-
console.error(`[Container] Command error: ${error}`);
|
|
144
|
-
},
|
|
145
|
-
onOutput: (stream, data, _command) => {
|
|
146
|
-
console.log(`[Container] [${stream}] ${data}`);
|
|
147
|
-
},
|
|
148
|
-
port: 3e3,
|
|
149
|
-
// Control plane port
|
|
150
|
-
stub: this
|
|
151
|
-
});
|
|
152
|
-
this.codeInterpreter = new CodeInterpreter(this);
|
|
153
|
-
this.ctx.blockConcurrencyWhile(async () => {
|
|
154
|
-
this.sandboxName = await this.ctx.storage.get("sandboxName") || null;
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
// RPC method to set the sandbox name
|
|
158
|
-
async setSandboxName(name) {
|
|
159
|
-
if (!this.sandboxName) {
|
|
160
|
-
this.sandboxName = name;
|
|
161
|
-
await this.ctx.storage.put("sandboxName", name);
|
|
162
|
-
console.log(`[Sandbox] Stored sandbox name via RPC: ${name}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
// RPC method to set environment variables
|
|
166
|
-
async setEnvVars(envVars) {
|
|
167
|
-
this.envVars = { ...this.envVars, ...envVars };
|
|
168
|
-
console.log(`[Sandbox] Updated environment variables`);
|
|
169
|
-
if (this.defaultSession) {
|
|
170
|
-
await this.defaultSession.setEnvVars(envVars);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
onStart() {
|
|
174
|
-
console.log("Sandbox successfully started");
|
|
175
|
-
}
|
|
176
|
-
onStop() {
|
|
177
|
-
console.log("Sandbox successfully shut down");
|
|
178
|
-
}
|
|
179
|
-
onError(error) {
|
|
180
|
-
console.log("Sandbox error:", error);
|
|
181
|
-
}
|
|
182
|
-
// Override fetch to route internal container requests to appropriate ports
|
|
183
|
-
async fetch(request) {
|
|
184
|
-
const url = new URL(request.url);
|
|
185
|
-
if (!this.sandboxName && request.headers.has("X-Sandbox-Name")) {
|
|
186
|
-
const name = request.headers.get("X-Sandbox-Name");
|
|
187
|
-
this.sandboxName = name;
|
|
188
|
-
await this.ctx.storage.put("sandboxName", name);
|
|
189
|
-
console.log(`[Sandbox] Stored sandbox name: ${this.sandboxName}`);
|
|
190
|
-
}
|
|
191
|
-
const port = this.determinePort(url);
|
|
192
|
-
return await this.containerFetch(request, port);
|
|
193
|
-
}
|
|
194
|
-
determinePort(url) {
|
|
195
|
-
const proxyMatch = url.pathname.match(/^\/proxy\/(\d+)/);
|
|
196
|
-
if (proxyMatch) {
|
|
197
|
-
return parseInt(proxyMatch[1]);
|
|
198
|
-
}
|
|
199
|
-
if (url.port) {
|
|
200
|
-
return parseInt(url.port);
|
|
201
|
-
}
|
|
202
|
-
return 3e3;
|
|
203
|
-
}
|
|
204
|
-
// Helper to ensure default session is initialized
|
|
205
|
-
async ensureDefaultSession() {
|
|
206
|
-
if (!this.defaultSession) {
|
|
207
|
-
const sessionId = `sandbox-${this.sandboxName || "default"}`;
|
|
208
|
-
this.defaultSession = await this.createSession({
|
|
209
|
-
id: sessionId,
|
|
210
|
-
env: this.envVars || {},
|
|
211
|
-
cwd: "/workspace",
|
|
212
|
-
isolation: true
|
|
213
|
-
});
|
|
214
|
-
console.log(`[Sandbox] Default session initialized: ${sessionId}`);
|
|
215
|
-
}
|
|
216
|
-
return this.defaultSession;
|
|
217
|
-
}
|
|
218
|
-
async exec(command, options) {
|
|
219
|
-
const session = await this.ensureDefaultSession();
|
|
220
|
-
return session.exec(command, options);
|
|
221
|
-
}
|
|
222
|
-
async startProcess(command, options) {
|
|
223
|
-
const session = await this.ensureDefaultSession();
|
|
224
|
-
return session.startProcess(command, options);
|
|
225
|
-
}
|
|
226
|
-
async listProcesses() {
|
|
227
|
-
const session = await this.ensureDefaultSession();
|
|
228
|
-
return session.listProcesses();
|
|
229
|
-
}
|
|
230
|
-
async getProcess(id) {
|
|
231
|
-
const session = await this.ensureDefaultSession();
|
|
232
|
-
return session.getProcess(id);
|
|
233
|
-
}
|
|
234
|
-
async killProcess(id, signal) {
|
|
235
|
-
const session = await this.ensureDefaultSession();
|
|
236
|
-
return session.killProcess(id, signal);
|
|
237
|
-
}
|
|
238
|
-
async killAllProcesses() {
|
|
239
|
-
const session = await this.ensureDefaultSession();
|
|
240
|
-
return session.killAllProcesses();
|
|
241
|
-
}
|
|
242
|
-
async cleanupCompletedProcesses() {
|
|
243
|
-
const session = await this.ensureDefaultSession();
|
|
244
|
-
return session.cleanupCompletedProcesses();
|
|
245
|
-
}
|
|
246
|
-
async getProcessLogs(id) {
|
|
247
|
-
const session = await this.ensureDefaultSession();
|
|
248
|
-
return session.getProcessLogs(id);
|
|
249
|
-
}
|
|
250
|
-
// Streaming methods - delegates to default session
|
|
251
|
-
async execStream(command, options) {
|
|
252
|
-
const session = await this.ensureDefaultSession();
|
|
253
|
-
return session.execStream(command, options);
|
|
254
|
-
}
|
|
255
|
-
async streamProcessLogs(processId, options) {
|
|
256
|
-
const session = await this.ensureDefaultSession();
|
|
257
|
-
return session.streamProcessLogs(processId, options);
|
|
258
|
-
}
|
|
259
|
-
async gitCheckout(repoUrl, options) {
|
|
260
|
-
const session = await this.ensureDefaultSession();
|
|
261
|
-
return session.gitCheckout(repoUrl, options);
|
|
262
|
-
}
|
|
263
|
-
async mkdir(path, options = {}) {
|
|
264
|
-
const session = await this.ensureDefaultSession();
|
|
265
|
-
return session.mkdir(path, options);
|
|
266
|
-
}
|
|
267
|
-
async writeFile(path, content, options = {}) {
|
|
268
|
-
const session = await this.ensureDefaultSession();
|
|
269
|
-
return session.writeFile(path, content, options);
|
|
270
|
-
}
|
|
271
|
-
async deleteFile(path) {
|
|
272
|
-
const session = await this.ensureDefaultSession();
|
|
273
|
-
return session.deleteFile(path);
|
|
274
|
-
}
|
|
275
|
-
async renameFile(oldPath, newPath) {
|
|
276
|
-
const session = await this.ensureDefaultSession();
|
|
277
|
-
return session.renameFile(oldPath, newPath);
|
|
278
|
-
}
|
|
279
|
-
async moveFile(sourcePath, destinationPath) {
|
|
280
|
-
const session = await this.ensureDefaultSession();
|
|
281
|
-
return session.moveFile(sourcePath, destinationPath);
|
|
282
|
-
}
|
|
283
|
-
async readFile(path, options = {}) {
|
|
284
|
-
const session = await this.ensureDefaultSession();
|
|
285
|
-
return session.readFile(path, options);
|
|
286
|
-
}
|
|
287
|
-
async readFileStream(path) {
|
|
288
|
-
const session = await this.ensureDefaultSession();
|
|
289
|
-
return session.readFileStream(path);
|
|
290
|
-
}
|
|
291
|
-
async listFiles(path, options = {}) {
|
|
292
|
-
const session = await this.ensureDefaultSession();
|
|
293
|
-
return session.listFiles(path, options);
|
|
294
|
-
}
|
|
295
|
-
async exposePort(port, options) {
|
|
296
|
-
await this.client.exposePort(port, options?.name);
|
|
297
|
-
if (!this.sandboxName) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
"Sandbox name not available. Ensure sandbox is accessed through getSandbox()"
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
const url = this.constructPreviewUrl(
|
|
303
|
-
port,
|
|
304
|
-
this.sandboxName,
|
|
305
|
-
options.hostname
|
|
306
|
-
);
|
|
307
|
-
return {
|
|
308
|
-
url,
|
|
309
|
-
port,
|
|
310
|
-
name: options?.name
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
async unexposePort(port) {
|
|
314
|
-
if (!validatePort(port)) {
|
|
315
|
-
logSecurityEvent(
|
|
316
|
-
"INVALID_PORT_UNEXPOSE",
|
|
317
|
-
{
|
|
318
|
-
port
|
|
319
|
-
},
|
|
320
|
-
"high"
|
|
321
|
-
);
|
|
322
|
-
throw new SecurityError(
|
|
323
|
-
`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
await this.client.unexposePort(port);
|
|
327
|
-
logSecurityEvent(
|
|
328
|
-
"PORT_UNEXPOSED",
|
|
329
|
-
{
|
|
330
|
-
port
|
|
331
|
-
},
|
|
332
|
-
"low"
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
async getExposedPorts(hostname) {
|
|
336
|
-
const response = await this.client.getExposedPorts();
|
|
337
|
-
if (!this.sandboxName) {
|
|
338
|
-
throw new Error(
|
|
339
|
-
"Sandbox name not available. Ensure sandbox is accessed through getSandbox()"
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
return response.ports.map((port) => ({
|
|
343
|
-
url: this.constructPreviewUrl(port.port, this.sandboxName, hostname),
|
|
344
|
-
port: port.port,
|
|
345
|
-
name: port.name,
|
|
346
|
-
exposedAt: port.exposedAt
|
|
347
|
-
}));
|
|
348
|
-
}
|
|
349
|
-
constructPreviewUrl(port, sandboxId, hostname) {
|
|
350
|
-
if (!validatePort(port)) {
|
|
351
|
-
logSecurityEvent(
|
|
352
|
-
"INVALID_PORT_REJECTED",
|
|
353
|
-
{
|
|
354
|
-
port,
|
|
355
|
-
sandboxId,
|
|
356
|
-
hostname
|
|
357
|
-
},
|
|
358
|
-
"high"
|
|
359
|
-
);
|
|
360
|
-
throw new SecurityError(
|
|
361
|
-
`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
let sanitizedSandboxId;
|
|
365
|
-
try {
|
|
366
|
-
sanitizedSandboxId = sanitizeSandboxId(sandboxId);
|
|
367
|
-
} catch (error) {
|
|
368
|
-
logSecurityEvent(
|
|
369
|
-
"INVALID_SANDBOX_ID_REJECTED",
|
|
370
|
-
{
|
|
371
|
-
sandboxId,
|
|
372
|
-
port,
|
|
373
|
-
hostname,
|
|
374
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
375
|
-
},
|
|
376
|
-
"high"
|
|
377
|
-
);
|
|
378
|
-
throw error;
|
|
379
|
-
}
|
|
380
|
-
const isLocalhost = isLocalhostPattern(hostname);
|
|
381
|
-
if (isLocalhost) {
|
|
382
|
-
const [host, portStr] = hostname.split(":");
|
|
383
|
-
const mainPort = portStr || "80";
|
|
384
|
-
try {
|
|
385
|
-
const baseUrl = new URL(`http://${host}:${mainPort}`);
|
|
386
|
-
const subdomainHost = `${port}-${sanitizedSandboxId}.${host}`;
|
|
387
|
-
baseUrl.hostname = subdomainHost;
|
|
388
|
-
const finalUrl = baseUrl.toString();
|
|
389
|
-
logSecurityEvent(
|
|
390
|
-
"PREVIEW_URL_CONSTRUCTED",
|
|
391
|
-
{
|
|
392
|
-
port,
|
|
393
|
-
sandboxId: sanitizedSandboxId,
|
|
394
|
-
hostname,
|
|
395
|
-
resultUrl: finalUrl,
|
|
396
|
-
environment: "localhost"
|
|
397
|
-
},
|
|
398
|
-
"low"
|
|
399
|
-
);
|
|
400
|
-
return finalUrl;
|
|
401
|
-
} catch (error) {
|
|
402
|
-
logSecurityEvent(
|
|
403
|
-
"URL_CONSTRUCTION_FAILED",
|
|
404
|
-
{
|
|
405
|
-
port,
|
|
406
|
-
sandboxId: sanitizedSandboxId,
|
|
407
|
-
hostname,
|
|
408
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
409
|
-
},
|
|
410
|
-
"high"
|
|
411
|
-
);
|
|
412
|
-
throw new SecurityError(
|
|
413
|
-
`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
414
|
-
);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
try {
|
|
418
|
-
const protocol = "https";
|
|
419
|
-
const baseUrl = new URL(`${protocol}://${hostname}`);
|
|
420
|
-
const subdomainHost = `${port}-${sanitizedSandboxId}.${hostname}`;
|
|
421
|
-
baseUrl.hostname = subdomainHost;
|
|
422
|
-
const finalUrl = baseUrl.toString();
|
|
423
|
-
logSecurityEvent(
|
|
424
|
-
"PREVIEW_URL_CONSTRUCTED",
|
|
425
|
-
{
|
|
426
|
-
port,
|
|
427
|
-
sandboxId: sanitizedSandboxId,
|
|
428
|
-
hostname,
|
|
429
|
-
resultUrl: finalUrl,
|
|
430
|
-
environment: "production"
|
|
431
|
-
},
|
|
432
|
-
"low"
|
|
433
|
-
);
|
|
434
|
-
return finalUrl;
|
|
435
|
-
} catch (error) {
|
|
436
|
-
logSecurityEvent(
|
|
437
|
-
"URL_CONSTRUCTION_FAILED",
|
|
438
|
-
{
|
|
439
|
-
port,
|
|
440
|
-
sandboxId: sanitizedSandboxId,
|
|
441
|
-
hostname,
|
|
442
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
443
|
-
},
|
|
444
|
-
"high"
|
|
445
|
-
);
|
|
446
|
-
throw new SecurityError(
|
|
447
|
-
`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// Code Interpreter Methods
|
|
452
|
-
/**
|
|
453
|
-
* Create a new code execution context
|
|
454
|
-
*/
|
|
455
|
-
async createCodeContext(options) {
|
|
456
|
-
return this.codeInterpreter.createCodeContext(options);
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Run code with streaming callbacks
|
|
460
|
-
*/
|
|
461
|
-
async runCode(code, options) {
|
|
462
|
-
const execution = await this.codeInterpreter.runCode(code, options);
|
|
463
|
-
return execution.toJSON();
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Run code and return a streaming response
|
|
467
|
-
*/
|
|
468
|
-
async runCodeStream(code, options) {
|
|
469
|
-
return this.codeInterpreter.runCodeStream(code, options);
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* List all code contexts
|
|
473
|
-
*/
|
|
474
|
-
async listCodeContexts() {
|
|
475
|
-
return this.codeInterpreter.listCodeContexts();
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Delete a code context
|
|
479
|
-
*/
|
|
480
|
-
async deleteCodeContext(contextId) {
|
|
481
|
-
return this.codeInterpreter.deleteCodeContext(contextId);
|
|
482
|
-
}
|
|
483
|
-
// ============================================================================
|
|
484
|
-
// Session Management (Simple Isolation)
|
|
485
|
-
// ============================================================================
|
|
486
|
-
/**
|
|
487
|
-
* Create a new execution session with isolation
|
|
488
|
-
* Returns a session object with exec() method
|
|
489
|
-
*/
|
|
490
|
-
async createSession(options) {
|
|
491
|
-
const sessionId = options.id || `session-${Date.now()}`;
|
|
492
|
-
await this.client.createSession({
|
|
493
|
-
id: sessionId,
|
|
494
|
-
env: options.env,
|
|
495
|
-
cwd: options.cwd,
|
|
496
|
-
isolation: options.isolation
|
|
497
|
-
});
|
|
498
|
-
return {
|
|
499
|
-
id: sessionId,
|
|
500
|
-
// Command execution - clean method names
|
|
501
|
-
exec: async (command, options2) => {
|
|
502
|
-
const result = await this.client.exec(sessionId, command);
|
|
503
|
-
return {
|
|
504
|
-
...result,
|
|
505
|
-
command,
|
|
506
|
-
duration: 0,
|
|
507
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
508
|
-
};
|
|
509
|
-
},
|
|
510
|
-
execStream: async (command, options2) => {
|
|
511
|
-
return await this.client.execStream(sessionId, command);
|
|
512
|
-
},
|
|
513
|
-
// Process management - route to session-aware methods
|
|
514
|
-
startProcess: async (command, options2) => {
|
|
515
|
-
const response = await this.client.startProcess(command, sessionId, {
|
|
516
|
-
processId: options2?.processId,
|
|
517
|
-
timeout: options2?.timeout,
|
|
518
|
-
env: options2?.env,
|
|
519
|
-
cwd: options2?.cwd,
|
|
520
|
-
encoding: options2?.encoding,
|
|
521
|
-
autoCleanup: options2?.autoCleanup
|
|
522
|
-
});
|
|
523
|
-
const process = response.process;
|
|
524
|
-
return {
|
|
525
|
-
id: process.id,
|
|
526
|
-
pid: process.pid,
|
|
527
|
-
command: process.command,
|
|
528
|
-
status: process.status,
|
|
529
|
-
startTime: new Date(process.startTime),
|
|
530
|
-
endTime: process.endTime ? new Date(process.endTime) : void 0,
|
|
531
|
-
exitCode: process.exitCode ?? void 0,
|
|
532
|
-
kill: async (signal) => {
|
|
533
|
-
await this.client.killProcess(process.id);
|
|
534
|
-
},
|
|
535
|
-
getStatus: async () => {
|
|
536
|
-
const resp = await this.client.getProcess(process.id);
|
|
537
|
-
return resp.process?.status || "error";
|
|
538
|
-
},
|
|
539
|
-
getLogs: async () => {
|
|
540
|
-
return await this.client.getProcessLogs(process.id);
|
|
541
|
-
}
|
|
542
|
-
};
|
|
543
|
-
},
|
|
544
|
-
listProcesses: async () => {
|
|
545
|
-
const response = await this.client.listProcesses(sessionId);
|
|
546
|
-
return response.processes.map((p) => ({
|
|
547
|
-
id: p.id,
|
|
548
|
-
pid: p.pid,
|
|
549
|
-
command: p.command,
|
|
550
|
-
status: p.status,
|
|
551
|
-
startTime: new Date(p.startTime),
|
|
552
|
-
endTime: p.endTime ? new Date(p.endTime) : void 0,
|
|
553
|
-
exitCode: p.exitCode ?? void 0,
|
|
554
|
-
kill: async (signal) => {
|
|
555
|
-
await this.client.killProcess(p.id);
|
|
556
|
-
},
|
|
557
|
-
getStatus: async () => {
|
|
558
|
-
const processResp = await this.client.getProcess(p.id);
|
|
559
|
-
return processResp.process?.status || "error";
|
|
560
|
-
},
|
|
561
|
-
getLogs: async () => {
|
|
562
|
-
return this.client.getProcessLogs(p.id);
|
|
563
|
-
}
|
|
564
|
-
}));
|
|
565
|
-
},
|
|
566
|
-
getProcess: async (id) => {
|
|
567
|
-
const response = await this.client.getProcess(id);
|
|
568
|
-
if (!response.process) return null;
|
|
569
|
-
const p = response.process;
|
|
570
|
-
return {
|
|
571
|
-
id: p.id,
|
|
572
|
-
pid: p.pid,
|
|
573
|
-
command: p.command,
|
|
574
|
-
status: p.status,
|
|
575
|
-
startTime: new Date(p.startTime),
|
|
576
|
-
endTime: p.endTime ? new Date(p.endTime) : void 0,
|
|
577
|
-
exitCode: p.exitCode ?? void 0,
|
|
578
|
-
kill: async (signal) => {
|
|
579
|
-
await this.client.killProcess(p.id);
|
|
580
|
-
},
|
|
581
|
-
getStatus: async () => {
|
|
582
|
-
const processResp = await this.client.getProcess(p.id);
|
|
583
|
-
return processResp.process?.status || "error";
|
|
584
|
-
},
|
|
585
|
-
getLogs: async () => {
|
|
586
|
-
return this.client.getProcessLogs(p.id);
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
},
|
|
590
|
-
killProcess: async (id, signal) => {
|
|
591
|
-
await this.client.killProcess(id);
|
|
592
|
-
},
|
|
593
|
-
killAllProcesses: async () => {
|
|
594
|
-
const response = await this.client.killAllProcesses(sessionId);
|
|
595
|
-
return response.killedCount;
|
|
596
|
-
},
|
|
597
|
-
streamProcessLogs: async (processId, options2) => {
|
|
598
|
-
return await this.client.streamProcessLogs(processId, options2);
|
|
599
|
-
},
|
|
600
|
-
getProcessLogs: async (id) => {
|
|
601
|
-
return await this.client.getProcessLogs(id);
|
|
602
|
-
},
|
|
603
|
-
cleanupCompletedProcesses: async () => {
|
|
604
|
-
return 0;
|
|
605
|
-
},
|
|
606
|
-
// File operations - clean method names (no "InSession" suffix)
|
|
607
|
-
writeFile: async (path, content, options2) => {
|
|
608
|
-
return await this.client.writeFile(path, content, options2?.encoding, sessionId);
|
|
609
|
-
},
|
|
610
|
-
readFile: async (path, options2) => {
|
|
611
|
-
return await this.client.readFile(path, options2?.encoding, sessionId);
|
|
612
|
-
},
|
|
613
|
-
readFileStream: async (path) => {
|
|
614
|
-
return await this.client.readFileStream(path, sessionId);
|
|
615
|
-
},
|
|
616
|
-
mkdir: async (path, options2) => {
|
|
617
|
-
return await this.client.mkdir(path, options2?.recursive, sessionId);
|
|
618
|
-
},
|
|
619
|
-
deleteFile: async (path) => {
|
|
620
|
-
return await this.client.deleteFile(path, sessionId);
|
|
621
|
-
},
|
|
622
|
-
renameFile: async (oldPath, newPath) => {
|
|
623
|
-
return await this.client.renameFile(oldPath, newPath, sessionId);
|
|
624
|
-
},
|
|
625
|
-
moveFile: async (sourcePath, destinationPath) => {
|
|
626
|
-
return await this.client.moveFile(sourcePath, destinationPath, sessionId);
|
|
627
|
-
},
|
|
628
|
-
listFiles: async (path, options2) => {
|
|
629
|
-
return await this.client.listFiles(path, sessionId, options2);
|
|
630
|
-
},
|
|
631
|
-
gitCheckout: async (repoUrl, options2) => {
|
|
632
|
-
return await this.client.gitCheckout(repoUrl, sessionId, options2?.branch, options2?.targetDir);
|
|
633
|
-
},
|
|
634
|
-
// Port management
|
|
635
|
-
exposePort: async (port, options2) => {
|
|
636
|
-
return await this.exposePort(port, options2);
|
|
637
|
-
},
|
|
638
|
-
unexposePort: async (port) => {
|
|
639
|
-
return await this.unexposePort(port);
|
|
640
|
-
},
|
|
641
|
-
getExposedPorts: async (hostname) => {
|
|
642
|
-
return await this.getExposedPorts(hostname);
|
|
643
|
-
},
|
|
644
|
-
// Environment management
|
|
645
|
-
setEnvVars: async (envVars) => {
|
|
646
|
-
console.log(`[Session ${sessionId}] Environment variables update not yet implemented`);
|
|
647
|
-
},
|
|
648
|
-
// Code Interpreter API
|
|
649
|
-
createCodeContext: async (options2) => {
|
|
650
|
-
return await this.createCodeContext(options2);
|
|
651
|
-
},
|
|
652
|
-
runCode: async (code, options2) => {
|
|
653
|
-
return await this.runCode(code, options2);
|
|
654
|
-
},
|
|
655
|
-
runCodeStream: async (code, options2) => {
|
|
656
|
-
return await this.runCodeStream(code, options2);
|
|
657
|
-
},
|
|
658
|
-
listCodeContexts: async () => {
|
|
659
|
-
return await this.listCodeContexts();
|
|
660
|
-
},
|
|
661
|
-
deleteCodeContext: async (contextId) => {
|
|
662
|
-
return await this.deleteCodeContext(contextId);
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
export {
|
|
669
|
-
getSandbox,
|
|
670
|
-
Sandbox,
|
|
671
|
-
proxyToSandbox,
|
|
672
|
-
isLocalhostPattern
|
|
673
|
-
};
|
|
674
|
-
//# sourceMappingURL=chunk-SQLJNZ3K.js.map
|