Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/powershell/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js vm:如何取消Script.runInNewContext()?_Node.js_V8 - Fatal编程技术网

Node.js vm:如何取消Script.runInNewContext()?

Node.js vm:如何取消Script.runInNewContext()?,node.js,v8,Node.js,V8,我想使用vm模块作为运行外部代码的安全方式。它工作得很好,但还有一个问题: var UNKNOWN_CODE = "while(true){}"; var vm = require("vm"); var obj = {}; var ctx = vm.createContext(obj); var script = vm.createScript(UNKNOWN_CODE); script.runInNewContext(ctx); console.log("finished"); //

我想使用
vm
模块作为运行外部代码的安全方式。它工作得很好,但还有一个问题:

var UNKNOWN_CODE = "while(true){}";

var vm = require("vm");

var obj = {};
var ctx = vm.createContext(obj);

var script = vm.createScript(UNKNOWN_CODE);

script.runInNewContext(ctx);

console.log("finished"); //never executed
是否有办法取消执行(例如,如果执行时间超过5秒)


提前谢谢

您需要在单独的进程中运行它,例如:

master.js:

var cluster = require('cluster');

cluster.setupMaster({
  exec : "runner.js",
  args : process.argv.slice(2),
  silent : false
});
//This will be fired when the forked process becomes online
cluster.on( "online", function(worker) {
    var timer = 0;

    worker.on( "message", function(msg) {
        clearTimeout(timer); //The worker responded in under 5 seconds, clear the timeout
        console.log(msg);
        worker.destroy(); //Don't leave him hanging 

    });
    timer = setTimeout( function() {
        worker.destroy(); //Give it 5 seconds to run, then abort it
        console.log("worker timed out");
    }, 5000);

    worker.send( 'while(true){}' ); //Send the code to run for the worker
});
cluster.fork();
runner.js:

//The runner.js is ran in a separate process and just listens for the message which contains code to be executed
process.on('message', function( UNKNOWN_CODE ) {

    var vm = require("vm");

    var obj = {};
    var ctx = vm.createContext(obj);

    var script = vm.createScript(UNKNOWN_CODE);

    script.runInNewContext(ctx);

    process.send( "finished" ); //Send the finished message to the parent process
});
要运行此示例,请将这些文件放在同一文件夹中,并定向到该文件夹,然后运行

node master.js
5秒后,您应该会看到“工作人员超时”消息。如果将其更改为
'while(false){}
,工作人员将立即执行代码,您应该看到
“finished”

您可能需要检查一下。不幸的是,它还没有更新到0.8.x

然而,正如@Esailija所提到的,没有办法安全地运行外部代码,除非它在另一个进程中

var Threads = require('threads_a_gogo');

var t = Threads.create();
t.eval("while(true) { console.log('.'); }");

setTimeout(function() {
  t.destroy();
  console.log('finished');
}, 1000);

是的,这现在是可能的,因为我向节点
vm
模块添加了
timeout
参数支持。您只需将毫秒超时值传递给
runInNewContext()
,如果代码未在指定的时间内完成执行,它将引发异常

注意,这并不意味着运行不可信代码的任何类型的安全模型。这只允许您对您信任或安全的代码进行超时

var vm = require("vm");

try {
    vm.runInNewContext("while(true) {}", {}, "loop", 1000);
} catch (e) {
    // Exception thrown after 1000ms
}

console.log("finished"); // Will now be executed
正是您所期望的:

$ time ./node test.js
finished

real    0m1.069s
user    0m1.047s
sys     0m0.017s
您可以将“脚本断路器”嵌入未知的\u代码中。比如:

;setTimeout(function() { throw new Error("Execution time limit reached!") }, 2000);
所以,整个事情看起来是这样的:

var UNKNOWN_CODE = "while(true){}";

var scriptBreaker = ';setTimeout(function() { throw new Error("Execution time limit reached!") }, 2000);';

var vm = require("vm");

var obj = {};
var ctx = vm.createContext(obj);

var script = vm.createScript(scriptBreaker + UNKNOWN_CODE);

try {
  script.runInNewContext(ctx);
  console.log("Finished");
}
catch (err) { 
  console.log("Timeout!"); 
  // Handle Timeout Error...
}
var cp = require('child_process');

function runUnsafeScript(script, callback) {
 var worker = cp.fork('./script-runner', [script]);

 worker.on('message', function(data) {
  worker.kill();
  callback(false, data);
 });

 worker.on('exit', function (code, signal) {
  callback(new Error(code), false);
 });

 worker.on('error', function (err) {
  callback(err, false);
 });

 setTimeout(function killOnTimeOut() {
  worker.kill();
  callback(new Error("Timeout"), false);
 }, 5000);
}
vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
try {
    vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
}
catch(e) {
    console.log(e); // Script execution timed out.
}

更新:

经过更多的测试,我得出结论,可靠的方法是使用Esailija指出的过程。不过,我的做法有点不同

在主应用程序中,我有如下代码:

var UNKNOWN_CODE = "while(true){}";

var scriptBreaker = ';setTimeout(function() { throw new Error("Execution time limit reached!") }, 2000);';

var vm = require("vm");

var obj = {};
var ctx = vm.createContext(obj);

var script = vm.createScript(scriptBreaker + UNKNOWN_CODE);

try {
  script.runInNewContext(ctx);
  console.log("Finished");
}
catch (err) { 
  console.log("Timeout!"); 
  // Handle Timeout Error...
}
var cp = require('child_process');

function runUnsafeScript(script, callback) {
 var worker = cp.fork('./script-runner', [script]);

 worker.on('message', function(data) {
  worker.kill();
  callback(false, data);
 });

 worker.on('exit', function (code, signal) {
  callback(new Error(code), false);
 });

 worker.on('error', function (err) {
  callback(err, false);
 });

 setTimeout(function killOnTimeOut() {
  worker.kill();
  callback(new Error("Timeout"), false);
 }, 5000);
}
vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
try {
    vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
}
catch(e) {
    console.log(e); // Script execution timed out.
}
在script-runner.js中,如下所示:

var vm = require("vm");
var script = vm.createScript( process.argv[2] );
var obj = { sendResult:function (result) { process.send(result); process.exit(0); } };
var context = vm.createContext(obj);

script.runInNewContext(context);

process.on('uncaughtException', function(err) {
 process.exit(1);
});
这种方法可以实现以下目标:

  • 在有限的上下文中运行脚本
  • 避免死循环和异常问题
  • 同时运行许多(受硬件限制的)不安全脚本,这样它们就不会互相中断
  • 将脚本执行结果传递给主应用程序进行进一步处理

在Node.js的较新版本(
v0.12
及更高版本)中,您将能够向
vm.runInNewContext
传递超时选项

这还没有出现在任何稳定的节点版本中,但是如果您希望使用最新的不稳定版本(
v0.11.13
),您可以通过如下方式传递超时参数:

var UNKNOWN_CODE = "while(true){}";

var scriptBreaker = ';setTimeout(function() { throw new Error("Execution time limit reached!") }, 2000);';

var vm = require("vm");

var obj = {};
var ctx = vm.createContext(obj);

var script = vm.createScript(scriptBreaker + UNKNOWN_CODE);

try {
  script.runInNewContext(ctx);
  console.log("Finished");
}
catch (err) { 
  console.log("Timeout!"); 
  // Handle Timeout Error...
}
var cp = require('child_process');

function runUnsafeScript(script, callback) {
 var worker = cp.fork('./script-runner', [script]);

 worker.on('message', function(data) {
  worker.kill();
  callback(false, data);
 });

 worker.on('exit', function (code, signal) {
  callback(new Error(code), false);
 });

 worker.on('error', function (err) {
  callback(err, false);
 });

 setTimeout(function killOnTimeOut() {
  worker.kill();
  callback(new Error("Timeout"), false);
 }, 5000);
}
vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
try {
    vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
}
catch(e) {
    console.log(e); // Script execution timed out.
}
现在,在1000毫秒之后,脚本将抛出一个超时错误。你可以这样抓住它:

var UNKNOWN_CODE = "while(true){}";

var scriptBreaker = ';setTimeout(function() { throw new Error("Execution time limit reached!") }, 2000);';

var vm = require("vm");

var obj = {};
var ctx = vm.createContext(obj);

var script = vm.createScript(scriptBreaker + UNKNOWN_CODE);

try {
  script.runInNewContext(ctx);
  console.log("Finished");
}
catch (err) { 
  console.log("Timeout!"); 
  // Handle Timeout Error...
}
var cp = require('child_process');

function runUnsafeScript(script, callback) {
 var worker = cp.fork('./script-runner', [script]);

 worker.on('message', function(data) {
  worker.kill();
  callback(false, data);
 });

 worker.on('exit', function (code, signal) {
  callback(new Error(code), false);
 });

 worker.on('error', function (err) {
  callback(err, false);
 });

 setTimeout(function killOnTimeOut() {
  worker.kill();
  callback(new Error("Timeout"), false);
 }, 5000);
}
vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
try {
    vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
}
catch(e) {
    console.log(e); // Script execution timed out.
}

谢谢你的回答。不幸的是,这对我不起作用,因为我需要在上下文之间传递缓冲区,而afaik缓冲区不能使用有限的节点IPC进行传输。还有其他想法吗?@muffel对不起,这里必须有另一个流程。进程本身无法执行代码,也无法执行其他跟踪代码的代码。这在物理上是不可能的,因为node.js进程是单线程的。至于缓冲区,我不知道。可能将它们转换为表示字节的数字数组?@Esailija这是否意味着vm2节点包()无法像广告中那样工作?请注意
worker.destroy()
已过时。它现在是worker.kill()。但是只有
worker.process.kill()保证“kill”。查看使用
setInterval
可以很容易地查看是否保持分叉。您会注意到,如果使用
worker.destroy()
(或
worker.kill()
,它们是别名),工作进程的数量会缓慢增长。使用
worker.process.kill()
它始终是一个进程。是的,它基于pthreads。尽管它仍然基于libev,但我已经设法在node 0.8上编译了它。node的哪个版本引入了它?
runInContext
是否也可以超时?@Andrew,这在节点v0.10.18中不起作用。(或我尝试过的任何其他版本)。复制并粘贴逐字记录,服务器就会挂起。这实际上是在Node
v0.11
及更高版本中添加的。我在下面添加了一个答案,描述了如何使用它(语法有点不同)。您可以使用
vm.runInNewContext('while(true){}',{},{timeout:1000})当内部脚本发出异步命令时,该命令立即返回,然后它可以在回调中执行任何它想执行的操作,而不管超时设置如何。是一个选项,但我想知道除了杀死进程之外,是否还有其他方法可以检测邪恶。有没有一种方法可以在不预先设置超时的情况下从外部终止
vm
?这是1。不必要,因为vm现在支持本机超时(请参阅接受的答案),2。不安全,您可以
clearTimeout()
(请参阅),3。总的来说有一种“黑客”的味道。我认为这可能是唯一正确的方式。如果不受信任的脚本在setTimeout或允许使用的任何异步函数的回调中启动while循环,您将如何处理这种情况?你唯一能做的另一件事就是使用类似但需要一些簿记的东西。