@unrdf/hooks 5.0.1
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/LICENSE +21 -0
- package/README.md +86 -0
- package/package.json +70 -0
- package/src/hooks/builtin-hooks.mjs +296 -0
- package/src/hooks/condition-cache.mjs +109 -0
- package/src/hooks/condition-evaluator.mjs +722 -0
- package/src/hooks/define-hook.mjs +211 -0
- package/src/hooks/effect-sandbox-worker.mjs +170 -0
- package/src/hooks/effect-sandbox.mjs +517 -0
- package/src/hooks/file-resolver.mjs +387 -0
- package/src/hooks/hook-chain-compiler.mjs +236 -0
- package/src/hooks/hook-executor-batching.mjs +277 -0
- package/src/hooks/hook-executor.mjs +465 -0
- package/src/hooks/hook-management.mjs +202 -0
- package/src/hooks/hook-scheduler.mjs +413 -0
- package/src/hooks/knowledge-hook-engine.mjs +358 -0
- package/src/hooks/knowledge-hook-manager.mjs +269 -0
- package/src/hooks/observability.mjs +531 -0
- package/src/hooks/policy-pack.mjs +572 -0
- package/src/hooks/quad-pool.mjs +249 -0
- package/src/hooks/quality-metrics.mjs +544 -0
- package/src/hooks/security/error-sanitizer.mjs +257 -0
- package/src/hooks/security/path-validator.mjs +194 -0
- package/src/hooks/security/sandbox-restrictions.mjs +331 -0
- package/src/hooks/telemetry.mjs +167 -0
- package/src/index.mjs +101 -0
- package/src/security/sandbox/browser-executor.mjs +220 -0
- package/src/security/sandbox/detector.mjs +342 -0
- package/src/security/sandbox/isolated-vm-executor.mjs +373 -0
- package/src/security/sandbox/vm2-executor.mjs +217 -0
- package/src/security/sandbox/worker-executor-runtime.mjs +74 -0
- package/src/security/sandbox/worker-executor.mjs +212 -0
- package/src/security/sandbox-adapter.mjs +141 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Browser Executor
|
|
3
|
+
* @module security/sandbox/browser-executor
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Browser-based sandbox executor using Web Workers.
|
|
7
|
+
* Provides sandboxed execution in browser environments with:
|
|
8
|
+
* - Separate worker context
|
|
9
|
+
* - Message-based communication
|
|
10
|
+
* - WASM support
|
|
11
|
+
* - Async/await support
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* global Worker */
|
|
15
|
+
import { trace } from '@opentelemetry/api';
|
|
16
|
+
|
|
17
|
+
const tracer = trace.getTracer('browser-executor');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Browser Executor (Web Workers)
|
|
21
|
+
*/
|
|
22
|
+
export class BrowserExecutor {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Object} [config] - Executor configuration
|
|
25
|
+
* @param {number} [config.timeout=5000] - Execution timeout in ms
|
|
26
|
+
* @param {boolean} [config.enableWasm=true] - Enable WASM support
|
|
27
|
+
* @param {boolean} [config.strictMode=true] - Enable strict mode
|
|
28
|
+
*/
|
|
29
|
+
constructor(config = {}) {
|
|
30
|
+
this.config = {
|
|
31
|
+
timeout: config.timeout || 5000,
|
|
32
|
+
enableWasm: config.enableWasm !== false,
|
|
33
|
+
strictMode: config.strictMode !== false,
|
|
34
|
+
...config,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** @type {Map<string, Worker>} */
|
|
38
|
+
this.workers = new Map();
|
|
39
|
+
|
|
40
|
+
this.executionCount = 0;
|
|
41
|
+
this.totalDuration = 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Execute code in Web Worker
|
|
46
|
+
* @param {string|Function} code - Code to execute
|
|
47
|
+
* @param {Object} [context] - Execution context
|
|
48
|
+
* @param {Object} [options] - Execution options
|
|
49
|
+
* @returns {Promise<Object>} Execution result
|
|
50
|
+
*/
|
|
51
|
+
async run(code, context = {}, options = {}) {
|
|
52
|
+
return tracer.startActiveSpan('security.browser.execute', async span => {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
const executionId = `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
span.setAttributes({
|
|
58
|
+
'security.executor.type': 'browser',
|
|
59
|
+
'security.execution.id': executionId,
|
|
60
|
+
'security.timeout': options.timeout || this.config.timeout,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Convert function to string if needed
|
|
64
|
+
const codeString = typeof code === 'function' ? code.toString() : code;
|
|
65
|
+
|
|
66
|
+
// Wrap code in strict mode if enabled
|
|
67
|
+
const wrappedCode = this.config.strictMode ? `"use strict";\n${codeString}` : codeString;
|
|
68
|
+
|
|
69
|
+
// Create worker blob
|
|
70
|
+
const workerCode = `
|
|
71
|
+
self.onmessage = function(e) {
|
|
72
|
+
const { code, context } = e.data;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Create safe environment
|
|
76
|
+
const sandbox = {
|
|
77
|
+
console: {
|
|
78
|
+
log: (...args) => self.postMessage({ type: 'log', args }),
|
|
79
|
+
error: (...args) => self.postMessage({ type: 'error', args }),
|
|
80
|
+
warn: (...args) => self.postMessage({ type: 'warn', args }),
|
|
81
|
+
info: (...args) => self.postMessage({ type: 'info', args })
|
|
82
|
+
},
|
|
83
|
+
Date: { now: () => Date.now() },
|
|
84
|
+
Math: Math,
|
|
85
|
+
JSON: JSON,
|
|
86
|
+
...context
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Execute code
|
|
90
|
+
const func = new Function(...Object.keys(sandbox), code);
|
|
91
|
+
const result = func(...Object.values(sandbox));
|
|
92
|
+
|
|
93
|
+
self.postMessage({ type: 'result', success: true, result });
|
|
94
|
+
} catch (error) {
|
|
95
|
+
self.postMessage({
|
|
96
|
+
type: 'result',
|
|
97
|
+
success: false,
|
|
98
|
+
error: error.message,
|
|
99
|
+
stack: error.stack
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
106
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
107
|
+
|
|
108
|
+
// Create worker promise
|
|
109
|
+
const result = await new Promise((resolve, reject) => {
|
|
110
|
+
const timeout = setTimeout(() => {
|
|
111
|
+
worker.terminate();
|
|
112
|
+
this.workers.delete(executionId);
|
|
113
|
+
URL.revokeObjectURL(workerUrl);
|
|
114
|
+
reject(new Error(`Browser worker execution timeout after ${this.config.timeout}ms`));
|
|
115
|
+
}, options.timeout || this.config.timeout);
|
|
116
|
+
|
|
117
|
+
const worker = new Worker(workerUrl);
|
|
118
|
+
this.workers.set(executionId, worker);
|
|
119
|
+
|
|
120
|
+
worker.onmessage = e => {
|
|
121
|
+
const { type, success, result, error, args } = e.data;
|
|
122
|
+
|
|
123
|
+
if (type === 'log' || type === 'error' || type === 'warn' || type === 'info') {
|
|
124
|
+
console[type]('[Worker]', ...args);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (type === 'result') {
|
|
129
|
+
clearTimeout(timeout);
|
|
130
|
+
worker.terminate();
|
|
131
|
+
this.workers.delete(executionId);
|
|
132
|
+
URL.revokeObjectURL(workerUrl);
|
|
133
|
+
|
|
134
|
+
if (success) {
|
|
135
|
+
resolve({ success: true, result });
|
|
136
|
+
} else {
|
|
137
|
+
reject(new Error(error || 'Worker execution failed'));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
worker.onerror = error => {
|
|
143
|
+
clearTimeout(timeout);
|
|
144
|
+
worker.terminate();
|
|
145
|
+
this.workers.delete(executionId);
|
|
146
|
+
URL.revokeObjectURL(workerUrl);
|
|
147
|
+
reject(error);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Send code to worker
|
|
151
|
+
worker.postMessage({ code: wrappedCode, context });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const duration = Date.now() - startTime;
|
|
155
|
+
this.executionCount++;
|
|
156
|
+
this.totalDuration += duration;
|
|
157
|
+
|
|
158
|
+
span.setAttributes({
|
|
159
|
+
'security.execution.duration': duration,
|
|
160
|
+
'security.execution.success': true,
|
|
161
|
+
});
|
|
162
|
+
span.setStatus({ code: 1 }); // OK
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
success: true,
|
|
166
|
+
result: result.result,
|
|
167
|
+
duration,
|
|
168
|
+
executionId,
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const duration = Date.now() - startTime;
|
|
172
|
+
|
|
173
|
+
span.recordException(error);
|
|
174
|
+
span.setAttributes({
|
|
175
|
+
'security.execution.duration': duration,
|
|
176
|
+
'security.execution.success': false,
|
|
177
|
+
'security.error.message': error.message,
|
|
178
|
+
});
|
|
179
|
+
span.setStatus({ code: 2, message: error.message });
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
error: error.message,
|
|
184
|
+
duration,
|
|
185
|
+
executionId,
|
|
186
|
+
};
|
|
187
|
+
} finally {
|
|
188
|
+
span.end();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get executor statistics
|
|
195
|
+
* @returns {Object}
|
|
196
|
+
*/
|
|
197
|
+
getStats() {
|
|
198
|
+
return {
|
|
199
|
+
type: 'browser',
|
|
200
|
+
config: this.config,
|
|
201
|
+
executionCount: this.executionCount,
|
|
202
|
+
averageDuration: this.executionCount > 0 ? this.totalDuration / this.executionCount : 0,
|
|
203
|
+
activeWorkers: this.workers.size,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Cleanup all workers
|
|
209
|
+
*/
|
|
210
|
+
async cleanup() {
|
|
211
|
+
for (const [executionId, worker] of this.workers.entries()) {
|
|
212
|
+
try {
|
|
213
|
+
worker.terminate();
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// Ignore termination errors
|
|
216
|
+
}
|
|
217
|
+
this.workers.delete(executionId);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sandbox Executor Detector
|
|
3
|
+
* @module security/sandbox/detector
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Automatically detects the best sandbox executor based on:
|
|
7
|
+
* - Node.js version and environment
|
|
8
|
+
* - Available dependencies
|
|
9
|
+
* - Platform capabilities (V8 isolates, Worker threads, etc.)
|
|
10
|
+
* - Security requirements
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { trace } from '@opentelemetry/api';
|
|
14
|
+
|
|
15
|
+
const tracer = trace.getTracer('sandbox-detector');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect runtime environment
|
|
19
|
+
* @returns {Object} Environment info
|
|
20
|
+
*/
|
|
21
|
+
export function detectEnvironment() {
|
|
22
|
+
return tracer.startActiveSpan('detector.detectEnvironment', span => {
|
|
23
|
+
try {
|
|
24
|
+
const env = {
|
|
25
|
+
isNode: typeof process !== 'undefined' && process.versions?.node,
|
|
26
|
+
isBrowser: typeof window !== 'undefined',
|
|
27
|
+
isWorker: typeof WorkerGlobalScope !== 'undefined',
|
|
28
|
+
nodeVersion: process.versions?.node || null,
|
|
29
|
+
v8Version: process.versions?.v8 || null,
|
|
30
|
+
platform: process.platform || 'unknown',
|
|
31
|
+
arch: process.arch || 'unknown',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
span.setAttributes({
|
|
35
|
+
'detector.isNode': env.isNode,
|
|
36
|
+
'detector.isBrowser': env.isBrowser,
|
|
37
|
+
'detector.nodeVersion': env.nodeVersion || 'unknown',
|
|
38
|
+
'detector.platform': env.platform,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
span.setStatus({ code: 1 }); // OK
|
|
42
|
+
return env;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
span.recordException(error);
|
|
45
|
+
span.setStatus({ code: 2, message: error.message });
|
|
46
|
+
throw error;
|
|
47
|
+
} finally {
|
|
48
|
+
span.end();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if isolated-vm is available and working
|
|
55
|
+
* @returns {Promise<boolean>} True if isolated-vm is available
|
|
56
|
+
*/
|
|
57
|
+
export async function checkIsolatedVm() {
|
|
58
|
+
return tracer.startActiveSpan('detector.checkIsolatedVm', async span => {
|
|
59
|
+
try {
|
|
60
|
+
// Try to import isolated-vm
|
|
61
|
+
const ivm = await import('isolated-vm').catch(() => null);
|
|
62
|
+
const available = !!ivm;
|
|
63
|
+
|
|
64
|
+
span.setAttribute('detector.isolatedVm.available', available);
|
|
65
|
+
|
|
66
|
+
if (available) {
|
|
67
|
+
// Quick smoke test
|
|
68
|
+
try {
|
|
69
|
+
const isolate = new ivm.default.Isolate({ memoryLimit: 8 });
|
|
70
|
+
await isolate.dispose();
|
|
71
|
+
span.setAttribute('detector.isolatedVm.working', true);
|
|
72
|
+
span.setStatus({ code: 1 });
|
|
73
|
+
return true;
|
|
74
|
+
} catch (testError) {
|
|
75
|
+
span.recordException(testError);
|
|
76
|
+
span.setAttribute('detector.isolatedVm.working', false);
|
|
77
|
+
span.setStatus({ code: 1 }); // Not an error, just not working
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
span.setStatus({ code: 1 });
|
|
83
|
+
return false;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
span.recordException(error);
|
|
86
|
+
span.setStatus({ code: 2, message: error.message });
|
|
87
|
+
return false;
|
|
88
|
+
} finally {
|
|
89
|
+
span.end();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if Worker threads are available
|
|
96
|
+
* @returns {Promise<boolean>} True if Worker threads are available
|
|
97
|
+
*/
|
|
98
|
+
export async function checkWorkerThreads() {
|
|
99
|
+
return tracer.startActiveSpan('detector.checkWorkerThreads', async span => {
|
|
100
|
+
try {
|
|
101
|
+
const { Worker } = await import('worker_threads').catch(() => ({}));
|
|
102
|
+
const available = !!Worker;
|
|
103
|
+
|
|
104
|
+
span.setAttribute('detector.workerThreads.available', available);
|
|
105
|
+
span.setStatus({ code: 1 });
|
|
106
|
+
|
|
107
|
+
return available;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
span.recordException(error);
|
|
110
|
+
span.setStatus({ code: 2, message: error.message });
|
|
111
|
+
return false;
|
|
112
|
+
} finally {
|
|
113
|
+
span.end();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if vm2 is available (legacy)
|
|
120
|
+
* @returns {Promise<boolean>} True if vm2 is available
|
|
121
|
+
*/
|
|
122
|
+
export async function checkVm2() {
|
|
123
|
+
return tracer.startActiveSpan('detector.checkVm2', async span => {
|
|
124
|
+
try {
|
|
125
|
+
const vm2Module = await import('vm2').catch(() => null);
|
|
126
|
+
const available = !!vm2Module?.VM;
|
|
127
|
+
|
|
128
|
+
span.setAttribute('detector.vm2.available', available);
|
|
129
|
+
span.setAttribute('detector.vm2.deprecated', true);
|
|
130
|
+
span.setStatus({ code: 1 });
|
|
131
|
+
|
|
132
|
+
return available;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
span.recordException(error);
|
|
135
|
+
span.setStatus({ code: 2, message: error.message });
|
|
136
|
+
return false;
|
|
137
|
+
} finally {
|
|
138
|
+
span.end();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Detect best available sandbox executor
|
|
145
|
+
* Priority: isolated-vm > worker > vm2 (deprecated) > browser
|
|
146
|
+
*
|
|
147
|
+
* @param {Object} [options] - Detection options
|
|
148
|
+
* @param {boolean} [options.preferIsolatedVm=true] - Prefer isolated-vm if available
|
|
149
|
+
* @param {boolean} [options.allowVm2=false] - Allow deprecated vm2 (not recommended)
|
|
150
|
+
* @param {boolean} [options.allowBrowser=true] - Allow browser executor
|
|
151
|
+
* @returns {Promise<string>} Executor type ('isolated-vm' | 'worker' | 'vm2' | 'browser')
|
|
152
|
+
*/
|
|
153
|
+
export async function detectBestExecutor(options = {}) {
|
|
154
|
+
return tracer.startActiveSpan('detector.detectBestExecutor', async span => {
|
|
155
|
+
try {
|
|
156
|
+
const { preferIsolatedVm = true, allowVm2 = false, allowBrowser = true } = options;
|
|
157
|
+
|
|
158
|
+
const env = detectEnvironment();
|
|
159
|
+
span.setAttributes({
|
|
160
|
+
'detector.preferIsolatedVm': preferIsolatedVm,
|
|
161
|
+
'detector.allowVm2': allowVm2,
|
|
162
|
+
'detector.allowBrowser': allowBrowser,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Browser environment
|
|
166
|
+
if (env.isBrowser && allowBrowser) {
|
|
167
|
+
span.setAttribute('detector.executor', 'browser');
|
|
168
|
+
span.setStatus({ code: 1 });
|
|
169
|
+
return 'browser';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Node.js environment - check capabilities in priority order
|
|
173
|
+
if (env.isNode) {
|
|
174
|
+
// 1. Try isolated-vm (best security and performance)
|
|
175
|
+
if (preferIsolatedVm && (await checkIsolatedVm())) {
|
|
176
|
+
span.setAttribute('detector.executor', 'isolated-vm');
|
|
177
|
+
span.setStatus({ code: 1 });
|
|
178
|
+
return 'isolated-vm';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 2. Try Worker threads (good isolation, available in Node 12+)
|
|
182
|
+
if (await checkWorkerThreads()) {
|
|
183
|
+
span.setAttribute('detector.executor', 'worker');
|
|
184
|
+
span.setStatus({ code: 1 });
|
|
185
|
+
return 'worker';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 3. Fall back to vm2 if explicitly allowed (deprecated, security issues)
|
|
189
|
+
if (allowVm2 && (await checkVm2())) {
|
|
190
|
+
span.setAttribute('detector.executor', 'vm2');
|
|
191
|
+
span.setAttribute('detector.warning', 'Using deprecated vm2 - security issues present');
|
|
192
|
+
span.setStatus({ code: 1 });
|
|
193
|
+
console.warn(
|
|
194
|
+
'[SECURITY WARNING] Using deprecated vm2 sandbox executor. ' +
|
|
195
|
+
'This has known security vulnerabilities. ' +
|
|
196
|
+
'Consider upgrading Node.js or installing isolated-vm.'
|
|
197
|
+
);
|
|
198
|
+
return 'vm2';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// No suitable executor found
|
|
203
|
+
const error = new Error(
|
|
204
|
+
'No suitable sandbox executor available. ' +
|
|
205
|
+
'Install isolated-vm or upgrade to Node.js 12+ for Worker threads.'
|
|
206
|
+
);
|
|
207
|
+
span.recordException(error);
|
|
208
|
+
span.setStatus({ code: 2, message: error.message });
|
|
209
|
+
throw error;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
span.recordException(error);
|
|
212
|
+
span.setStatus({ code: 2, message: error.message });
|
|
213
|
+
throw error;
|
|
214
|
+
} finally {
|
|
215
|
+
span.end();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Create executor instance based on type
|
|
222
|
+
* @param {string} executorType - Type of executor to create
|
|
223
|
+
* @param {Object} [config] - Executor configuration
|
|
224
|
+
* @returns {Promise<Object>} Executor instance
|
|
225
|
+
*/
|
|
226
|
+
export async function createExecutor(executorType, config = {}) {
|
|
227
|
+
return tracer.startActiveSpan('detector.createExecutor', async span => {
|
|
228
|
+
try {
|
|
229
|
+
span.setAttribute('detector.executorType', executorType);
|
|
230
|
+
|
|
231
|
+
let ExecutorClass;
|
|
232
|
+
|
|
233
|
+
switch (executorType) {
|
|
234
|
+
case 'isolated-vm':
|
|
235
|
+
ExecutorClass = (await import('./isolated-vm-executor.mjs')).IsolatedVmExecutor;
|
|
236
|
+
break;
|
|
237
|
+
|
|
238
|
+
case 'worker':
|
|
239
|
+
ExecutorClass = (await import('./worker-executor.mjs')).WorkerExecutor;
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'vm2':
|
|
243
|
+
console.warn('[DEPRECATION] vm2 executor is deprecated and has security vulnerabilities');
|
|
244
|
+
ExecutorClass = (await import('./vm2-executor.mjs')).Vm2Executor;
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'browser':
|
|
248
|
+
ExecutorClass = (await import('./browser-executor.mjs')).BrowserExecutor;
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
default:
|
|
252
|
+
throw new Error(`Unknown executor type: ${executorType}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const executor = new ExecutorClass(config);
|
|
256
|
+
span.setStatus({ code: 1 });
|
|
257
|
+
return executor;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
span.recordException(error);
|
|
260
|
+
span.setStatus({ code: 2, message: error.message });
|
|
261
|
+
throw error;
|
|
262
|
+
} finally {
|
|
263
|
+
span.end();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Auto-detect and create best executor
|
|
270
|
+
* @param {Object} [config] - Executor configuration
|
|
271
|
+
* @param {Object} [detectionOptions] - Detection options
|
|
272
|
+
* @returns {Promise<Object>} Executor instance
|
|
273
|
+
*/
|
|
274
|
+
export async function createBestExecutor(config = {}, detectionOptions = {}) {
|
|
275
|
+
return tracer.startActiveSpan('detector.createBestExecutor', async span => {
|
|
276
|
+
try {
|
|
277
|
+
const executorType = await detectBestExecutor(detectionOptions);
|
|
278
|
+
span.setAttribute('detector.selectedExecutor', executorType);
|
|
279
|
+
|
|
280
|
+
const executor = await createExecutor(executorType, config);
|
|
281
|
+
span.setStatus({ code: 1 });
|
|
282
|
+
|
|
283
|
+
return executor;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
span.recordException(error);
|
|
286
|
+
span.setStatus({ code: 2, message: error.message });
|
|
287
|
+
throw error;
|
|
288
|
+
} finally {
|
|
289
|
+
span.end();
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Get executor capabilities
|
|
296
|
+
* @param {string} executorType - Executor type
|
|
297
|
+
* @returns {Object} Capabilities object
|
|
298
|
+
*/
|
|
299
|
+
export function getExecutorCapabilities(executorType) {
|
|
300
|
+
const capabilities = {
|
|
301
|
+
'isolated-vm': {
|
|
302
|
+
memoryIsolation: 'full',
|
|
303
|
+
cpuIsolation: 'full',
|
|
304
|
+
asyncSupport: true,
|
|
305
|
+
wasmSupport: true,
|
|
306
|
+
securityLevel: 'high',
|
|
307
|
+
performance: 'high',
|
|
308
|
+
overhead: 'low',
|
|
309
|
+
},
|
|
310
|
+
worker: {
|
|
311
|
+
memoryIsolation: 'partial',
|
|
312
|
+
cpuIsolation: 'partial',
|
|
313
|
+
asyncSupport: true,
|
|
314
|
+
wasmSupport: false,
|
|
315
|
+
securityLevel: 'medium',
|
|
316
|
+
performance: 'medium',
|
|
317
|
+
overhead: 'medium',
|
|
318
|
+
},
|
|
319
|
+
vm2: {
|
|
320
|
+
memoryIsolation: 'weak',
|
|
321
|
+
cpuIsolation: 'none',
|
|
322
|
+
asyncSupport: false,
|
|
323
|
+
wasmSupport: false,
|
|
324
|
+
securityLevel: 'low',
|
|
325
|
+
performance: 'medium',
|
|
326
|
+
overhead: 'low',
|
|
327
|
+
deprecated: true,
|
|
328
|
+
securityIssues: true,
|
|
329
|
+
},
|
|
330
|
+
browser: {
|
|
331
|
+
memoryIsolation: 'partial',
|
|
332
|
+
cpuIsolation: 'partial',
|
|
333
|
+
asyncSupport: true,
|
|
334
|
+
wasmSupport: true,
|
|
335
|
+
securityLevel: 'medium',
|
|
336
|
+
performance: 'low',
|
|
337
|
+
overhead: 'high',
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
return capabilities[executorType] || null;
|
|
342
|
+
}
|