@flowiseai/nodevm

中文文档
3.9.25发布于 2024/09/17MITTypeScriptCommonJS近 30 天下载 9120

nodevm是一个沙盒,可以使用白列节点的内置模块运行不可信代码

中文使用文档
文档目录21 节
  1. 流出节点沙盒
  2. 特征
  3. 怎么会这样
  4. 节点的vm和vm2有什么区别?
  5. 安装
  6. 快速示例
  7. 文档
  8. 志愿人员
  9. 节点VM
  10. 通过相对路径加载模块
  11. 解析器
  12. VMS 脚本
  13. 处理错误
  14. 调试沙盒代码
  15. 示例
  16. 只读对象( 实验)
  17. 受保护物体(实验性)
  18. 跨框关系
  19. 国 际
  20. 已知问题
  21. 部署

流出节点沙盒

这是原始的一叉 vm2 数据 复发. 为解决CVE问题,已经进行了修改.

vm2是一个沙盒,可以使用白列节点的内置模块运行不可信代码.

特征

  • 在单个过程中安全运行未信任的代码, 并肩运行代码
  • 全面控制沙盒控制台输出
  • 沙盒对过程方法的使用有限
  • 可以从沙盒中需要模块(内建和外建)
  • 您可以限制访问某些( 或所有) 内置模块
  • 您可以在沙盒之间安全地调用方法并交换数据和调用
  • 不受一切已知攻击方法的影响
  • 传输支持

怎么会这样

  • 它使用内部的VM模块来创建安全上下文.
  • 用这个 代理 防止逃离沙盒.
  • 它超越了控制模块访问所需的内置.

节点的vm和vm2有什么区别?

你自己试试看

const vm = require('vm');
vm.runInNewContext('this.constructor.constructor("return process")().exit()');
console.log('Never gets executed.');
const {VM} = require('vm2');
new VM().run('this.constructor.constructor("return process")().exit()');
// Throws ReferenceError: process is not defined

安装

重要性: VM2需要Node.js 6或更新.

npm install vm2

快速示例

const {VM} = require('vm2');
const vm = new VM();

vm.run(`process.exit()`); // TypeError: process.exit is not a function
const {NodeVM} = require('vm2');
const vm = new NodeVM({
    require: {
        external: true,
        root: './'
    }
});

vm.run(`
    var request = require('request');
    request('http://www.google.com', function (error, response, body) {
        console.error(error);
        if (!error && response.statusCode == 200) {
            console.log(body); // Show the HTML for the Google homepage.
        }
    });
`, 'vm.js');

文档

志愿人员

VM 是一个简单的沙盒,可以同步运行无信任的代码 require 特性。 只有 JavaScript 内置对象和节点对象 Buffer 备有。 排期函数( E)setInterval, (中文(简体) ), setTimeout 财务报告和审定财务报表 setImmediate) 默认不可用.

选项 :

  • timeout - 脚本超时数以毫秒计. 警告: 您可能想要一起使用此选项 allowAsync=false。此外,在从沙盒返回的对象上操作可以运行任意代码并绕过超时. 如果返回的物体是原始的,应该进行测试 typeof 并完全丢弃它(使用此对象进行记录或创建错误消息也可能再次运行任意代码) .
  • sandbox - VM的全球对象.
  • compiler - 说吧 javascript (违约)或 coffeescript 或自定义编译器函数。 如果编译器设定为 coffeescript。 。 。 .
  • eval - 如果设置为 false 任何呼吁 eval 或函数构造器( e)Function, (中文(简体) ), GeneratorFunction)将丢出一个 EvalError (默认 : true) (中文(简体) ).
  • wasm - 如果设置为 false 任何试图编译WebAssembly模块的尝试都会投出一个 WebAssembly.CompileError (默认 : true) (中文(简体) ).
  • allowAsync - 如果设置为 false 试图使用 async 将丢出一个 VMError (默认 : true) (中文(简体) ).

重要性: 超时只在运行的同步代码上有效 run超时 没有 vM返回的任何方法的工作。 有些时候超时不起作用 244号。 。 。 .

const {VM} = require('vm2');

const vm = new VM({
    timeout: 1000,
    allowAsync: false,
    sandbox: {}
});

vm.run('process.exit()'); // throws ReferenceError: process is not defined

您也可以从 VM 获取值 .

let number = vm.run('1337'); // returns 1337

人口贩运: 更多用法示例见测试.

节点VM

与 VM, (中文(简体) ), NodeVM 允许您以与常规节点上下文相同的方式要求模块.

选项 :

  • console - 说吧 inherit 启用控制台, redirect 转向事件, off 以禁用控制台( 默认 ): inherit) (中文(简体) ).
  • sandbox - VM的全球对象.
  • compiler - 说吧 javascript (违约)或 coffeescript 或自定义编译器功能(即接收代码,且是文件路径). 如果编译器设定为 coffeescript。 。 。 .
  • eval - 如果设置为 false 任何呼吁 eval 或函数构造器( e)Function, (中文(简体) ), GeneratorFunction)将丢出一个 EvalError (默认 : true) (中文(简体) ).
  • wasm - 如果设置为 false 任何试图编译WebAssembly模块的尝试都会投出一个 WebAssembly.CompileError (默认 : true) (中文(简体) ).
  • sourceExtensions - 文件扩展组,作为源代码处理(默认: ['js']) (中文(简体) ).
  • require - 说吧 true,用于启用 require 方法( 默认 ): false) (中文(简体) ).
  • require.external - 价值观可以是 true,允许的外部模块数组,或对象(默认: false) (中文(简体) ). 所有路径匹配 /node_modules/${any_allowed_external_module}/(?!/node_modules/) 允许要求.
  • require.external.modules - 允许的外部模块阵列。 还支持通配符, 因此指定 ['@scope/*-ver-??]例如,将允许使用所有含有窗体名称的模块 @scope/something-ver-aa, (中文(简体) ), @scope/other-ver-11等 (中文(简体) ). 这个 * 通配符不匹配路径分隔符 .
  • require.external.transitive - 布尔表示是否允许外部模块的过渡依赖性(默认: false) (中文(简体) ). 警告:当一个模块需要过渡时,任何模块都可以正常地要求它,即使这在被装入之前是不可能的.
  • require.builtin - 允许的内置模块的阵列,为所有人接受["*"](默认:无). 警告:"*"可能很危险,因为可以添加新的内置.
  • require.root - 需要本地模块的限制路径(默认:每条路径).
  • require.mock - 收集模拟模块(外部或内置).
  • require.context - 说吧 host (默认)在主机中要求模块并将其代理到沙盒中. sandbox 在沙盒中装入、编译和需要模块. callback(moduleFilename, ext) 以动态方式选择每个模块的背景。 默认为沙盒( sandbox) 。 除外 events,内置模块总是在主机中需要并被代理入沙盒.
  • require.import - 启动时将装入NodeVM的一系列模块.
  • require.resolve - 在传统的节点搜索路径中找不到模块时,需要额外的搜索功能.
  • require.customRequire - 使用代替 require 从主机装入模块的函数.
  • require.strict - 说吧 false 以不强制要求加载的模块采用严格模式(默认: true) (中文(简体) ).
  • require.fs - 自定义文件系统执行.
  • nesting - 说吧 警告:允许这是安全风险,因为脚本可以创建一个可以要求任何主机模块的节点VM. true 启用 VM 嵌入( 默认 ) : false) (中文(简体) ).
  • wrapper - 说吧 commonjs (默认)将脚本包入普通JS包装器, none 以获取脚本返回的值.
  • argv - 被传递到 process.argv。 。 。 .
  • env - 传递给 process.env。 。 。 .
  • strict - 说吧 true 以严格模式装入模块(默认值 : false) (中文(简体) ).

重要性: 超时对NodeVM无效,所以不能豁免 while (true) {} 或类似的邪恶.

记得:你允许的模块越多,你的沙盒就越脆弱.

const {NodeVM} = require('vm2');

const vm = new NodeVM({
    console: 'inherit',
    sandbox: {},
    require: {
        external: true,
        builtin: ['fs', 'path'],
        root: './',
        mock: {
            fs: {
                readFileSync: () => 'Nice try!'
            }
        }
    }
});

// Sync

let functionInSandbox = vm.run('module.exports = function(who) { console.log("hello "+ who); }');
functionInSandbox('world');

// Async

let functionWithCallbackInSandbox = vm.run('module.exports = function(who, callback) { callback("hello "+ who); }');
functionWithCallbackInSandbox('world', (greeting) => {
    console.log(greeting);
});

何时 wrapper 设置为 none, (中文(简体) ), NodeVM 行为更像 VM 用于同步代码.

assert.ok(vm.run('return true') === true);

人口贩运: 更多用法示例见测试.

通过相对路径加载模块

要通过相对路径加载模块,就必须通过您运行的脚本的全部路径,作为 vm 的第二个参数 run 如果脚本是字符串,则使用方法。 文件名会显示在脚本生成的任何堆栈痕迹中 .

vm.run('require("foobar")', '/data/myvmscript.js');

如果您正在运行的脚本是 VMScript, 路径会在 VMScript 构建器中给出 .

const script = new VMScript('require("foobar")', {filename: '/data/myvmscript.js'});
vm.run(script);

解析器

解析器可以通过 makeResolverFromLegacyOptions 用于多个 NodeVM 允许共享已编译的模块代码的实例, 可能加快加载时间 。 第一个例子是 NodeVM 可用 makeResolverFromLegacyOptions 如下所示.

const resolver = makeResolverFromLegacyOptions({
    external: true,
    builtin: ['fs', 'path'],
    root: './',
    mock: {
        fs: {
            readFileSync: () => 'Nice try!'
        }
    }
});
const vm = new NodeVM({
    console: 'inherit',
    sandbox: {},
    require: resolver
});

VMS 脚本

可以通过使用预编译脚本来提高性能. 预编译的VMScript可以多次运行. 需要注意的是,代码与任何VM(文本)无关,而是在每次运行前,仅为该运行而绑定.

const {VM, VMScript} = require('vm2');

const vm = new VM();
const script = new VMScript('Math.random()');
console.log(vm.run(script));
console.log(vm.run(script));

两者都有用 VM 财务报告和审定财务报表 NodeVM。 。 。 .

const {NodeVM, VMScript} = require('vm2');

const vm = new NodeVM();
const script = new VMScript('module.exports = Math.random()');
console.log(vm.run(script));
console.log(vm.run(script));

代码在第一次运行时自动被编译. 随时可以编译代码 script.compile()。一旦编译出代码,该方法就没有效果.

处理错误

代码编译错误和同步代码执行错误可以通过 try-catch。同步代码执行中的错误可以通过附加处理 uncaughtException 事件处理器到节点 process。 。 。 .

try {
    var script = new VMScript('Math.random()').compile();
} catch (err) {
    console.error('Failed to compile script.', err);
}

try {
    vm.run(script);
} catch (err) {
    console.error('Failed to execute script.', err);
}

process.on('uncaughtException', (err) => {
    console.error('Asynchronous error caught.', err);
});

调试沙盒代码

您可以调试或检查在沙盒中运行的代码, 仿佛它在正常过程中运行.

  • 您可以使用断点( 需要您指定脚本文件名称)
  • 你用得着 debugger 关键词.
  • 可以使用一步入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入出出入入入入出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出.

示例

/tmp/main.js: (中文(简体) )

const {VM, VMScript} = require('.');
const fs = require('fs');
const file = `${__dirname}/sandbox.js`;

// By providing a file name as second argument you enable breakpoints
const script = new VMScript(fs.readFileSync(file), file);

new VM().run(script);

/tmp/sandbox.js (英语)

const foo = 'ahoj';

// The debugger keyword works just fine everywhere.
// Even without specifying a file name to the VMScript object.
debugger;

只读对象( 实验)

为了防止沙盒脚本从代理对象中添加、更改或删除属性,可以使用 freeze 使对象只读的方法。 这只在VM内部有效. 被冻结的物体受到严重影响。 原始类型不能被冻结.

不使用示例 freeze数字 :

const util = {
    add: (a, b) => a + b
}

const vm = new VM({
    sandbox: {util}
});

vm.run('util.add = (a, b) => a - b');
console.log(util.add(1, 1)); // returns 0

使用示例 freeze数字 :

const vm = new VM(); // Objects specified in the sandbox cannot be frozen.
vm.freeze(util, 'util'); // Second argument adds object to global.

vm.run('util.add = (a, b) => a - b'); // Fails silently when not in strict mode.
console.log(util.add(1, 1)); // returns 2

关键是: 无法冻结已经代理VM的物体.

受保护物体(实验性)

与 freeze,这种方法允许沙盒脚本在对象上添加,更改,或删除属性,只有一个例外——无法附加函数. 因此,沙盒脚本无法修改方法如 toJSON, (中文(简体) ), toString 或者说 inspect。 。 。 .

关键是: 无法保护已经代理 VM 的物体 .

跨框关系

const assert = require('assert');
const {VM} = require('vm2');

const sandbox = {
    object: new Object(),
    func: new Function(),
    buffer: new Buffer([0x01, 0x05])
}

const vm = new VM({sandbox});

assert.ok(vm.run(`object`) === sandbox.object);
assert.ok(vm.run(`object instanceof Object`));
assert.ok(vm.run(`object`) instanceof Object);
assert.ok(vm.run(`object.__proto__ === Object.prototype`));
assert.ok(vm.run(`object`).__proto__ === Object.prototype);

assert.ok(vm.run(`func`) === sandbox.func);
assert.ok(vm.run(`func instanceof Function`));
assert.ok(vm.run(`func`) instanceof Function);
assert.ok(vm.run(`func.__proto__ === Function.prototype`));
assert.ok(vm.run(`func`).__proto__ === Function.prototype);

assert.ok(vm.run(`new func() instanceof func`));
assert.ok(vm.run(`new func()`) instanceof sandbox.func);
assert.ok(vm.run(`new func().__proto__ === func.prototype`));
assert.ok(vm.run(`new func()`).__proto__ === sandbox.func.prototype);

assert.ok(vm.run(`buffer`) === sandbox.buffer);
assert.ok(vm.run(`buffer instanceof Buffer`));
assert.ok(vm.run(`buffer`) instanceof Buffer);
assert.ok(vm.run(`buffer.__proto__ === Buffer.prototype`));
assert.ok(vm.run(`buffer`).__proto__ === Buffer.prototype);
assert.ok(vm.run(`buffer.slice(0, 1) instanceof Buffer`));
assert.ok(vm.run(`buffer.slice(0, 1)`) instanceof Buffer);

国 际

在命令行中使用 vm2 之前, 在全球安装它 npm install vm2 -g。 。 。 .

vm2 ./script.js

已知问题

  • 有一些已知的安全问题来规避沙盒.
  • 无法定义扩展代位类的类. 这包括使用在 Object.create。 。 。 .
  • 直接Eval不起作用.
  • 日志沙盒阵列将重复属性中的数组部分.
  • 源代码转换可以为一个函数产生不同的源字符串.
  • 从沙盒内部可以崩溃节点过程.

部署

  1. 更新 package.json 版本编号
  2. 提交更改
  3. 运行 npm publish

版本与发布记录

@flowiseai/nodevm 共发布 1 个版本,首次发布于 2024/09/17;过去 12 个月发布了 0 个版本。

版本发布日期距今
3.9.25latest2024/09/172 年前

关于 @flowiseai/nodevm 的常见问题

@flowiseai/nodevm 是什么?

nodevm是一个沙盒,可以使用白列节点的内置模块运行不可信代码

如何安装 @flowiseai/nodevm?

在项目目录运行 npm install @flowiseai/nodevm,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 vm2,可以用 npx vm2 直接运行。

@flowiseai/nodevm 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@flowiseai/nodevm 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@flowiseai/nodevm 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.10。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@flowiseai/nodevm 可以商用吗?

@flowiseai/nodevm 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@flowiseai/nodevm 的下载量有多少?

根据 npm 官方下载统计,@flowiseai/nodevm 在 2026/10/15 – 2026/11/13 这 30 天内被下载 9,120 次,日均约 304 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@flowiseai/nodevm 还在维护吗?

最新版本 3.9.25 发布于 2024/09/17(2 年前);至今共发布 1 个版本,其中过去 12 个月发布了 0 个;首次发布于 2024/09/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。