fix: remove VM2 sandboxing to resolve Next.js build errors
**VM2 Removal:** - Remove VM2 dependency from package-executor - Rewrite executor to use direct package execution with require() - Add TODO comment for future sandboxing implementation **Why this change:** - VM2 requires runtime filesystem access to bridge.js which doesn't work with Next.js Turbopack bundling - Even marking as serverExternalPackages fails because VM2 uses hardcoded file paths - Direct execution allows builds to complete while we find Next.js-compatible sandboxing solution **Next Steps:** - Implement proper sandboxing with isolated-vm or similar Next.js-compatible solution - Add security measures for package execution - Consider moving package execution to separate microservice This unblocks CI/CD while maintaining playground functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6d8bea6c8d
commit
14b64f0320
4 changed files with 30 additions and 51 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:*",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
|
|
@ -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: {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue