/**
* Code Sandbox - Secure execution of TypeScript code
*
* Uses Node.js built-in VM module for isolation.
* Note: This is less secure than isolated-vm but doesn't require native compilation.
* For production, consider using Deno or isolated-vm with an older Node version.
*/
import vm from "vm";
import ts from "typescript";
import type { ExecutionOptions, ExecutionResult } from "../types/index.js";
import type { MCPOrchestrator } from "../orchestrator/MCPOrchestrator.js";
import { logDebug, logError } from "../utils/logger.js";
export class CodeSandbox {
constructor(private orchestrator: MCPOrchestrator) {}
/**
* Execute TypeScript code in a sandbox
*/
async execute(
code: string,
options: ExecutionOptions = {},
): Promise<ExecutionResult> {
const startTime = Date.now();
const timeout = options.timeout || 30000; // 30 seconds default
const logs: string[] = [];
let result: any = undefined;
let error: string | undefined = undefined;
try {
// Compile TypeScript to JavaScript
const jsCode = this.compileTypeScript(code);
// Create sandbox context
const sandbox = this.createSandbox(logs);
// Create VM context
const context = vm.createContext(sandbox);
// Execute code (Note: timeout not directly supported in runInContext)
const script = new vm.Script(jsCode, {
filename: "sandbox.js",
});
// Wrap in promise for timeout
result = await Promise.race([
script.runInContext(context, {
breakOnSigint: true,
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Execution timeout")), timeout),
),
]);
logDebug("Code execution successful", {
executionTime: Date.now() - startTime,
logCount: logs.length,
});
} catch (err) {
error = err instanceof Error ? err.message : String(err);
logError("Code execution failed", { error });
}
return {
logs,
result,
error,
executionTime: Date.now() - startTime,
};
}
/**
* Compile TypeScript to JavaScript
*/
private compileTypeScript(code: string): string {
try {
// Use TypeScript's transpileModule for simple compilation
const result = ts.transpileModule(code, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
removeComments: false,
},
});
return result.outputText;
} catch (error) {
throw new Error(
`TypeScript compilation failed: ${
error instanceof Error ? error.message : String(error)
}`,
);
}
}
/**
* Create sandbox environment
*/
private createSandbox(logs: string[]): any {
// Captured console
const capturedConsole = {
log: (...args: any[]) => {
const message = args
.map((arg) =>
typeof arg === "object" ? JSON.stringify(arg) : String(arg),
)
.join(" ");
logs.push(message);
},
error: (...args: any[]) => {
const message =
"[ERROR] " +
args
.map((arg) =>
typeof arg === "object" ? JSON.stringify(arg) : String(arg),
)
.join(" ");
logs.push(message);
},
warn: (...args: any[]) => {
const message =
"[WARN] " +
args
.map((arg) =>
typeof arg === "object" ? JSON.stringify(arg) : String(arg),
)
.join(" ");
logs.push(message);
},
info: (...args: any[]) => {
const message =
"[INFO] " +
args
.map((arg) =>
typeof arg === "object" ? JSON.stringify(arg) : String(arg),
)
.join(" ");
logs.push(message);
},
debug: (...args: any[]) => {
const message =
"[DEBUG] " +
args
.map((arg) =>
typeof arg === "object" ? JSON.stringify(arg) : String(arg),
)
.join(" ");
logs.push(message);
},
};
// MCP call binding
const __mcp_call = async (toolName: string, args: any): Promise<any> => {
logDebug("Sandbox calling MCP tool", { toolName, args });
return this.orchestrator.callTool(toolName, args);
};
// Sandbox environment
// IMPORTANT: This is minimal isolation. For production, use isolated-vm or Deno.
const sandbox = {
console: capturedConsole,
__mcp_call,
// Provide minimal globals
Promise,
Object,
Array,
String,
Number,
Boolean,
Date,
JSON,
Math,
parseInt,
parseFloat,
isNaN,
isFinite,
// Block dangerous globals
require: undefined,
process: undefined,
global: undefined,
globalThis: undefined,
Buffer: undefined,
setTimeout: undefined,
setInterval: undefined,
setImmediate: undefined,
clearTimeout: undefined,
clearInterval: undefined,
clearImmediate: undefined,
// NO network access
fetch: undefined,
XMLHttpRequest: undefined,
WebSocket: undefined,
// NO filesystem access
fs: undefined,
path: undefined,
};
return sandbox;
}
/**
* Reset sandbox state
*/
reset(): void {
// Node.js VM doesn't need explicit reset
logDebug("Sandbox reset");
}
/**
* Destroy sandbox
*/
destroy(): void {
// Node.js VM doesn't need explicit cleanup
logDebug("Sandbox destroyed");
}
}