Skip to content

Commit ced96e3

Browse files
committed
test: add isolated vm test code
1 parent 9a132d9 commit ced96e3

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

src/server/tests/vm/isolated-vm.ts

+335
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import ivm from 'isolated-vm';
2+
import { performance } from 'perf_hooks';
3+
4+
/**
5+
* This file demonstrates the usage of isolated-vm module for creating secure sandboxed environments
6+
* to execute untrusted JavaScript code with memory limits and resource isolation.
7+
*/
8+
9+
// Demo 1: Basic Isolate and Script Execution
10+
async function basicIsolateDemo() {
11+
console.log('\n=== Demo 1: Basic Isolate and Script Execution ===');
12+
13+
// Create a new isolate with a memory limit (in MB)
14+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
15+
16+
// Create a new context within the isolate
17+
const context = await isolate.createContext();
18+
19+
// Compile a simple script
20+
const script = await isolate.compileScript('1 + 2');
21+
22+
// Run the script in the context
23+
const result = await script.run(context);
24+
25+
console.log('Script result:', result);
26+
27+
// Clean up resources
28+
context.release();
29+
script.release();
30+
isolate.dispose();
31+
}
32+
33+
// Demo 2: Passing Values Between Isolate and Host
34+
async function valuePassingDemo() {
35+
console.log('\n=== Demo 2: Passing Values Between Isolate and Host ===');
36+
37+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
38+
const context = await isolate.createContext();
39+
40+
// Get a reference to the global object in the context
41+
const global = context.global;
42+
43+
// Set a value in the isolate
44+
global.setSync('hostValue', 42);
45+
46+
// Create a function in the isolate that uses the value
47+
const script = await isolate.compileScript('hostValue + 100');
48+
const result = await script.run(context);
49+
50+
console.log('Result using host value:', result);
51+
52+
// Get a value from the isolate
53+
const getValueScript = await isolate.compileScript(
54+
'({ data: "Hello from isolate", number: 123 })'
55+
);
56+
const isolateValue = await getValueScript.run(context);
57+
58+
console.log('Value from isolate:', isolateValue);
59+
60+
// Clean up
61+
context.release();
62+
script.release();
63+
getValueScript.release();
64+
isolate.dispose();
65+
}
66+
67+
// Demo 3: Exposing Host Functions to the Isolate
68+
async function exposeFunctionsDemo() {
69+
console.log('\n=== Demo 3: Exposing Host Functions to the Isolate ===');
70+
71+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
72+
const context = await isolate.createContext();
73+
const global = context.global;
74+
75+
// Create a function in the host that we'll expose to the isolate
76+
const logCallback = new ivm.Reference(function (...args: any[]) {
77+
console.log('Isolate logged:', ...args);
78+
});
79+
80+
// Expose the function to the isolate
81+
global.setSync('hostLog', logCallback);
82+
83+
// Create a script that uses the host function
84+
const script = await isolate.compileScript(`
85+
hostLog('Hello from the isolate!');
86+
hostLog('Multiple', 'arguments', 'work too');
87+
'Function called successfully';
88+
`);
89+
90+
const result = await script.run(context);
91+
console.log('Script result:', result);
92+
93+
// Clean up
94+
context.release();
95+
script.release();
96+
logCallback.release();
97+
isolate.dispose();
98+
}
99+
100+
// Demo 4: Memory Limits and Resource Constraints
101+
async function memoryLimitsDemo() {
102+
console.log('\n=== Demo 4: Memory Limits and Resource Constraints ===');
103+
104+
// Create an isolate with a very small memory limit
105+
const isolate = new ivm.Isolate({ memoryLimit: 8 }); // 8MB limit
106+
const context = await isolate.createContext();
107+
108+
try {
109+
// Try to allocate a large array that should exceed the memory limit
110+
const script = await isolate.compileScript(`
111+
const arr = new Array(10000000).fill('memory hog');
112+
'Created large array';
113+
`);
114+
115+
console.log('Attempting to run memory-intensive script...');
116+
const result = await script.run(context, { timeout: 1000 }); // 1 second timeout
117+
console.log('Result:', result);
118+
119+
script.release();
120+
} catch (error) {
121+
console.log('Error caught (expected):', error.message);
122+
} finally {
123+
context.release();
124+
isolate.dispose();
125+
}
126+
}
127+
128+
// Demo 5: Transferable Objects and References
129+
async function transferableDemo() {
130+
console.log('\n=== Demo 5: Transferable Objects and References ===');
131+
132+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
133+
const context = await isolate.createContext();
134+
const global = context.global;
135+
136+
// Create a complex object
137+
const complexObject = {
138+
name: 'Test Object',
139+
values: [1, 2, 3, 4, 5],
140+
nested: {
141+
prop: 'nested property',
142+
},
143+
};
144+
145+
// Transfer the object to the isolate using an external copy
146+
const copy = new ivm.ExternalCopy(complexObject).copyInto();
147+
global.setSync('hostObject', copy);
148+
149+
// Create a script that modifies the object
150+
const script = await isolate.compileScript(`
151+
// Access the object
152+
const obj = hostObject;
153+
154+
// Modify it
155+
obj.values.push(6);
156+
obj.newProp = 'added in isolate';
157+
158+
// Return the modified object
159+
obj;
160+
`);
161+
162+
const result = await script.run(context);
163+
console.log('Modified object from isolate:', result);
164+
165+
// Clean up
166+
context.release();
167+
script.release();
168+
isolate.dispose();
169+
}
170+
171+
// Demo 6: Error Handling and Timeouts
172+
async function errorHandlingDemo() {
173+
console.log('\n=== Demo 6: Error Handling and Timeouts ===');
174+
175+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
176+
const context = await isolate.createContext();
177+
178+
// Script with a syntax error
179+
try {
180+
const scriptWithSyntaxError = await isolate.compileScript(
181+
'function() { invalid syntax }'
182+
);
183+
} catch (error) {
184+
console.log('Caught syntax error during compilation:', error.message);
185+
}
186+
187+
// Script with a runtime error
188+
try {
189+
const scriptWithRuntimeError = await isolate.compileScript(
190+
'undefinedVariable.method()'
191+
);
192+
await scriptWithRuntimeError.run(context);
193+
} catch (error) {
194+
console.log('Caught runtime error during execution:', error.message);
195+
}
196+
197+
// Script with an infinite loop (timeout)
198+
try {
199+
const infiniteLoopScript = await isolate.compileScript('while(true) {}');
200+
console.log('Running infinite loop with timeout...');
201+
await infiniteLoopScript.run(context, { timeout: 500 }); // 500ms timeout
202+
} catch (error) {
203+
console.log('Caught timeout error:', error.message);
204+
} finally {
205+
context.release();
206+
isolate.dispose();
207+
}
208+
}
209+
210+
// Demo 7: Performance Measurement
211+
async function performanceDemo() {
212+
console.log('\n=== Demo 7: Performance Measurement ===');
213+
214+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
215+
const context = await isolate.createContext();
216+
217+
// Create a compute-intensive script
218+
const computeScript = await isolate.compileScript(`
219+
function fibonacci(n) {
220+
if (n <= 1) return n;
221+
return fibonacci(n - 1) + fibonacci(n - 2);
222+
}
223+
224+
fibonacci(20); // Compute-intensive calculation
225+
`);
226+
227+
// Measure execution time
228+
const start = performance.now();
229+
const result = await computeScript.run(context, { timeout: 5000 });
230+
const end = performance.now();
231+
232+
console.log(`Fibonacci result: ${result}`);
233+
console.log(`Execution time: ${(end - start).toFixed(2)}ms`);
234+
235+
// Clean up
236+
context.release();
237+
computeScript.release();
238+
isolate.dispose();
239+
}
240+
241+
// Demo 8: Building a Sandbox Environment
242+
async function sandboxDemo() {
243+
console.log('\n=== Demo 8: Building a Sandbox Environment ===');
244+
245+
const isolate = new ivm.Isolate({ memoryLimit: 128 });
246+
const context = await isolate.createContext();
247+
const global = context.global;
248+
249+
// Set up a minimal console implementation
250+
const consoleMock = {
251+
log: new ivm.Reference(function (...args: any[]) {
252+
console.log('[Sandbox]', ...args);
253+
}),
254+
error: new ivm.Reference(function (...args: any[]) {
255+
console.error('[Sandbox Error]', ...args);
256+
}),
257+
};
258+
259+
// Create a safe setTimeout implementation
260+
const setTimeoutMock = new ivm.Reference(function (
261+
callback: ivm.Reference,
262+
ms: number
263+
) {
264+
// Limit the timeout to prevent abuse
265+
const limitedMs = Math.min(ms, 2000);
266+
267+
setTimeout(() => {
268+
try {
269+
// Call the callback from the isolate
270+
callback.apply(undefined, [], { timeout: 500 });
271+
} catch (error) {
272+
console.error('Error in sandbox setTimeout callback:', error);
273+
} finally {
274+
callback.release();
275+
}
276+
}, limitedMs);
277+
278+
return limitedMs;
279+
});
280+
281+
// Set up the sandbox environment
282+
global.setSync('global', global.derefInto());
283+
global.setSync('console', consoleMock);
284+
global.setSync('setTimeout', setTimeoutMock);
285+
286+
// Run a script in the sandbox
287+
const sandboxScript = await isolate.compileScript(`
288+
console.log('Hello from the sandbox!');
289+
290+
// Test setTimeout
291+
setTimeout(() => {
292+
console.log('Timeout executed in sandbox');
293+
}, 1000);
294+
295+
// Return a value
296+
'Sandbox initialized';
297+
`);
298+
299+
const result = await sandboxScript.run(context, { timeout: 1000 });
300+
console.log('Script result:', result);
301+
302+
// Wait for the setTimeout to complete
303+
await new Promise((resolve) => setTimeout(resolve, 2000));
304+
305+
// Clean up
306+
context.release();
307+
sandboxScript.release();
308+
consoleMock.log.release();
309+
consoleMock.error.release();
310+
setTimeoutMock.release();
311+
isolate.dispose();
312+
}
313+
314+
// Main function to run all demos
315+
async function runIsolatedVMDemos() {
316+
console.log('Starting isolated-vm demos...');
317+
318+
try {
319+
await basicIsolateDemo();
320+
await valuePassingDemo();
321+
await exposeFunctionsDemo();
322+
await memoryLimitsDemo();
323+
await transferableDemo();
324+
await errorHandlingDemo();
325+
await performanceDemo();
326+
await sandboxDemo();
327+
328+
console.log('\nAll demos completed successfully!');
329+
} catch (error) {
330+
console.error('Demo failed with error:', error);
331+
}
332+
}
333+
334+
// Run the demos
335+
runIsolatedVMDemos().catch(console.error);

0 commit comments

Comments
 (0)