Объедините NodeJS Fibers + VM Sandbox

Я хочу запустить ненадежный код в Node, который может выглядеть так:

for (var i = 0; i < 5; i++){
    green_led(1);
    sleep(500);
    green_led(0);
    sleep(500);
}

Используя Fibers, я получил синхронное поведение, работающее, как и ожидалось:

var Fiber = require('fibers');

function sleep(ms){
    var fiber = Fiber.current;
    setTimeout(function(){ fiber.run(); }, ms);
    Fiber.yield();
}
function green_led(active){
    //...
}
Fiber(function(){
    for (var i = 0; i < 5; i++){
        green_led(1);
        sleep(500);
        green_led(0);
        sleep(500);
    }
}).run();

Сложность заключается в том, как песочить код. Необходимость использовать волокна делает его действительно сложным; Я не совсем уверен, как начать. Как я могу получить указанную выше песочницу с помощью vm2? Например, следующее явно не будет работать:

var code = "\
for (var i = 0; i < 5; i++){\
    green_led(1);\
    sleep(500);\
    green_led(0);\
    sleep(500);\
}\
";
function sleep(ms){
    var fiber = Fiber.current;
    setTimeout(function(){ fiber.run(); }, ms);
    Fiber.yield();
}
function green_led(active){
    //...
}
Fiber(function(){
    vm.run(code);
}).run();

(Это не сработает, потому что green_led и sleep не видны изолированному коду на виртуальной машине).

Как это сделать? Либо...

  1. Возможно, все должно запускаться внутри виртуальной машины, включая Fibers и реализацию green_led и т. д.?
  2. Или было бы лучше, чтобы код, запускаемый виртуальной машиной, был минимальным, а вместо этого каким-то образом добавлялся в белый список/прокси green_led и sleep? Не совсем легко с серым веществом, достаточно сложно понять, как вообще работают Волокна!

person CL22    schedule 09.12.2016    source источник


Ответы (1)


Это на самом деле довольно прямолинейно.

var Fiber = require('fibers');

const {VM} = require('vm2');
const vm = new VM({
    sandbox: {
        green_led: green_led,
        sleep: sleep
    }
});

function sleep(ms){
    var fiber = Fiber.current;
    setTimeout(function(){ fiber.run(); }, ms);
    Fiber.yield();
}
function green_led(active){
    //...
}

vm.run(
    `Fiber(function(){
        for (var i = 0; i < 5; i++){
            green_led(1);
            sleep(500);
            green_led(0);
            sleep(500);
        }
    }).run()`
);

Приведенный выше подход передает ссылку на Fiber и другие функции sleep и green_led через объект песочницы. Это можно сделать другими способами. Например, sleep и green_led могут быть определены в строке, переданной в vm.run(), а виртуальная машина сама может include fibers вот так:

const {NodeVM} = require('vm2');
var vm = new NodeVM({
    require: {
        external: true,
    }
});
vm.run(
    `
    var Fiber = require("fibers");
    function sleep(ms){
        var fiber = Fiber.current;
        setTimeout(function(){ fiber.run(); }, ms);
        Fiber.yield();
    }
    function green_led(active){
        //...
    }
    Fiber(function(){
        for (var i = 0; i < 5; i++){
            green_led(1);
            sleep(500);
            green_led(0);
            sleep(500);
        }
    }).run()`
);

В соответствии с документацией обратите внимание на разницу между VM и NodeVM. Из двух вышеупомянутых методов только первый сможет использовать функцию тайм-аута. Также второй способ не застрахован от while (true) {} и т.д.

person CL22    schedule 10.12.2016