标签:
NodeJS 中有一个名为 vm 的包,用来创建运行 NodeJS 代码(JavaScript, ECMAScript)的虚拟机。
var vm = require(‘vm‘);
这个vm的方法不多:
详情查看 NodeJS文档: VM
简单地来说,这个包里面有Script(脚本)、Context(上下文)这两种对象。
上下文中含有当前可以操作的各种对象,因此又可以称为运行环境。如果我们生成一个新的上下文并与当前上下文隔离,那么就相等于我们建立了一个沙箱,在沙箱中运行的NodeJS脚本将无法影响外部的环境。
关于Script这个对象,其实存在感不是很强,因为它直接由字符串构造,在很多时候可以直接用代码字符串代替。
var vm = require(‘vm‘);
var sandbox = vm.createContext({}); // Empty Context
var code = ‘var x = 1;‘
vm.runInContext(code, sandbox);
console.log(sandbox); // {x: 1}
可以针对NodeJS设计一类评测机,它可以直接检查沙箱内的变量来判断程序是否正确。
这个时候,初始的上下文就相当于输入数据。
var vm = require(‘vm‘);
var testcases = [];
for (var i = 0; i < 1000; i++) {
var a = Math.random() * 1000;
var b = Math.random() * 1000;
testcases.push({
input: {
a: a,
b: b
},
output: {
ans: a + b,
}
})
}
var code = ‘ans = a + b + (a < 5 ? 1: 0)‘;
testcases.forEach((e, i) => {
var sandbox = vm.createContext(e.input);
vm.runInContext(code, sandbox, {time: 1000}); // time limit: 1000ms
for(var key in e.output){
if(e.output[key] != sandbox[key]){
console.log(`testing failed in case ${i}`);
break;
}
}
})
上面这个程序生成了1000组随机数,并逐个建立沙盒,然后在其中运行代码,最后检查运行后的环境中某些变量是否符合要求。
这里是一个很简单的 A + B Problem, 但这里代码加入了一个扰动,当
a < 5
时答案会错误,这是刻意制造的错误,用于演示错误发生的情况。
可以看到,这样的待测试代码里面并不依赖I/O,评测系统的用户在提交代码的时候无须包括操作stdin, stdout的代码。就像写一个函数一样。
尽管当你不做任何事的时候,当用户代码包含var x = require(XXX);
试图加载包的时候,将会报ReferenceError: require is not defined
的错误。
因为他们的沙盒中根本就没有这个require函数,就没有办法加载包,更不会加载一些危险的包,如vm
与fs
。
但是,如果你一定要给他们开放包可供引用,那么你可以将外部的require函数扔到沙箱中:
var sandbox = vm.createContext({
require: require
})
这样用户代码里包含 require 函数将不会报错,会正确执行。
为了安全,你决定过滤一些危险的包:
var myRequire(package){
if(package == ‘fs‘ || package == ‘vm‘) return {};
return require(package);
}
var sandbox = vm.createContext({
require: myRequire
});
这样用户在引用 fs, vm 这些包的时候就会发现,得到的是一个空对象,自然也无法调用其中的方法了。
过滤的逻辑非常自由,你可以在里面加钩子,甚至重载整个 require 函数,甚至加入一些你自己的包。
标签:
原文地址:http://blog.csdn.net/zccz14/article/details/51344627