Skip to main content
Glama

Electron MCP Server

by halilural
sandbox.tsโ€ข8.23 kB
import { spawn } from 'child_process'; import { promises as fs } from 'fs'; import { join } from 'path'; import { randomUUID } from 'crypto'; import { logger } from '../utils/logger'; export interface SandboxOptions { timeout?: number; maxMemory?: number; allowedModules?: string[]; blacklistedFunctions?: string[]; } export interface SandboxResult { success: boolean; result?: any; error?: string; executionTime: number; memoryUsed?: number; } const DEFAULT_BLACKLISTED_FUNCTIONS = [ 'eval', 'Function', 'setTimeout', 'setInterval', 'setImmediate', 'require', 'import', 'process', 'global', 'globalThis', '__dirname', '__filename', 'Buffer', 'XMLHttpRequest', 'fetch', 'WebSocket', 'Worker', 'SharedWorker', 'ServiceWorker', 'importScripts', 'postMessage', 'close', 'open', ]; const DEFAULT_BLACKLISTED_OBJECTS = [ 'fs', 'child_process', 'cluster', 'crypto', 'dgram', 'dns', 'http', 'https', 'net', 'os', 'path', 'stream', 'tls', 'url', 'util', 'v8', 'vm', 'worker_threads', 'zlib', 'perf_hooks', 'inspector', 'repl', 'readline', 'domain', 'events', 'querystring', 'punycode', 'constants', ]; export class CodeSandbox { private options: Required<SandboxOptions>; constructor(options: SandboxOptions = {}) { this.options = { timeout: options.timeout || 5000, maxMemory: options.maxMemory || 50 * 1024 * 1024, // 50MB allowedModules: options.allowedModules || [], blacklistedFunctions: [ ...DEFAULT_BLACKLISTED_FUNCTIONS, ...(options.blacklistedFunctions || []), ], }; } async executeCode(code: string): Promise<SandboxResult> { const startTime = Date.now(); const sessionId = randomUUID(); logger.info(`Starting sandboxed execution [${sessionId}]`); try { // Validate code before execution const validation = this.validateCode(code); if (!validation.isValid) { return { success: false, error: `Code validation failed: ${validation.errors.join(', ')}`, executionTime: Date.now() - startTime, }; } // Create isolated execution environment const result = await this.executeInIsolation(code, sessionId); const executionTime = Date.now() - startTime; logger.info(`Sandboxed execution completed [${sessionId}] in ${executionTime}ms`); return { success: true, result: result, executionTime, }; } catch (error) { const executionTime = Date.now() - startTime; logger.error(`Sandboxed execution failed [${sessionId}]:`, error); return { success: false, error: error instanceof Error ? error.message : String(error), executionTime, }; } } private validateCode(code: string): { isValid: boolean; errors: string[] } { const errors: string[] = []; // Check for blacklisted functions for (const func of this.options.blacklistedFunctions) { const regex = new RegExp(`\\b${func}\\s*\\(`, 'g'); if (regex.test(code)) { errors.push(`Forbidden function: ${func}`); } } // Check for blacklisted objects for (const obj of DEFAULT_BLACKLISTED_OBJECTS) { const regex = new RegExp(`\\b${obj}\\b`, 'g'); if (regex.test(code)) { errors.push(`Forbidden module/object: ${obj}`); } } // Check for dangerous patterns const dangerousPatterns = [ /require\s*\(/g, /import\s+.*\s+from/g, /\.constructor/g, /\.__proto__/g, /prototype\./g, /process\./g, /global\./g, /this\.constructor/g, /\[\s*['"`]constructor['"`]\s*\]/g, /\[\s*['"`]__proto__['"`]\s*\]/g, /Function\s*\(/g, /eval\s*\(/g, /window\./g, /document\./g, /location\./g, /history\./g, /navigator\./g, /alert\s*\(/g, /confirm\s*\(/g, /prompt\s*\(/g, ]; for (const pattern of dangerousPatterns) { if (pattern.test(code)) { errors.push(`Dangerous pattern detected: ${pattern.source}`); } } return { isValid: errors.length === 0, errors, }; } private async executeInIsolation(code: string, sessionId: string): Promise<any> { // Create a secure wrapper script const wrapperCode = this.createSecureWrapper(code); // Write to temporary file const tempDir = join(process.cwd(), 'temp', sessionId); await fs.mkdir(tempDir, { recursive: true }); const scriptPath = join(tempDir, 'script.cjs'); // Use .cjs for CommonJS try { await fs.writeFile(scriptPath, wrapperCode); // Execute in isolated Node.js process const result = await this.executeInProcess(scriptPath); return result; } finally { // Cleanup try { await fs.unlink(scriptPath); await fs.rm(tempDir, { recursive: true, force: true }); // Also try to clean up the parent temp directory if it's empty try { const parentTempDir = join(process.cwd(), 'temp'); await fs.rmdir(parentTempDir); } catch { // Ignore if not empty or doesn't exist } } catch (cleanupError) { logger.warn(`Failed to cleanup temp files for session ${sessionId}:`, cleanupError); } } } private createSecureWrapper(userCode: string): string { return ` "use strict"; const vm = require('vm'); // Create isolated context const originalProcess = process; const originalConsole = console; // Create safe console const safeConsole = { log: (...args) => originalConsole.log('[SANDBOX]', ...args), error: (...args) => originalConsole.error('[SANDBOX]', ...args), warn: (...args) => originalConsole.warn('[SANDBOX]', ...args), info: (...args) => originalConsole.info('[SANDBOX]', ...args), debug: (...args) => originalConsole.debug('[SANDBOX]', ...args) }; // Create a secure context with only safe globals const sandboxContext = vm.createContext({ console: safeConsole, Math: Math, Date: Date, JSON: JSON, parseInt: parseInt, parseFloat: parseFloat, isNaN: isNaN, isFinite: isFinite, String: String, Number: Number, Boolean: Boolean, Array: Array, Object: Object, RegExp: RegExp, Error: Error, TypeError: TypeError, RangeError: RangeError, SyntaxError: SyntaxError, // Provide a safe setTimeout that's actually synchronous for safety setTimeout: (fn, delay) => { if (typeof fn === 'function' && delay === 0) { return fn(); } throw new Error('setTimeout not available in sandbox'); } }); try { // Execute user code in isolated VM context const result = vm.runInContext(${JSON.stringify(userCode)}, sandboxContext, { timeout: 5000, // 5 second timeout displayErrors: true, breakOnSigint: true }); // Send result back originalProcess.stdout.write(JSON.stringify({ success: true, result: result })); } catch (error) { originalProcess.stdout.write(JSON.stringify({ success: false, error: error.message, stack: error.stack })); } `; } private executeInProcess(scriptPath: string): Promise<any> { return new Promise((resolve, reject) => { const child = spawn('node', [scriptPath], { stdio: ['ignore', 'pipe', 'pipe'], timeout: this.options.timeout, }); let stdout = ''; let stderr = ''; child.stdout.on('data', (data) => { stdout += data.toString(); }); child.stderr.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { if (code === 0) { try { const result = JSON.parse(stdout); if (result.success) { resolve(result.result); } else { reject(new Error(result.error)); } } catch (parseError) { reject(new Error(`Failed to parse execution result: ${parseError}`)); } } else { reject(new Error(`Process exited with code ${code}: ${stderr}`)); } }); child.on('error', (error) => { reject(error); }); }); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/halilural/electron-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server