If I want to let users dynamically run scripts in my app ...
There's nothing safe with this requirement, and none of the options is CSP friendly.
The "best" approach you could use though is Function
, 'cause at least that doesn't reaches/target the local scope, like eval
would do, keeping the execution within its function body scope.
The question now would be how to make the code available to the user without interfering with the rest of the code, or the previous call, or the global scope.
For this specific case I suggest a CommonJS like approach, so that your users must define what they are exporting in their code.
Example
const CommonJS = code => {
const exports = {};
const module = {exports};
const runtime = Function(
'module', 'exports',
`"use strict";${code};return module`
);
return runtime(module, exports);
};
const {exports} = CommonJS(`
module.exports = function foo(){console.log('Hello!')}
`);
exports();
Using this approach your users can define a single, or multiple, exports, like in Node.js, becoming familiar with the CommonJS module system, and avoiding any sort of global scope pollution, thanks to the "use strict"
directive and the Function closure itself.
Alternatively, you can const foo = Function('return(' + code + ')')()
but that requires code
to be a single expression, so I would personally stick with the previous approach.