1

I have some asynchronous JavaScript code, which I'd like to run using vm2 / NodeVM.

The code tests two functions: a user-submitted function evenAndOdd and a pre-defined function solution using Node's built-in assert library.

My question is, how can I get the my resolved Promise out of vm.run()?

The Aynchronous Code

This is the test code I want to turn into a string and run using vm2:

const assert = require('assert');

function evenAndOdd(arr) {
  return [arr.filter(el => el % 2 === 0).sort(), arr.filter(el => el % 2 === 1).sort()];
};

function solution(arr) {
  return [arr.filter(el => el % 2 === 0).sort(), arr.filter(el => el % 2 === 1).sort()];
};

const runTest = async () => {

  return new Promise((resolve, reject) => {

    const map = new Map();
    const tests = 10;

    for (let i = 0; i < tests; i++) {

      let randLength = parseInt(Math.random() * 19 + 1);
      let randArray = [...Array(randLength)].map(e => ~~(Math.random() * randLength));

      const test = async () => {
        return assert.deepEqual(evenAndOdd(randArray), solution(randArray));
      }

      const description = "TEST CASE: evenAndOdd([" + randArray + "}]) EXPECT: [[" + solution(randArray)[0] + "], [" + solution(randArray)[1] + "]] GOT: [[" + evenAndOdd(randArray)[0] + "], [" + evenAndOdd(randArray)[1] + "]]";

      test()
        .then(() => {
          map.set(description, true);
          if (map.size === tests) {
            resolve(map);
          };
        })
        .catch(() => {
          map.set(description, false);
          if (map.size === tests) {
            resolve(map);
          };
        })
    }
  })
}

If I append the following code, I get exactly what I want logged to the console:

runTest().then((result) => {
  console.log(result)
});

The vm Code

My question is, how do I get the resolved promise from vm.run()?

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

  const vm = new NodeVM({
    console: 'inherit',
    sandbox: {},
    require: {
      external: true,
      builtin: ['assert'],
      import: ['assert'],
      root: "./",
    },
  });

const result = vm.run(stringifiedJavaScript, 'vm.js');

Right now, the result variable above is undefined.

Bret Cameron
  • 451
  • 1
  • 4
  • 18

2 Answers2

3

NodeVM.run() executes the code and returns the module created by the execution, which can then be accessed by the host code. In order to access the result of the computation in the processed code, you should assign it to module.exports.

Consider the following example:

const {NodeVM} = require("vm2");
const vm = new NodeVM();

const vmExports = vm.run('module.exports.pr = Promise.resolve(42);');

vmExports.pr.then(x => console.log(x));

The variable vmExports holds the module exported by the run call, and the field vmExports.pr holds the promise produced by the sandboxed script. The exported promise is thenable or can be awaited to access the resolved values.

Kalev
  • 1,158
  • 7
  • 17
0

You can also directly return the promise from your code.

Setting the wrapper option to none will return the value returned by the code as stated here https://github.com/patriksimek/vm2#nodevm

wrapper - commonjs (default) to wrap script into CommonJS wrapper, none to retrieve value returned by the script.

For example,

const vm = new NodeVM({
  wrapper: 'none'
})

const vmRes = await vm.run('return Promise.resolve(100)')

console.log(vmRes) // 100