VM moduli


ULASHISH

VM moduliga kirish

VM (Virtual Machine) moduli kodni izolyatsiyalangan kontekstlar ichida kompilyatsiya qilish va ishga tushirish imkonini beradi.

Bu quyidagilar uchun foydalidir:

  • Ishonchsiz kodni sinov muhitida xavfsiz ishga tushirish
  • JavaScript kodini dinamik ravishda baholash
  • Plaginlar va kengaytma tizimlarini yaratish
  • Maxsus skript muhitlarini yaratish
  • Kodni izolyatsiyada tekshirish

Ogohlantirish: VM moduli asosiy JavaScript muhitidan izolyatsiyani ta’minlasa-da, u butunlay xavfsiz sinov muhiti emas. Undan ishonchsiz kodni ishlatish uchun yagona xavfsizlik mexanizmi sifatida foydalanmaslik kerak.


VM moduli import qilinmoqda

VM modulidan foydalanish uchun uni Node.js ilovangizga import qilishingiz kerak:

const vm = require('vm');

Asosiy tushunchalar

VM moduli bir nechta asosiy komponentlarga ega:

Komponent Tavsif
Script Turli kontekstlarda bir necha marta bajarilishi mumkin bo‘lgan kompilyatsiya qilingan JavaScript kodi
Context Sinov muhitiga o‘xshash skriptlar bajariladigan izolyatsiya qilingan global obyekt
ContextifiedObject VM konteksti bilan bog‘langan va uning global obyekti sifatida xizmat qiluvchi obyekt


Asosiy foydalanish: JavaScript-ni kontekstda ishga tushirish

VM modulidan foydalanishning eng oddiy usuli kontekstda kodni ishga tushirishdir:

const vm = require('vm');

// Create a context object
const context = { x: 2 };

// Compile and run a script in the context
vm.createContext(context);
vm.runInContext('x = x * 2; y = 10;', context);

// Inspect the modified context
console.log(context); // Outputs: { x: 4, y: 10 }

Ushbu misolda:

  1. Biz x o‘zgaruvchisi bilan kontekst obyektini yaratamiz
  2. Biz ushbu obyektni vm.createContext() yordamida "kontekstlashtiramiz"
  3. Biz ushbu kontekstda x ni o‘zgartiruvchi va y yaratuvchi JavaScript kodini ishlatamiz.
  4. O‘zgarishlar kontekst obyektida aks ettiriladi

VM moduli usullari

Skript usullari

Metod Tavsif
vm.Script(code[, options]) Kompilyatsiya qilingan kodni ifodalovchi yangi skript obyektini yaratadi
script.runInContext(contextObject[, options]) Belgilangan kontekstda tuzilgan kodni ishga tushiradi
script.runInNewContext([contextObject][, options]) Kompilyatsiya qilingan kodni yangi kontekstda ishga tushiradi
script.runInThisContext([options]) Joriy kontekstda tuzilgan kodni ishga tushiradi

Kontekst usullari

Metod Tavsif
vm.createContext([contextObject][, options]) Skriptni bajarish uchun ishlatilishi mumkin bo‘lgan yangi kontekst yaratadi
vm.isContext(object) Obyekt kontekstlanganligini tekshiradi
vm.runInContext(code, contextObject[, options]) Berilgan kontekstda kodni kompilyatsiya qiladi va bajaradi
vm.runInNewContext(code[, contextObject][, options]) Kodni yangi kontekstda kompilyatsiya qiladi va bajaradi
vm.runInThisContext(code[, options]) Joriy kontekstda kodni kompilyatsiya qiladi va bajaradi

Skriptlarni yaratish va kompilyatsiya qilish

Xuddi shu kodni bir necha marta bajarishda yaxshiroq ishlash uchun uni Script klassi yordamida oldindan kompilyatsiya qilishingiz mumkin:

const vm = require('vm');

// Compile the script once
const script = new vm.Script('x += 40; let z = 30;');

// Create multiple contexts
const context1 = { x: 10 };
const context2 = { x: 20 };

// Contextify the objects
vm.createContext(context1);
vm.createContext(context2);

// Run the same script in different contexts
script.runInContext(context1);
script.runInContext(context2);

console.log(context1); // Outputs: { x: 50, z: 30 }
console.log(context2); // Outputs: { x: 60, z: 30 }

Eslatma: Skriptlarni alohida kompilyatsiya qilish, bir xil kodni bir necha marta bajarish kerak bo‘lganda samaraliroq bo‘ladi, chunki tahlil va kompilyatsiya bosqichlari faqat bir marta amalga oshiriladi.


Kodni ishga tushirishning turli usullari

1. runInContext

Oldindan yaratilgan kontekstda kodni ishga tushiradi:

const vm = require('vm');

const context = { value: 10 };
vm.createContext(context);

// Run directly
vm.runInContext('value += 5', context);
console.log(context.value); // 15

// Compile then run
const script = new vm.Script('value *= 2');
script.runInContext(context);
console.log(context.value); // 30

2. runInNewContext

Yangi kontekst yaratadi va unda kodni ishga tushiradi:

const vm = require('vm');

// No need to call createContext first
const context = { value: 10 };
vm.runInNewContext('value += 5; result = value * 2;', context);

console.log(context); // { value: 15, result: 30 }

3. runInThisContext

Joriy V8 kontekstida kodni ishga tushiradi (eval ga o‘xshash, lekin xavfsizroq):

const vm = require('vm');

// Define a variable in the current scope
const locallet = 20;
let result;

// This won't have access to localVar
vm.runInThisContext('result = (typeof locallet !== "undefined" ? locallet : "not defined")');
console.log(result); // 'not defined'

// But it can access globals
global.globallet = 30;
vm.runInThisContext('result = globalVar');
console.log(result); // 30

// Compare with eval, which CAN access local variables
eval('result = localVar');
console.log(result); // 20

Eslatma: runInThisContext eval ga o‘xshaydi, lekin u chaqirilgan doiradagi mahalliy o‘zgaruvchilarga kirish huquqiga ega emas. Bu uni biroz xavfsizroq qiladi, chunki u mahalliy o‘zgaruvchilarga ta’sir qiluvchi kodni kiritish xavfini kamaytiradi.


Vaqt tugashi opsiyasi bilan ishlash

Cheksiz sikllar yoki uzoq davom etadigan skriptlarning oldini olish uchun skriptni bajarish uchun kutish vaqtini belgilashingiz mumkin:

const vm = require('vm');

const context = { result: 0 };
vm.createContext(context);

try {
  // This should timeout after 1000ms (1 second)
  vm.runInContext(`
    let counter = 0;
    while (true) {
      counter++;
      result = counter;
    }
  `, context, { timeout: 1000 });
} catch (err) {
  console.error(`Execution timed out: ${err.message}`);
  console.log(`Results before timeout: counter reached ${context.result}`);
}

Ogohlantirish: Vaqt tugashi opsiyasi bajarilish aniq belgilangan vaqtda to‘xtashiga kafolat bermaydi. Haqiqiy vaqt tugashi biroz farq qilishi mumkin.


Node.js asosiy modullariga kirishni nazorat qilish

Odatiy bo‘lib, VM kontekstlarida ishlaydigan kod Node.js asosiy modullariga kirish huquqiga ega emas. Qaysi modullar mavjudligini boshqarishingiz mumkin:

const vm = require('vm');
const fs = require('fs');

// Create a sandbox with controlled access to core modules
const sandbox = {
  // Allow limited access to console
  console: {
    log: console.log,
    error: console.error
  },
  
  // Provide controlled access to fs module
  fs: {
    readFileSync: fs.readFileSync
  },
  
  // Custom utility
  util: {
    add: (a, b) => a + b,
    multiply: (a, b) => a * b
  },
  
  // No access to process, child_process, etc.
};

vm.createContext(sandbox);

// Run code with limited access
try {
  vm.runInContext(`
    // We can use the allowed methods
    console.log('Running in sandbox');
    console.log('2 + 3 =', util.add(2, 3));
    
    // Try to read a safe file
    try {
      const content = fs.readFileSync('example.txt', 'utf8');
      console.log('File content:', content);
    } catch (err) {
      console.error('File read error:', err.message);
    }
    
    // Try to access process (should fail)
    try {
      console.log('Process info:', process.version);
    } catch (err) {
      console.error('Cannot access process:', err.message);
    }
  `, sandbox);
} catch (err) {
  console.error('Sandbox execution failed:', err);
}

Ogohlantirish: Ayrim modullarga kirishni cheklashingiz mumkin bo‘lsa-da, bu yondashuv to‘liq xavfsiz emas. Qat’iy tajovuzkor hali ham sinov qutisidan qochish yo‘llarini topishi mumkin. Haqiqiy xavfsiz sandboxing uchun qo‘shimcha izolyatsiyalash usullarini yoki maxsus kutubxonalarni ko‘rib chiqing.


Oddiy shablon dvigatelini yaratish

VM moduli oddiy shablon mexanizmini yaratish uchun ishlatilishi mumkin:

const vm = require('vm');

function renderTemplate(template, data) {
  // Create template function - replace {{ let }} with values
  const templateScript = `
    function template(data) {
      let output = \`${template.replace(/\{\{\s*(\w+)\s*\}\}/g, '${data.$1}')}\`;
      return output;
    }
    template(data);
  `;
  
  // Create a context with the data
  const context = { data };
  vm.createContext(context);
  
  // Execute the template function
  return vm.runInContext(templateScript, context);
}

// Example usage
const template = `
<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ title }}</h1>
  <p>Welcome, {{ name }}!</p>
  <p>Today is {{ date }}</p>
</body>
</html>
`;

const data = {
  title: 'My Template Page',
  name: 'User',
  date: new Date().toLocaleDateString()
};

const rendered = renderTemplate(template, data);
console.log(rendered);

Eslatma: Ushbu misol oddiy foydalanish misolini ko‘rsatsa-da, Rulda yoki EJS kabi ishlab chiqarish shablonlari yanada mustahkam va xavfsizroq. Agar foydalanuvchi ma’lumotlari to‘g‘ri o‘tkazib yuborilmasa, bu misol in’ektsiya hujumlariga qarshi himoyasiz.


Plagin tizimini yaratish

VM moduli plaginlar alohida yuklanishi va bajarilishi mumkin bo‘lgan plagin tizimlarini yaratish uchun foydalidir:

const vm = require('vm');
const fs = require('fs');
const path = require('path');

class PluginSystem {
  constructor() {
    this.plugins = new Map();
    this.api = {
      version: '1.0.0',
      registerHook: this.registerHook.bind(this),
      utils: {
        add: (a, b) => a + b,
        multiply: (a, b) => a * b,
        formatDate: (date) => new Date(date).toLocaleDateString()
      }
    };
    
    this.hooks = {
      init: [],
      process: [],
      shutdown: []
    };
  }
  
  // Register a plugin hook
  registerHook(hookName, callback) {
    if (this.hooks[hookName]) {
      this.hooks[hookName].push(callback);
      console.log(`Registered ${hookName} hook`);
    } else {
      console.error(`Invalid hook name: ${hookName}`);
    }
  }
  
  // Load a plugin from file
  loadPlugin(pluginName, pluginCode) {
    try {
      console.log(`Loading plugin: ${pluginName}`);
      
      // Create a sandbox for this plugin
      const sandbox = {
        console: {
          log: (msg) => console.log(`[${pluginName}] ${msg}`),
          error: (msg) => console.error(`[${pluginName}] ${msg}`)
        },
        setTimeout,
        clearTimeout,
        api: this.api
      };
      
      // Create context and run the plugin code
      const context = vm.createContext(sandbox);
      vm.runInContext(pluginCode, context);
      
      // Store the loaded plugin
      this.plugins.set(pluginName, {
        name: pluginName,
        sandbox
      });
      
      console.log(`Successfully loaded plugin: ${pluginName}`);
    } catch (err) {
      console.error(`Error loading plugin ${pluginName}:`, err.message);
    }
  }
  
  // Run all hooks of a specific type
  async runHooks(hookName, data) {
    console.log(`Running ${hookName} hooks...`);
    
    for (const hook of this.hooks[hookName]) {
      try {
        const result = await hook(data);
        console.log(`Hook result:`, result);
      } catch (err) {
        console.error(`Error in ${hookName} hook:`, err.message);
      }
    }
  }
  
  // Load all plugins from a directory
  loadPluginsFromDirectory(directory) {
    try {
      const files = fs.readdirSync(directory);
      
      for (const file of files) {
        if (file.endsWith('.js')) {
          const pluginName = path.basename(file, '.js');
          const pluginPath = path.join(directory, file);
          const pluginCode = fs.readFileSync(pluginPath, 'utf8');
          
          this.loadPlugin(pluginName, pluginCode);
        }
      }
    } catch (err) {
      console.error('Error loading plugins directory:', err.message);
    }
  }
  
  // Run the plugin system
  async run(data) {
    await this.runHooks('init', data);
    await this.runHooks('process', data);
    await this.runHooks('shutdown', data);
  }
}

// Example plugin code (normally this would be in a separate file)
const examplePlugin = `
// Register initialization hook
api.registerHook('init', async (data) => {
  console.log('Plugin initializing with data:', data);
  return 'Initialization complete';
});

// Register processing hook
api.registerHook('process', async (data) => {
  console.log('Processing data');
  return {
    processed: true,
    sum: api.utils.add(data.x, data.y),
    product: api.utils.multiply(data.x, data.y),
    date: api.utils.formatDate(new Date())
  };
});

// Register shutdown hook
api.registerHook('shutdown', async () => {
  console.log('Plugin shutting down');
  return 'Shutdown complete';
});

console.log('Plugin loaded with API version', api.version);
`;

// Create and run the plugin system
(async () => {
  const system = new PluginSystem();
  
  // Load plugins
  system.loadPlugin('example', examplePlugin);
  
  // You could also load from a directory
  // system.loadPluginsFromDirectory('./plugins');
  
  // Run the system
  await system.run({ x: 5, y: 10 });
})();

Eng yaxshi amaliyotlar va xavfsizlik masalalari

Xavfsizlik bo‘yicha eng yaxshi amaliyotlar

  1. Xavfsizlik uchun faqat VM moduliga ishonmang: Ishonchsiz kod uchun qo‘shimcha xavfsizlik choralaridan foydalaning.
  2. Resurslarni cheklash: Bajarilgan kod uchun kutish vaqti va xotira chegaralarini o‘rnating.
  3. Kirishni boshqarish: Sinov qutisiga faqat kerakli funksiyalarni taqdim eting.
  4. Kirishlarni tekshirish: VMda qayta ishlashdan oldin barcha kirishlarni diqqat bilan tekshiring.
  5. Jarayonlarni izolyatsiya qilishni ko‘rib chiqing: Yuqori darajadagi xavfsizlik uchun ishonchsiz kodni alohida jarayonlar yoki konteynerlarda ishlating.

Ishlashning eng yaxshi amaliyotlari

  1. Skriptlarni bir marta kompilyatsiya qiling: Bir necha marta bajariladigan kod uchun new vm.Script() dan foydalaning.
  2. Kontekstlarni qayta ishlatish: Yangi kontekstlarni yaratish qimmatga tushadi, shuning uchun iloji boricha qayta foydalaning.
  3. Kontekst hajmini cheklash: Ishlashni yaxshilash uchun kontekstlarni kichikroq tuting.
  4. Katta ma’lumotlar bilan ehtiyot bo‘ling: Katta ma’lumotlar tuzilmalarini kontekstlar o‘rtasida o‘tkazish samarasiz bo‘lishi mumkin.

VM moduli va eval()

VM moduli eval() dan foydalanishga nisbatan bir qancha afzalliklarni beradi:

Xususiyat VM moduli eval()
Mahalliy o‘zgaruvchilarga kirish Yo‘q (runInThisContext bilan) Ha
Izolyatsiya Better (separate contexts) None (same context)
Xavfsizlik Better (controlled context) Yomonroq (hamma narsaga kira oladi)
Takroriy bajarish uchun ishlash Yaxshiroq (oldindan kompilyatsiya qilish mumkin) Yomonroq (har safar kompilyatsiya qiladi)
Amalga oshirish ustidan nazorat Ko‘proq (timeout’lar va boshqalar) Ozroq

VM modulining cheklovlari

  1. To‘liq sinov muhiti emas: VM kontekstlari alohida jarayonlar kabi haqiqiy izolyatsiyani ta’minlamaydi.
  2. Protsessor yoki xotira cheklovlari yo‘q: Resursdan foydalanishni bevosita cheklab bo‘lmaydi (faqat vaqt tugashi mavjud).
  3. Prototipning ifloslanish xavfi: VM kontekstidagi kod hali ham JavaScript prototiplarini o‘zgartirishi mumkin.
  4. Sinxron bajarish: Ishlayotgan kod voqealar siklini bloklaydi (agar siz uni ishchi ipda ishlatmasangiz).
  5. Nosozliklarni tuzatish muammolari: VM kontekstlarida ishlaydigan kodni tuzatish qiyin bo‘lishi mumkin.

Ogohlantirish: Muhim xavfsizlik ilovalari uchun child_process moduli, konteynerlar yoki vm2 kabi ixtisoslashgan kutubxonalar bilan alohida jarayonlar kabi mustahkamroq sinov muhiti yechimlaridan foydalaning.


Xulosa

Node.js VM moduli ajratilgan V8 kontekstlarida JavaScript kodini bajarish usulini taqdim etadi. Quyidagilar uchun foydalidir:

  • Ba’zi darajadagi izolyatsiya bilan dinamik ravishda kodni ishga tushirish
  • Xavfsiz kengaytirilishi mumkin bo‘lgan plagin tizimlarini yaratish
  • Shablon dvigatellari va skript muhitlarini yaratish
  • Kodni boshqariladigan kontekstlarda sinovdan o‘tkazish

Ishonchsiz kodni ishga tushirish uchun to‘liq xavfsizlik yechimi bo‘lmasa-da, VM moduli eval() ga qaraganda ko‘proq izolyatsiyani taklif qiladi va Node.js ilovalarida JavaScript-ni baholash uchun qimmatli vositadir.


Kengaytirilgan kontekstni boshqarish

Maxsus global va modullar yordamida murakkab VM kontekstlarini qanday yaratish va boshqarishni bilib oling:

1. Global o‘zgaruvchilar bilan maxsus kontekst yaratish

const vm = require('vm');
const util = require('util');

// Create a custom context with specific global variables
const context = {
  console: {
    log: (...args) => {
      // Custom console.log implementation
      process.stdout.write('Custom Log: ' + util.format(...args) + '\n');
    },
    error: console.error,
    warn: console.warn,
    info: console.info
  },
  // Add custom utilities
  utils: {
    formatDate: () => new Date().toISOString(),
    generateId: () => Math.random().toString(36).substr(2, 9)
  },
  // Add a safe require function
  require: (moduleName) => {
    const allowedModules = ['path', 'url', 'util'];
    if (!allowedModules.includes(moduleName)) {
      throw new Error(`Module '${moduleName}' is not allowed`);
    }
    return require(moduleName);
  }
};

// Contextify the object
vm.createContext(context);

// Run code in the custom context
const code = `
  console.log('Current time:', utils.formatDate());
  console.log('Generated ID:', utils.generateId());
  
  try {
    const fs = require('fs'); // This will throw an error
  } catch (err) {
    console.error('Security error:', err.message);
  }
  
  // This will work as it's an allowed module
  const path = require('path');
  console.log('Current directory:', path.dirname('/path/to/file.txt'));
`;

try {
  vm.runInContext(code, context, { filename: 'custom-context.js' });
} catch (err) {
  console.error('Script execution failed:', err);
}

2. VMda modul tizimi

VM kontekstida oddiy modul tizimini joriy qiling:

const vm = require('vm');
const fs = require('fs');
const path = require('path');

class VMModuleSystem {
  constructor(basePath = '.') {
    this.basePath = path.resolve(basePath);
    this.cache = new Map();
    this.context = vm.createContext({
      module: { exports: {} },
      exports: {},
      console: console,
      require: this.require.bind(this),
      __dirname: this.basePath,
      __filename: path.join(this.basePath, 'main.js')
    });
  }
  
  require(modulePath) {
    // Handle core modules
    if (require.resolve.paths(modulePath) === null) {
      return require(modulePath);
    }
    
    // Resolve the module path
    const resolvedPath = this.resolveModule(modulePath);
    
    // Check cache
    if (this.cache.has(resolvedPath)) {
      return this.cache.get(resolvedPath).exports;
    }
    
    // Create new module
    const module = { exports: {} };
    this.cache.set(resolvedPath, module);
    
    try {
      // Read and execute the module
      const code = fs.readFileSync(resolvedPath, 'utf8');
      const wrapper = `(function(module, exports, require, __dirname, __filename) {${code}\n})`;
      
      const script = new vm.Script(wrapper, {
        filename: resolvedPath,
        lineOffset: 0,
        displayErrors: true
      });
      
      const localRequire = (path) => this.require(path);
      localRequire.resolve = (request) => this.resolveModule(request, resolvedPath);
      
      script.runInNewContext({
        module: module,
        exports: module.exports,
        require: localRequire,
        __dirname: path.dirname(resolvedPath),
        __filename: resolvedPath
      });
      
      return module.exports;
    } catch (err) {
      this.cache.delete(resolvedPath);
      throw err;
    }
  }
  
  resolveModule(request, parentPath) {
    try {
      // Try to resolve as a file
      if (request.startsWith('./') || request.startsWith('../')) {
        const resolved = path.resolve(path.dirname(parentPath || this.basePath), request);
        
        // Try with .js extension
        try {
          const stats = fs.statSync(resolved + '.js');
          if (stats.isFile()) return resolved + '.js';
        } catch (e) {}
        
        // Try as directory with index.js
        try {
          const indexPath = path.join(resolved, 'index.js');
          const stats = fs.statSync(indexPath);
          if (stats.isFile()) return indexPath;
        } catch (e) {}
        
        // Try as file without extension
        try {
          const stats = fs.statSync(resolved);
          if (stats.isFile()) return resolved;
        } catch (e) {}
      }
      
      // Try to resolve as a module
      try {
        return require.resolve(request);
      } catch (e) {
        throw new Error(`Cannot find module '${request}'`);
      }
    } catch (err) {
      throw new Error(`Cannot find module '${request}': ${err.message}`);
    }
  }
  
  runFile(filePath) {
    const absolutePath = path.resolve(this.basePath, filePath);
    return this.require(absolutePath);
  }
}

// Example usage
const moduleSystem = new VMModuleSystem(__dirname);

try {
  // This will execute the file in the VM with the custom module system
  moduleSystem.runFile('example-module.js');
} catch (err) {
  console.error('Module execution failed:', err);
}

Xavfsizlik bo‘yicha eng yaxshi amaliyotlar

VM modulidan foydalanganda xavfsizlik birinchi o‘rinda turishi kerak. Mana bir nechta eng yaxshi amaliyotlar:

const vm = require('vm');
const { execSync } = require('child_process');

// UNSAFE: Directly executing untrusted code
function unsafeEval(code) {
  // This is dangerous as it has access to the entire Node.js environment
  return vm.runInThisContext(code);
}

// SAFER: Isolated context with limited access
function safeEval(code, timeout = 1000) {
  // Create a context with only the necessary globals
  const context = {
    console: {
      log: console.log,
      error: console.error
    },
    // Add safe utilities
    Math: Object.create(null),
    JSON: {
      parse: JSON.parse,
      stringify: JSON.stringify
    },
    // Add a safe setTimeout with limits
    setTimeout: (fn, delay) => {
      if (delay > 1000) delay = 1000; // Cap delay at 1 second
      return setTimeout(fn, delay);
    }
  };
  
  // Copy safe methods from Math
  Object.getOwnPropertyNames(Math)
    .filter(prop => typeof Math[prop] === 'function')
    .forEach(prop => {
      context.Math[prop] = Math[prop];
    });
  
  // Create the context without prototype access
  const sandbox = vm.createContext(context, {
    name: 'sandbox',
    codeGeneration: {
      strings: false,
      wasm: false
    }
  });
  
  // Run the code with a timeout
  try {
    const script = new vm.Script(`
      (function() {
        "use strict";
        ${code}
      })();
    `, {
      filename: 'sandbox.js',
      lineOffset: 0,
      displayErrors: true,
      timeout: timeout,
      microtaskMode: 'afterEvaluate'
    });
    
    return script.runInContext(sandbox, { timeout });
  } catch (err) {
    console.error('Script execution failed:', err.message);
    throw new Error('Script execution failed');
  }
}

// Example of safe evaluation
try {
  const result = safeEval(`
    function add(a, b) { return a + b; }
    add(2, 3);
  `);
  console.log('Safe evaluation result:', result); // Outputs: 5
  
  // This will be caught by our safe evaluator
  safeEval('process.exit(1)');
} catch (err) {
  console.error('Caught error:', err.message);
}

// Example of security risks
console.log('\nTesting security risks:');

try {
  console.log('1. Accessing process:');
  safeEval('process.versions.node');
} catch (err) {
  console.log('✓ Blocked access to process object');
}

try {
  console.log('2. Infinite loop:');
  safeEval('while(true){}');
} catch (err) {
  console.log('✓ Caught infinite loop with timeout');
}

try {
  console.log('3. Prototype pollution:');
  safeEval('({}).constructor.prototype.polluted = true');
  console.log('✓ Blocked prototype pollution');
} catch (err) {
  console.log('✓ Blocked prototype pollution');
}

Muhim: VM moduli xavfsizlik chegarasi emas. Haqiqatan ham ishonchsiz kodni ishlatish uchun Docker, AWS Lambda yoki Google Cloud Functions kabi maxsus sandboxing yechimlaridan foydalanishni o‘ylab ko‘ring.


Ishlashni optimallashtirish

Ushbu texnikalar yordamida VM unumdorlikni optimallashtiring:

const vm = require('vm');
const { performance, PerformanceObserver } = require('perf_hooks');

// 1. Compile once, run many times
const expensiveCalculation = new vm.Script(`
  function calculate(n) {
    let result = 0;
    for (let i = 0; i < n; i++) {
      result += Math.sqrt(i) * Math.PI;
    }
    return result;
  }
  
  // Return the function reference
  calculate;
`);

// Create a context
const context = { Math };
vm.createContext(context);

// Run once to get the function
const calculate = expensiveCalculation.runInContext(context);

// Now we can call the function multiple times without recompiling
console.log('Result (n=1000):', calculate(1000));
console.log('Result (n=2000):', calculate(2000));

// 2. Use code caching for better performance
const cache = new Map();

function compileWithCache(code, filename) {
  if (cache.has(code)) {
    console.log(`Using cached script for ${filename}`);
    return cache.get(code);
  }
  
  console.log(`Compiling script for ${filename}`);
  const script = new vm.Script(code, {
    filename,
    cachedData: null, // Will be populated on first run
    produceCachedData: true
  });
  
  cache.set(code, script);
  return script;
}

// 3. Measure performance
function measurePerformance() {
  const obs = new PerformanceObserver((items) => {
    const entry = items.getEntries()[0];
    console.log(`\nExecution time for ${entry.name}: ${entry.duration.toFixed(2)}ms`);
    performance.clearMarks();
  });
  obs.observe({ entryTypes: ['measure'] });
  
  // Test with different script sizes
  const smallScript = new vm.Script('let sum = 0; for (let i = 0; i < 1000; i++) sum += i; return sum;');
  const largeScript = new vm.Script(`
    function processData(data) {
      return data.map(x => ({
        ...x,
        processed: true,
        timestamp: Date.now(),
        hash: require('crypto').createHash('md5').update(JSON.stringify(x)).digest('hex')
      }));
    }
    
    // Process sample data
    const data = Array(1000).fill(null).map((_, i) => ({ id: i, value: Math.random() }));
    return processData(data);
  `);
  
  // Measure execution
  performance.mark('small-start');
  smallScript.runInThisContext();
  performance.mark('small-end');
  
  performance.mark('large-start');
  largeScript.runInThisContext();
  performance.mark('large-end');
  
  performance.measure('Small script execution', 'small-start', 'small-end');
  performance.measure('Large script execution', 'large-start', 'large-end');
}

// Run performance test
measurePerformance();

// 4. Reuse contexts for better performance
function createOptimizedContext() {
  const context = {
    // Only include what's necessary
    console: {
      log: console.log,
      error: console.error
    },
    // Add required globals
    setTimeout,
    clearTimeout,
    // Add custom utilities
    utils: {
      formatNumber: n => new Intl.NumberFormat().format(n),
      formatDate: d => d.toISOString()
    }
  };
  
  // Create context once
  vm.createContext(context);
  return context;
}

// Reuse the same context for multiple scripts
const sharedContext = createOptimizedContext();

// Run multiple scripts with the same context
function runWithSharedContext(code) {
  try {
    const script = new vm.Script(code);
    return script.runInContext(sharedContext);
  } catch (err) {
    console.error('Script execution failed:', err);
    throw err;
  }
}

// Example usage
const script1 = 'console.log("Script 1:", utils.formatNumber(1234567.89));';
const script2 = 'console.log("Script 2:", utils.formatDate(new Date()));';

runWithSharedContext(script1);
runWithSharedContext(script2);

Ishlash bo‘yicha maslahatlar:

  1. Iloji bo‘lsa, qayta kompilyatsiya qilish xarajatlarini oldini olish uchun Skriptlarni oldindan kompilyatsiya qiling
  2. Har bir ijro uchun yangilarini yaratish o‘rniga kontekstlardan qayta foydalaning
  3. Kontekst hajmini minimallashtiring Faqat kerakli globallarni qo‘shish orqali
  4. Tez-tez bajariladigan skriptlar uchun Kod keshlashdan foydalaning
  5. To‘siqlarni aniqlash uchun Umumiylikni kuzatib boring
  6. Protsessor talab qiladigan operatsiyalar uchun ishchi iplarini ko‘rib chiqing



W3Schools Pathfinder

Yutuqlaringizni kuzating – bu bepul!