~3 دقیقه مطالعه • بروزرسانی ۹ دی ۱۴۰۴
1. معرفی
ماژول node:vm APIهایی برای کامپایل و اجرای کد جاوااسکریپت در کانتکستهای جداگانه ارائه میدهد. هر کانتکست globalThis مخصوص خود را دارد و کد داخل آن از محیط اصلی جداست. این قابلیت برای اجرای پویا، افزونهها، موتورهای قالب و REPL بسیار کاربردی است.
2. دسترسی به ماژول
const vm = require('node:vm');
3. مفاهیم اصلی: کانتکست و ایزولیشن
Node.js کد را داخل یک V8 Context اجرا میکند. ماژول vm اجازه میدهد کانتکستهای جدید بسازید تا کد در محیطی جدا از برنامهٔ اصلی اجرا شود.
4. ساخت یک کانتکست
const context = { foo: 'hello', console };
vm.createContext(context);
حالا context نقش globalThis را برای کد داخل کانتکست دارد.
5. روشهای اجرای کد
5.1 vm.runInContext()
اجرای کد داخل یک کانتکست موجود.
vm.runInContext('foo += " world!"; console.log(foo)', context);
console.log(context.foo); // "hello world!"
5.2 vm.runInNewContext()
ساخت کانتکست جدید و اجرای کد داخل آن.
const result = vm.runInNewContext('a + b', { a: 10, b: 20 });
console.log(result); // 30
5.3 vm.runInThisContext()
اجرای کد در کانتکست فعلی Node.js — با دسترسی کامل به سیستم. بسیار خطرناک.
vm.runInThisContext('process.exit(1)');
5.4 vm.Script — کامپایل یکبار، اجرا چندبار
برای اجرای تکراری کد با سرعت بالا.
const script = new vm.Script('count += 1; name = "kitty"');
const context = { count: 0 };
vm.createContext(context);
for (let i = 0; i < 1000; i++) {
script.runInContext(context);
}
console.log(context.count); // 1000
6. گزینههای مهم
{
filename: 'my-script.vm',
timeout: 1000,
breakOnSigint: true,
microtaskMode: 'afterEvaluate'
}
7. پشتیبانی از ماژولها (Experimental)
نیازمند --experimental-vm-modules.
const module = new vm.SourceTextModule('export default 42');
await module.link(() => {});
await module.evaluate();
console.log(module.namespace.default); // 42
8. پشتیبانی از import() پویا
با استفاده از importModuleDynamically یا USE_MAIN_CONTEXT_DEFAULT_LOADER.
9. هشدار امنیتی بسیار مهم
vm یک سندباکس امنیتی نیست. هرگز کد غیرقابلاعتماد را با آن اجرا نکنید.
- میتواند به internals دسترسی پیدا کند.
- میتواند حلقهٔ رویداد را قفل کند.
- میتواند از طریق prototype escape خارج شود.
- ایزولیشن واقعی ندارد.
هرگز این کار را نکنید:
vm.runInNewContext(userCode, { require, process });
راهکارهای امنتر
- استفاده از
DONT_CONTEXTIFY - محدود کردن شدید sandbox
- استفاده از timeout
- برای امنیت واقعی: child_process، Worker Threads یا container
10. مثالهای کاربردی
10.1 سندباکس ساده
const sandbox = {
result: null,
console: { log: (msg) => console.log('User:', msg) }
};
vm.createContext(sandbox);
vm.runInContext('result = 2 + 3 * 4', sandbox);
console.log(sandbox.result); // 14
10.2 موتور قالب ساده
function render(template, data) {
const code = `with(data) { return \`${template}\`; }`;
const fn = new vm.Script(code).runInNewContext({ data });
return fn;
}
console.log(render('Hello {{name}}!', { name: 'Ali' }));
10.3 اندازهگیری حافظه
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
.then(info => console.log(info));
11. بهترین شیوهها
- همیشه از
createContext()+runInContext()استفاده کنید. - هرگز
require،processیاglobalرا expose نکنید. - برای کدهای ناشناخته timeout بگذارید.
- برای اجرای تکراری از
vm.Scriptاستفاده کنید. - برای async از
microtaskMode: 'afterEvaluate'استفاده کنید.
نتیجهگیری
ماژول node:vm ابزاری قدرتمند برای اجرای پویا، سیستمهای افزونه، موتورهای قالب و تست ایزولهٔ منطق است. اما نباید برای امنیت استفاده شود. با طراحی درست سندباکس و محدودسازی مناسب، vm میتواند اجرای کنترلشده و منعطف کد را در برنامههای Node.js فراهم کند.
نوشته و پژوهش شده توسط دکتر شاهین صیامی