13

vm外部コードを実行するための安全な方法としてモジュールを使用したいと考えています。これはかなりうまく機能しますが、1 つの問題が残っています。

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 秒以上続く場合)?

前もって感謝します!

4

5 に答える 5

25

次のように、別のプロセスで実行する必要があります。

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();

ランナー.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 秒後に「worker timed out」というメッセージが表示されます。ワーカーに変更すると'while(false){}'、すぐにコードが実行され、"finished"代わりに表示されるはずです。

クラスタ ドキュメント

于 2012-07-29T21:24:57.753 に答える
6

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...
}

アップデート:

さらにテストを重ねた結果、信頼できるアプローチはプロセスを指摘された Esailja として使用することであるという結論に達しました。しかし、私はそれを少し違ったやり方でやっています。

メインアプリには、次のようなコードがあります。

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);
});

このアプローチにより、次の目標を達成することができました。

  • 限られたコンテキストでスクリプトを実行する
  • デッド ループと例外の問題を回避する
  • 多くの(ハードウェアによって制限される)安全でないスクリプトを同時に実行して、相互に干渉しないようにする
  • スクリプトの実行結果をメインアプリに渡してさらに処理する
于 2014-03-18T22:19:43.653 に答える
3

Threads a Gogoをチェックしたいかもしれません。残念ながら、まだ 0.8.x にアップデートされていません。

ただし、@Esailja が述べたように、別のプロセスにない限り、外部コードを安全に実行する方法はありません

var Threads = require('threads_a_gogo');

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

setTimeout(function() {
  t.destroy();
  console.log('finished');
}, 1000);
于 2012-08-03T00:35:05.127 に答える
0

Node.js (およびそれ以降) の新しいバージョンではv0.12、タイムアウト オプションを に渡すことができますvm.runInNewContext

これはまだどの安定ノード リリースにもありませんが、最新の不安定リリース ( v0.11.13) を使用する場合は、次のようにタイムアウト パラメータを渡すことができます。

vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});

これで、1000 ミリ秒後に、スクリプトはタイムアウト エラーをスローします。次のようにキャッチできます。

try {
    vm.runInNewContext('while(1) {}', {}, {timeout: '1000'});
}
catch(e) {
    console.log(e); // Script execution timed out.
}
于 2014-05-12T03:23:08.877 に答える