ASA2 ASA2 - 6 months ago 18
Node.js Question

Converting Eval to Node's VM Module with an Enclosed Function

We're using Loopback for our REST end-points and have one end-point that pulls JavaScript from a database and then loads and executes specific functions at the server in the Node instance using eval. We want to convert to Node's VM functionality but are running into issues. Specifically how to:


  1. Load code that has multiple functions in a 'namespace',

  2. Execute one of those functions,

  3. Allow the injected functions to access an imported module,

  4. Then return a result from that function.



The 'eval' based code worked well but seems to have stopped working with the latest Node. So, we're using this opportunity to convert to Node's 'vm' sandboxing. However, we're having trouble calling a function that's embedded in a namespace.

// ********************************
// CODE TO CALL THE SERVER END-POINT AND CONSUME THE RETURN
// ********************************
let javaScriptCode = `
namespace QueryLib {

// Execute a query.
export function onQuery(callback) {

// Execute the Query functions from the 'Query' import to retrieve data.
Query.select('RowID', 'CustomerID', 'Name').from('customer').then(function (e, rows) {
callback(e, rows)
})
}
}
`

// Call the end-point and receive the query data.
ServerRestEndPoints.executeDynamicScriptOnServer(javaScriptCode, function (data) {
// Process 'data' here.
});

// ****************************************************************
// SERVER (END-POINT) CODE
// ****************************************************************

// ********************************
// CURRENT VERION USING 'EVAL'
// ********************************
import Query = require("query");

export function executeDynamicScriptOnServer(javaScript, callback) {

// Load the module with the script passed in (pulled from the db).
eval(javaScript);

// Load the function from the dynamic script
let func = eval('onQuery');

// Execute the function and return the data
func(function (returnData) {
callback(returnData)
});
}

// ********************************
// NEW VERSION ATTEMPTING TO USE 'vm' SANDBOXING.
// ********************************
import Query = require("query");
var vm = require("vm");

export function executeDynamicScriptOnServer(javaScript, callback) {

// Load the module with the dyanamic script.
vm.runInNewContext(javaScript, vm.createContext({ Query: Query.Query }));

// ---- > NOT SURE HOW TO EXECUTE THE 'QueryLib.onQuery" FUNCTION IN THE INJECTED JAVASCRIPT.
func(function (returnData) {
callback(returnData)
});
}

Answer

I believe this should accomplish what you need:

// ********************************
// NEW VERSION ATTEMPTING TO USE 'vm' SANDBOXING.
// ********************************
var Query = require("query");
var vm = require("vm");

export function executeDynamicScriptOnServer(javaScript, callback) {
  javaScript += 'onQuery(done)'
  // Load the module with the dyanamic script.
  vm.runInNewContext(javaScript, vm.createContext({
    Query: Query.Query,
    done: function(returnData) {
      // do stuff with the result
      callback(returnData)
    }
  }));
}