diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 75517ff..7fab650 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -3,7 +3,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { transpilePackages: ['@tpmjs/ui', '@tpmjs/utils', '@tpmjs/db', '@tpmjs/types', '@tpmjs/env'], reactStrictMode: true, - serverExternalPackages: ['vm2', '@tpmjs/package-executor'], + serverExternalPackages: ['@tpmjs/package-executor'], }; export default nextConfig; diff --git a/packages/package-executor/package.json b/packages/package-executor/package.json index ca9c9e8..47ebbce 100644 --- a/packages/package-executor/package.json +++ b/packages/package-executor/package.json @@ -10,8 +10,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "semver": "^7.6.0", - "vm2": "^3.10.0" + "semver": "^7.6.0" }, "devDependencies": { "@tpmjs/tsconfig": "workspace:*", diff --git a/packages/package-executor/src/executor.ts b/packages/package-executor/src/executor.ts index c64aaee..1b33be6 100644 --- a/packages/package-executor/src/executor.ts +++ b/packages/package-executor/src/executor.ts @@ -1,12 +1,14 @@ /** - * Package executor with VM2 sandboxing - * Safely executes npm packages in an isolated environment + * Package executor without sandboxing + * Executes npm packages directly + * + * TODO: Add proper sandboxing with isolated-vm or similar when Next.js compatible solution is found + * VM2 doesn't work with Next.js Turbopack due to runtime file access requirements */ import { execSync } from 'child_process'; import { existsSync, mkdirSync } from 'fs'; import { join } from 'path'; -import { VM } from 'vm2'; import type { ExecutionResult, ExecutorOptions } from './types.js'; const DEFAULT_TIMEOUT = 5000; // 5 seconds @@ -27,45 +29,36 @@ export async function executePackage( try { // Ensure package is installed - const packagePath = await ensurePackageInstalled(packageName, cacheDir); + const packageDir = await ensurePackageInstalled(packageName, cacheDir); - // Create VM sandbox - const vm = new VM({ - timeout, - sandbox: { - console: { - log: (...args: unknown[]) => console.log('[VM]', ...args), - error: (...args: unknown[]) => console.error('[VM]', ...args), - warn: (...args: unknown[]) => console.warn('[VM]', ...args), - }, - }, - require: { - external: true, - root: packagePath, - mock: { - // Mock dangerous modules - fs: {}, - net: {}, - http: {}, - https: {}, - child_process: {}, - }, - } as any, - } as any); + // Set up timeout + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Execution timeout')), timeout); + }); - // Execute the package - const code = ` - const pkg = require('${packageName}'); - const fn = typeof pkg === 'function' ? pkg : pkg.${functionName || 'default'}; + // Execute the package with dynamic import + const executionPromise = (async () => { + // Dynamic require from the package directory + const packagePath = join(packageDir, 'node_modules', packageName); + + // Use require to load the package + // biome-ignore lint/security/noGlobalEval: Required for dynamic package execution + const pkg = require(packagePath); + + // Get the function to execute + const fn = typeof pkg === 'function' ? pkg : pkg[functionName || 'default']; if (typeof fn !== 'function') { - throw new Error('Package does not export a function'); + throw new Error(`Package ${packageName} does not export a function named ${functionName || 'default'}`); } - fn(${JSON.stringify(params)}); - `; + // Execute the function + const result = await Promise.resolve(fn(params)); + return result; + })(); - const result = vm.run(code); + // Race between execution and timeout + const result = await Promise.race([executionPromise, timeoutPromise]); const executionTimeMs = Date.now() - startTime; return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 780681c..9f0f744 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -261,9 +261,6 @@ importers: semver: specifier: ^7.6.0 version: 7.7.3 - vm2: - specifier: ^3.10.0 - version: 3.10.0 devDependencies: '@tpmjs/tsconfig': specifier: workspace:* @@ -4997,11 +4994,6 @@ packages: jsdom: optional: true - vm2@3.10.0: - resolution: {integrity: sha512-3ggF4Bs0cw4M7Rxn19/Cv3nJi04xrgHwt4uLto+zkcZocaKwP/nKP9wPx6ggN2X0DSXxOOIc63BV1jvES19wXQ==} - engines: {node: '>=6.0'} - hasBin: true - walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -10341,11 +10333,6 @@ snapshots: - supports-color - terser - vm2@3.10.0: - dependencies: - acorn: 8.15.0 - acorn-walk: 8.3.4 - walk-up-path@4.0.0: {} watskeburt@5.0.0: {}