diff --git a/packages/bruno-js/src/sandbox/isolatedvm/index.js b/packages/bruno-js/src/sandbox/isolatedvm/index.js index 2606939e9..cb35a3024 100644 --- a/packages/bruno-js/src/sandbox/isolatedvm/index.js +++ b/packages/bruno-js/src/sandbox/isolatedvm/index.js @@ -8,77 +8,13 @@ const addLibraryShimsToContext = require('./shims/lib'); // execute `npm run build:isolated-vm:inbuilt-modules` if the below file doesn't exist const getBundledCode = require('../../bundle-browser-rollup'); +const addSleepShimToContext = require('./shims/sleep'); const toNumber = (value) => { const num = Number(value); return Number.isInteger(num) ? parseInt(value, 10) : parseFloat(value); }; -class IsolatedVMStrict { - constructor() { - this.result = null; - this.init(); - } - - init() { - this.isolate = new ivm.Isolate(); - this.context = this.isolate.createContextSync(); - this.context.global.setSync('global', this.context.global.derefInto()); - this.context.evalSync(` - let bru = {}; - let req = {}; - let res = {}; - `); - this.context.global.setSync('setResult', (arg) => { - this.result = arg; - }); - } - - dispose() { - this.isolate.dispose(); - } - - reset() { - this.context.evalSync(` - bru = {}; - req = {}; - res = {}; - `); - } - - execute({ script: externalScript, context: externalContext, scriptType = 'script' }) { - if (!isNaN(Number(externalScript))) { - return toNumber(externalScript); - } - try { - const { bru, req, res } = externalContext; - - bru && addBruShimToContext(this.context, bru); - req && addBrunoRequestShimToContext(this.context, req); - res && addBrunoResponseShimToContext(this.context, res); - - const templateLiteralText = ` - global.value = \`${externalScript}\`; - setResult(global.value); - `; - - const jsExpressionText = ` - global.value = ${externalScript}; - setResult(global.value); - `; - - let scriptText = scriptType === 'template-literal' ? templateLiteralText : jsExpressionText; - - const script = this.isolate.compileScriptSync(scriptText); - script.runSync(this.context); - this.reset(); - return this.result; - } catch (error) { - console.error('Error executing the script!', error); - } - } -} - const executeInIsolatedVMStrict = ({ script: externalScript, context: externalContext, scriptType = 'script' }) => { if (!isNaN(Number(externalScript))) { return Number(externalScript); @@ -126,106 +62,6 @@ const executeInIsolatedVMStrict = ({ script: externalScript, context: externalCo isolate.dispose(); }; -class IsolatedVMAsync { - constructor() { - this.result; - this.initPromise = this.init(); - } - - async init() { - this.isolate = new ivm.Isolate(); - this.context = this.isolate.createContextSync(); - this.context.global.setSync('global', this.context.global.derefInto()); - this.context.evalSync(` - let bru = {}; - let req = {}; - let res = {}; - let console = {}; - global.requireObject = {}; - global.require = (module) => { - return global.requireObject[module]; - } - `); - - try { - this.bundledCode = getBundledCode?.toString() || ''; - await this.context.eval(`(${this.bundledCode})()`); - } catch (err) { - console.debug('Error bundling libraries', err); - } - - await addLibraryShimsToContext(this.context); - - this.context.global.setSync('setResult', (arg) => { - this.result = arg; - }); - } - - dispose() { - this.isolate.dispose(); - } - - reset() { - this.context.evalSync(` - bru = {}; - req = {}; - res = {}; - console = {}; - `); - } - - async execute({ script: externalScript, context: externalContext, scriptType = 'script' }) { - await this.initPromise; - if (!isNaN(Number(externalScript))) { - return toNumber(externalScript); - } - let result; - try { - const { bru, req, res, test, __brunoTestResults, console: consoleFn } = externalContext; - - bru && addBruShimToContext(this.context, bru); - req && addBrunoRequestShimToContext(this.context, req); - res && addBrunoResponseShimToContext(this.context, res); - consoleFn && addConsoleShimToContext(this.context, consoleFn); - - test && __brunoTestResults && (await addTestShimToContext(this.context, __brunoTestResults)); - - const jsScriptText = ` - new Promise(async (resolve, reject) => { - console?.debug && console.debug('isolated-vm:execution-start:'); - try { - ${externalScript} - } catch (error) { - console?.debug && console.debug('isolated-vm:execution-end:with-error', error?.message); - } - console?.debug && console.debug('isolated-vm:execution-end:'); - resolve(); - }); - `; - const templateLiteralText = ` - global.value = \`${externalScript}\`; - setResult(global.value); - `; - const jsExpressionText = ` - global.value = ${externalScript}; - setResult(global.value); - `; - let scriptText = - scriptType === 'template-literal' - ? templateLiteralText - : scriptType === 'expression' - ? jsExpressionText - : jsScriptText; - const script = await this.isolate.compileScript(scriptText); - await script.run(this.context); - this.reset(); - return this.result; - } catch (error) { - console.error('Error executing the script!', error); - } - } -} - const executeInIsolatedVMAsync = async ({ script: externalScript, context: externalContext, @@ -247,11 +83,7 @@ const executeInIsolatedVMAsync = async ({ let res = {}; let console = {}; global.requireObject = {}; - global.require = (module) => { - return global.requireObject[module]; - } `); - await addLibraryShimsToContext(this.context); context.global.setSync('log', function (...args) { console.debug(...args); @@ -270,6 +102,7 @@ const executeInIsolatedVMAsync = async ({ req && addBrunoRequestShimToContext(context, req); res && addBrunoResponseShimToContext(context, res); consoleFn && addConsoleShimToContext(context, consoleFn); + addSleepShimToContext(context); await context.eval( ` @@ -279,6 +112,8 @@ const executeInIsolatedVMAsync = async ({ ` ); + await addLibraryShimsToContext(context); + test && __brunoTestResults && (await addTestShimToContext(context, __brunoTestResults)); context.global.setSync('setResult', function (arg) { @@ -287,6 +122,8 @@ const executeInIsolatedVMAsync = async ({ const jsScriptText = ` new Promise(async (resolve, reject) => { + // modify the setTimeout function with the shim to work-around the callback-function clone issues + setTimeout = global.setTimeout; console?.debug && console.debug('isolated-vm:execution-start:'); try { ${externalScript} diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/lib/axios.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/lib/axios.js index 74740d31e..b6e9d1888 100644 --- a/packages/bruno-js/src/sandbox/isolatedvm/shims/lib/axios.js +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/lib/axios.js @@ -22,7 +22,8 @@ const addAxiosShimToContext = async (context) => { let args = argStrings?.map((arg) => JSON.parse(arg)); const res = await axios(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { @@ -37,7 +38,8 @@ const addAxiosShimToContext = async (context) => { const res = await axios .get(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { @@ -52,7 +54,8 @@ const addAxiosShimToContext = async (context) => { const res = await axios .post(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { @@ -67,7 +70,8 @@ const addAxiosShimToContext = async (context) => { const res = await axios .put(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { @@ -82,7 +86,8 @@ const addAxiosShimToContext = async (context) => { const res = await axios .delete(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { @@ -97,7 +102,8 @@ const addAxiosShimToContext = async (context) => { const res = await axios .patch(...args) .then((response) => { - return cleanJson(response?.data); + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); }) .catch((err) => { return { diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/sleep.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/sleep.js new file mode 100644 index 000000000..14ce92e30 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/sleep.js @@ -0,0 +1,32 @@ +const ivm = require('isolated-vm'); +const addSleepShimToContext = (context, console) => { + context.evalClosureSync( + ` + global.sleep = (...args) => $0.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + `, + [ + async (...argStrings) => { + await new Promise((resolve) => { + const timer = Number(argStrings?.[0]); + if (!Number.isInteger(timer) || timer < 0) { + resolve(); + } + setTimeout(() => { + resolve(); + }, timer); + }); + return new ivm.ExternalCopy('done').copyInto({ release: true }); + } + ], + { arguments: { reference: true } } + ); + + context.evalSync(` + global.setTimeout = async (fn, timer) => { + await sleep(timer); + await fn.apply(); + } + `); +}; + +module.exports = addSleepShimToContext;