在 Node.js 中更改动态加载模块的全局范围

Altering Global Scope for dynamically loaded module in Node.js

从源动态加载模块:

var src="HERE GOES MY SOURCE"
var Module = module.constructor;
var m = new Module();
m._compile(src, 'a-path-that-does-not-exist');

需要实现以下目标:

  1. 传递一些 variables/functions 以便它们可以在 src 脚本中全局使用。可以在 "m.foo" 中设置它们,但希望脚本使用 "foo" 而不是使用 "module.foo"。 "global.foo" 有效,但请参阅第 2 点。
  2. 如何限制src脚本访问全局范围?
  3. 如何限制 src 使用 require 或其他方式加载其他模块。
  4. 如何从 运行 异步操作中限制 src

所有,我能想到的就是将脚本包装在它自己的函数中,有点像 nodejs 已经为 commonJS 模块所做的。这是常规包装纸。

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});

如果您用自己的包装器包装该用户代码,然后当您调用它来执行它时,您可以为 requiremodule 和任何其他半全局符号定义您自己的值.

如果你也放'use strict';作为包装函数的第一行(在任何用户代码之前),那么这将消除对 global 对象的默认分配,就像 x = 4 一样,因为如果没有明确定义这将是一个错误x 第一。如果您随后还创建自己的全局对象并将其作为参数传递,则可以防止任何人分配给真正的全局对象。我不认为你可以阻止对预先存在的全局变量的隐式读取访问。

因此,您的包装器可能如下所示:

(function(exports, require, module, __filename, __dirname, global) {
     'use strict';
     // insert user code here before evaluating it with eval()
     // and getting the function which you can then call and pass the desired arguments
});

然后,当你调用这个函数时,你将你想要的所有参数的值传递给它(不是真实的参数)。


请注意,很难说这种方案到底有多防漏。任何真正的安全性都应该是 运行 在资源受限的 VM 中。


另一个想法,您可以 运行 在具有自己的原始全局变量集的工作线程中。因此,您执行上述所有操作并 运行 在工作线程中完成。


在评论中解决您的问题:

Does the 'use strict'; need to go inside the wrapper function or outside?

它必须是包装函数内的第一行代码,就在您插入用户代码的位置之前。这个想法是强制该包装器内的函数范围(用户代码所在的位置)处于严格模式以限制它可以做的一些事情。

Could you explain the "I don't think you can prevent implicit read access to pre-existing globals."? If i provide my own object as global, how can the inner script access preexisting globals?

任何代码,甚至是严格模式代码都可以在没有 global 前缀的情况下访问预先存在的全局变量。虽然您可以通过在包装函数参数中用您自己的 global 隐藏它并强制它进入严格模式来阻止代码创建新的全局变量,但您不能阻止严格模式代码读取现有的全局变量,因为它们可以所以没有全局前缀。所以,如果有一个预先存在的名为 "foo" 的全局变量,那么现有代码可以像这样引用它:

 console.log(foo);

 foo = 12;

如果在更近的范围内没有 foo,解释器将在全局对象上找到 foo 并使用它。

请注意,严格模式会阻止自动创建一个新的全局变量,例如:

 greeting = "happy birthday"

Could you elaborate more no "resource restricted VM"?

我说的是真正的 hardware/OS 级 VM,它允许您完全控制进程可能使用的资源(磁盘访问、套接字、内存、硬件等)。它本质上是一个虚拟计算机环境,与同一系统上的任何其他 VM 分开。这是一个比较严格的控制级别。

WorkerThread is a very interesting concept. will take a look! My understanding was that WorkerThreads provide memory isolation and the only way to share data is by sending messages (effectively creating copies)?

是的,工作线程提供了很好的隔离,因为它们启动了一个全新的 JS 引擎并拥有自己的全局变量。他们可以以某些方式共享 ArrayBuffers(如果你选择这样做),但普通的 JS 变量不能跨线程边界访问。他们通常会通过消息传递(通过事件队列自动同步)进行通信,但如果需要,您也可以通过套接字进行通信。