Node.js vm:如何取消Script.runInNewContext()?
我想使用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秒以上)?
提前致谢!
是的,这是现在可能的,因为我添加了对Node vm
模块的timeout
参数支持。 您可以简单地将毫秒超时值传递给runInNewContext()
,如果代码没有在指定的时间内完成执行,则会引发exception。
请注意,这并不意味着运行不受信任的代码的任何种类的安全模型。 这只允许您超时您信任或以其他方式保护的代码。
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
您需要在一个单独的过程中运行它,例如:
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"
。
群集文档
您可以将“脚本破坏者”embedded到UNKNOWN_CODE中。 就像是:
;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... }
更新:
经过更多的testing,我得出的结论是可靠的方法将是使用指针Esailija进程。 不过,我做的有点不同。
在主应用程序中,我有这样的代码:
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); }
在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); });
这种方法可以达到以下目标:
- 在有限的上下文中运行脚本
- 避免死循环和exception问题
- 同时运行许多(受硬件限制的)不安全的脚本,以免互相干扰
- 将脚本执行结果传递给主应用程序进行进一步处理
你可能想检查线程一个Gogo 。 不幸的是,它还没有更新到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);
在更新版本的Node.js( v0.12
及更高版本)中,您将能够将超时选项传递给vm.runInNewContext
。
这还没有稳定的节点版本,但是如果你想使用最新的unstable版本( v0.11.13
),你可以像这样传递一个timeout参数:
vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
现在,在1000毫秒之后,该脚本将引发超时错误。 你可以这样捕捉它:
try { vm.runInNewContext('while(1) {}', {}, {timeout: '1000'}); } catch(e) { console.log(e); // Script execution timed out. }
- MongoDB Gridfs的NODE显示图像
- 为什么不在db.collection()中callback?find()。toArray(callback)在数据库连接被删除的情况下调用?
- 在python服务器上构build来自Fabric.js JSON的图像
- 如何从node-gyp定位'imqi.hpp'
- 如何获取端口80上的node.js express.js
- npm link“文件被标记为可执行文件,但不能被操作系统运行。
- Node.JS / Express.JS CouchDB会话存储
- 将asynchronous工作stream更改为Promise(Bluebird)
- 如何确保正确的parameter passing给循环中声明的函数调用?