从Node core vm模块中获取值

时间:2016-11-14 07:02:37

标签: node.js

如何在节点中的vm上下文中获取值。

如果我创建一个上下文并在其中运行脚本,我是否使用util.inspect([[context]])

如果我这样做,它会回来序列化吗?

出于某种原因,当我util.inspect("null")回来时 '\'null'\'

是否有人知道原因,以及从节点vm模块中运行上下文中获取值的最佳方法是什么?

我还做了一些微基准测试,似乎创建一个新的上下文比在thisContext中运行它要慢得多。此外,当我摆脱超时时,它似乎更快地运行更多

所以我想使用这个上下文而不必使用全局变量来引起泄漏,并且还试图加快这种性能。

此代码的上下文是我正在尝试沙箱灾难性的正则表达式。

global.mapperContext = {
  result: false
};

const scriptString = `
  global.mapperContext.result = true;

  if ("${string}".match(${regex})) {
    global.mapperContext.result = false;
  }
`;

const vmScript = new vm.script(scriptString);

try {
  vmScript.runInThisContext({ timeout: 1000 });
} catch (e) {
  // Do something with error
}

// This works but I dont like attaching things to the global object.
// I could cause memory leaks...
console.log(global.mapperContext); 
const mapperContext = { result };

const scriptString = `
(function IIFE() {

  result = true;

  if ("${string}".match(${regex})) {
    result = false;
  }

})()
`;

const sandbox = vm.createContext(mapperContext);
const script = new vm.script(scriptString);

script.runInContext(script, sandbox, {timeout: 1000});

// This result is serialized and is hard to parse.
util.inspect(sandbox.result);

1 个答案:

答案 0 :(得分:2)

您的代码几乎是正确的,但您的script.runInContext()应该是这样的:

script.runInContext(sandbox, {timeout: 1000});

通过该更改,您应该将sandbox.result视为布尔值。

就性能而言,您应该能够在sandbox的多次调用中重复使用script.runInContext()个变量,这样会对某些人产生帮助。

您可能尝试的其他方法是创建一个通用脚本,您可以通过将它们传递到同一个脚本而不是为每组值创建新脚本来重用任何字符串和正则表达式值。例如:

const vm = require('vm');
const mapperContext = { result: false, string: 'bar', regex: /baa/ };

const scriptString = `
(function IIFE() {

  result = true;

  if (regex.test(string)) {
    result = false;
  }

})()
`;

// Only perform these two calls once ...
const sandbox = vm.createContext(mapperContext);
const script = new vm.Script(scriptString);

// ... and then run the script as many times as needed ...
script.runInContext(sandbox, {timeout: 1000});
console.dir(sandbox.result);

// ...

mapperContext.string = 'foo';
mapperContext.regex = /foo/;
script.runInContext(sandbox, {timeout: 1000});
console.dir(sandbox.result);

// ...

您可能还会考虑的一个转折是简单地返回一个布尔值,而不是在函数内部设置全局(result),这样返回值就可以作为script.runInContext()的返回值。例如:

const vm = require('vm');
const mapperContext = { string: 'bar', regex: /baa/ };
const scriptString = 'regex.test(string);';
const sandbox = vm.createContext(mapperContext);
const script = new vm.Script(scriptString);
var ret;

ret = script.runInContext(sandbox, {timeout: 1000});
console.dir(ret);

// ...

mapperContext.string = 'foo';
mapperContext.regex = /foo/;
ret = script.runInContext(sandbox, {timeout: 1000});
console.dir(ret);

// ...

最后,当然,您需要确保在try-catch块中将调用包装到script.runInContext(),以防出现超时。对于性能(pre-node v7.0.0),你需要在一个单独的函数中隔离这个try-catch,因为在节点v7之前,V8将永久地优化包含try-catch(或try-finally)的整个函数:

const vm = require('vm');

function tryRun(string, regex, timeout) {
  var ctx = tryRun.ctx;
  var sandbox = tryRun.sandbox;
  var script = tryRun.script;
  if (!ctx) {
    ctx = tryRun.ctx = { string: string, regex: regex };
    sandbox = tryRun.sandbox = vm.createContext(ctx);
    script = tryRun.script = new vm.Script('regex.test(string)');
  } else {
    ctx.string = string;
    ctx.regex = regex;
  }
  timeout = timeout || 1000;
  try {
    return script.runInContext(sandbox, {timeout});
  } catch (ex) {
    return ex;
  }
}

console.dir(tryRun('bar', /baa/));

// ...

console.dir(tryRun('foo', /foo/));

// ...

// Example of timeout
console.dir(tryRun('xxxx'.repeat(100), /(x+x+)+y/));

// ...