diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index c8e3b72ce..6260b5eaa 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -14,7 +14,8 @@ "url": "git+https://github.com/usebruno/bruno.git" }, "scripts": { - "test": "jest" + "test": "jest", + "postinstall": "cd node_modules/isolated-vm && npm rebuild" }, "files": [ "src", diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 23db67a0a..7ebad6b2f 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -42,7 +42,7 @@ const runSingleRequest = async function ( // todo: allow to override from cli args // we will default to vm2 (developer-mode) for 1.x version for backward compatibility // 2.x will default to isolated-vm (safe mode) - scriptingConfig.runtime = 'vm2'; + scriptingConfig.runtime = 'isolated-vm'; // make axios work in node using form data // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427 diff --git a/packages/bruno-js/package.json b/packages/bruno-js/package.json index b9c519b00..b3d94f519 100644 --- a/packages/bruno-js/package.json +++ b/packages/bruno-js/package.json @@ -12,8 +12,7 @@ }, "scripts": { "test": "jest --testPathIgnorePatterns test.js", - "rebuild:electron": "cd ./node_modules/isolated-vm && npx electron-rebuild", - "postinstall": "npm run isolated-vm:install && npm run isolated-vm:prebuild:dev && npm run isolated-vm:bundle-libraries", + "postinstall": "npm run isolated-vm:install && npm run isolated-vm:bundle-libraries", "isolated-vm:install": "cd ./node_modules/isolated-vm && npm install", "isolated-vm:prebuild:dev": "node ./scripts/prebuild-isolated-vm-for-dev.js", "isolated-vm:prebuild:prod": "node ./scripts/prebuild-isolated-vm-for-prod-builds.js", @@ -37,6 +36,7 @@ "nanoid": "3.3.4", "node-fetch": "2.*", "node-vault": "^0.10.2", + "quickjs-emscripten": "^0.29.2", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/packages/bruno-js/src/runtime/test-runtime.js b/packages/bruno-js/src/runtime/test-runtime.js index c13890e0a..a2a68bc1e 100644 --- a/packages/bruno-js/src/runtime/test-runtime.js +++ b/packages/bruno-js/src/runtime/test-runtime.js @@ -108,7 +108,7 @@ class TestRuntime { }; } - if (this.mode == 'safe') { + if (this.runtime === 'isolated-vm') { await executeInIsolatedVMAsync({ script: testsFile, context: context, diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/index.js b/packages/bruno-js/src/sandbox/isolatedvm copy/index.js new file mode 100644 index 000000000..cb35a3024 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/index.js @@ -0,0 +1,167 @@ +const ivm = require('isolated-vm'); +const addBruShimToContext = require('./shims/bru'); +const addBrunoRequestShimToContext = require('./shims/bruno-request'); +const addConsoleShimToContext = require('./shims/console'); +const addBrunoResponseShimToContext = require('./shims/bruno-response'); +const addTestShimToContext = require('./shims/test'); +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); +}; + +const executeInIsolatedVMStrict = ({ script: externalScript, context: externalContext, scriptType = 'script' }) => { + if (!isNaN(Number(externalScript))) { + return Number(externalScript); + } + let result; + const isolate = new ivm.Isolate(); + try { + const context = isolate.createContextSync(); + context.global.setSync('global', context.global.derefInto()); + + const { bru, req, res } = externalContext; + + context.evalSync(` + let bru = {}; + let req = {}; + let res = {}; + `); + + bru && addBruShimToContext(context, bru); + req && addBrunoRequestShimToContext(context, req); + res && addBrunoResponseShimToContext(context, res); + + context.global.setSync('setResult', function (arg) { + result = arg; + }); + + const templateLiteralText = ` + let value = \`${externalScript}\`; + setResult(value); + `; + + const jsExpressionText = ` + let value = ${externalScript}; + setResult(value); + `; + + let scriptText = scriptType === 'template-literal' ? templateLiteralText : jsExpressionText; + + const script = isolate.compileScriptSync(scriptText); + script.runSync(context); + return result; + } catch (error) { + console.error('Error executing the script!', error); + } + isolate.dispose(); +}; + +const executeInIsolatedVMAsync = async ({ + script: externalScript, + context: externalContext, + modules = {}, + scriptType = 'script' +}) => { + if (!isNaN(Number(externalScript))) { + return toNumber(externalScript); + } + let result; + const isolate = new ivm.Isolate(); + try { + const context = await isolate.createContext(); + await context.global.set('global', context.global.derefInto()); + + context.evalSync(` + let bru = {}; + let req = {}; + let res = {}; + let console = {}; + global.requireObject = {}; + `); + + context.global.setSync('log', function (...args) { + console.debug(...args); + }); + + try { + const bundledCode = getBundledCode?.toString() || ''; + await context.eval(`(${bundledCode})()`); + } catch (err) { + console.debug('Error bundling libraries', err); + } + + const { bru, req, res, test, __brunoTestResults, console: consoleFn } = externalContext; + + bru && addBruShimToContext(context, bru); + req && addBrunoRequestShimToContext(context, req); + res && addBrunoResponseShimToContext(context, res); + consoleFn && addConsoleShimToContext(context, consoleFn); + addSleepShimToContext(context); + + await context.eval( + ` + global.require = (module) => { + return global.requireObject[module]; + } + ` + ); + + await addLibraryShimsToContext(context); + + test && __brunoTestResults && (await addTestShimToContext(context, __brunoTestResults)); + + context.global.setSync('setResult', function (arg) { + result = arg; + }); + + 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} + } 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 = ` + let value = \`${externalScript}\`; + setResult(value); + `; + + const jsExpressionText = ` + let value = ${externalScript}; + setResult(value); + `; + + let scriptText = + scriptType === 'template-literal' + ? templateLiteralText + : scriptType === 'expression' + ? jsExpressionText + : jsScriptText; + + const script = await isolate.compileScript(scriptText); + await script.run(context); + return result; + } catch (error) { + console.error('Error executing the script!', error); + } + isolate.dispose(); +}; + +module.exports = { + executeInIsolatedVMStrict, + executeInIsolatedVMAsync +}; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bru.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bru.js new file mode 100644 index 000000000..17bae399e --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bru.js @@ -0,0 +1,59 @@ +const addBruShimToContext = (context, bru) => { + context.global.setSync('cwd', function () { + return bru.cwd(); + }); + + context.global.setSync('getEnvName', function () { + return bru.getEnvName(); + }); + + context.global.setSync('getProcessEnv', function (key) { + return bru.getProcessEnv(key); + }); + + context.global.setSync('getEnvVar', function (key) { + return bru.getEnvVar(key); + }); + + context.global.setSync('setEnvVar', function (key, value) { + bru.setEnvVar(key, value); + }); + + context.global.setSync('setVar', function (key, value) { + bru.setVar(key, value); + }); + + context.global.setSync('getVar', function (key) { + return bru.getVar(key); + }); + + context.global.setSync('setNextRequest', function (nextRequest) { + bru.setNextRequest(nextRequest); + }); + + context.global.setSync('visualize', function (htmlString) { + bru.visualize(htmlString); + }); + + context.global.setSync('getSecretVar', function (key) { + return bru.getSecretVar(key); + }); + + context.evalSync(` + bru = { + ...bru || {}, + cwd: global.cwd, + getEnvName: global.getEnvName, + getProcessEnv: global.getProcessEnv, + getEnvVar: global.getEnvVar, + setEnvVar: global.setEnvVar, + setVar: global.setVar, + getVar: global.getVar, + setNextRequest: global.setNextRequest, + visualize: global.visualize, + getSecretVar: global.getSecretVar + } + `); +}; + +module.exports = addBruShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-request.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-request.js new file mode 100644 index 000000000..f8a1f9b9e --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-request.js @@ -0,0 +1,79 @@ +const addBrunoRequestShimToContext = (context, req) => { + context.global.setSync('getUrl', function () { + return req.getUrl(); + }); + + context.global.setSync('setUrl', function (url) { + req.setUrl(url); + }); + + context.global.setSync('getMethod', function () { + return req.getMethod(); + }); + + context.global.setSync('getAuthMode', function () { + return req.getAuthMode(); + }); + + context.global.setSync('setMethod', function (method) { + req.setMethod(method); + }); + + context.global.setSync('getHeaders', function () { + return req.getHeaders(); + }); + + context.global.setSync('setHeaders', function (headers) { + req.setHeaders(headers); + }); + + context.global.setSync('getHeader', function (name) { + return req.getHeader(name); + }); + + context.global.setSync('setHeader', function (name, value) { + req.setHeader(name, value); + }); + + context.global.setSync('getBody', function () { + return req.getBody(); + }); + + context.global.setSync('setBody', function (data) { + req.setBody(data); + }); + + context.global.setSync('setMaxRedirects', function (maxRedirects) { + req.setMaxRedirects(maxRedirects); + }); + + context.global.setSync('getTimeout', function () { + return req.getTimeout(); + }); + + context.global.setSync('setTimeout', function (timeout) { + req.setTimeout(timeout); + }); + + context.evalSync(` + req = { + ...req || {}, + getUrl: global.getUrl, + setUrl: global.setUrl, + getMethod: global.getMethod, + getAuthMode: global.getAuthMode, + setMethod: global.setMethod, + getHeaders: global.getHeaders, + setHeaders: global.setHeaders, + getHeader: global.getHeader, + setHeader: global.setHeader, + getBody: global.getBody, + setBody: global.setBody, + setMaxRedirects: global.setMaxRedirects, + getTimeout: global.getTimeout, + setTimeout: global.setTimeout + } +`); +}; + +module.exports = addBrunoRequestShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-response.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-response.js new file mode 100644 index 000000000..7ac8d5901 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/bruno-response.js @@ -0,0 +1,46 @@ +const ivm = require('isolated-vm'); + +const addBrunoResponseShimToContext = (context, res) => { + context.global.setSync('status', new ivm.ExternalCopy(res?.status).copyInto()); + context.global.setSync('headers', new ivm.ExternalCopy(res?.headers).copyInto()); + context.global.setSync('body', new ivm.ExternalCopy(res?.body).copyInto()); + context.global.setSync('responseTime', new ivm.ExternalCopy(res?.responseTime).copyInto()); + + context.global.setSync('getStatus', function () { + return res?.getStatus(); + }); + + context.global.setSync('getHeader', function (name) { + return res?.getHeader(name); + }); + + context.global.setSync('getHeaders', function () { + return res?.getHeaders(); + }); + + context.global.setSync('getBody', function () { + return res?.getBody(); + }); + + context.global.setSync('getResponseTime', function () { + return res?.getResponseTime(); + }); + + context.evalSync(` + res = { + ...res || {}, + status: global.status, + statusText: global.statusText, + headers: global.headers, + body: global.body, + responseTime: global.responseTime, + getStatus: global.getStatus, + getHeader: global.getHeader, + getHeaders: global.getHeaders, + getBody: global.getBody, + getResponseTime: global.getResponseTime + } + `); +}; + +module.exports = addBrunoResponseShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/console.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/console.js new file mode 100644 index 000000000..ead852992 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/console.js @@ -0,0 +1,39 @@ +const addConsoleShimToContext = (context, console) => { + context.global.setSync('log', function (...args) { + console?.log && console.log(...args); + return args; + }); + + context.global.setSync('debug', function (...args) { + console?.debug && console.debug(...args); + return args; + }); + + context.global.setSync('info', function (...args) { + console?.info && console.info(...args); + return args; + }); + + context.global.setSync('warn', function (...args) { + console?.warn && console.warn(...args); + return args; + }); + + context.global.setSync('error', function (...args) { + console?.error && console.error(...args); + return args; + }); + + context.evalSync(` + console = { + ...console || {}, + log: global.log, + debug: global.debug, + info: global.info, + warn: global.warn, + error: global.error + } + `); +}; + +module.exports = addConsoleShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios-min.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios-min.js new file mode 100644 index 000000000..b3949b167 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios-min.js @@ -0,0 +1,57 @@ +const axios = require('axios'); +const ivm = require('isolated-vm'); +const { cleanJson } = require('../../../../utils'); + +const addAxiosShimToContext = async (context) => { + await context.evalClosure( + ` + globalThis.axios = (...args) => $0.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + ${['get', 'post', 'put', 'patch', 'delete'] + ?.map( + (method, idx) => + `globalThis.axios.${method} = (...args) => $${ + idx + 1 + }.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg)))` + ) + .join('\n')} + globalThis.requireObject = { + ...globalThis.requireObject, + axios: globalThis.axios, + } + `, + [ + async (...argStrings) => { + console.log(argStrings); + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios(...args) + .then((response) => { + return cleanJson(response?.data); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + ...['get', 'post', 'put', 'patch', 'delete']?.map((method) => async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios[method](...args) + .then((response) => { + return cleanJson(response?.data); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }) + ], + { arguments: { reference: true } } + ); +}; + +module.exports = addAxiosShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios.js new file mode 100644 index 000000000..b6e9d1888 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/axios.js @@ -0,0 +1,121 @@ +const axios = require('axios'); +const ivm = require('isolated-vm'); +const { cleanJson } = require('../../../../utils'); + +const addAxiosShimToContext = async (context) => { + await context.evalClosure( + ` + globalThis.axios = (...args) => $0.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.axios.get = (...args) => $1.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.axios.post = (...args) => $2.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.axios.put = (...args) => $3.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.axios.delete = (...args) => $4.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.axios.patch = (...args) => $5.applySyncPromise(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.requireObject = { + ...globalThis.requireObject, + axios: globalThis.axios, + } + `, + [ + async (...argStrings) => { + console.log(argStrings); + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios + .get(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios + .post(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios + .put(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios + .delete(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + }, + async (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + const res = await axios + .patch(...args) + .then((response) => { + const { status, headers, data } = response || {}; + return cleanJson({ status, headers, data }); + }) + .catch((err) => { + return { + message: err.message + // response: cleanJson(err.response) + }; + }); + return new ivm.ExternalCopy(res).copyInto({ release: true }); + } + ], + { arguments: { reference: true } } + ); +}; + +module.exports = addAxiosShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/index.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/index.js new file mode 100644 index 000000000..341b4860d --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/index.js @@ -0,0 +1,11 @@ +const addAxiosShimToContext = require('./axios'); +const addNanoidShimToContext = require('./nanoid'); +const addUuidShimToContext = require('./uuid'); + +const addLibraryShimsToContext = async (context) => { + await addAxiosShimToContext(context); + await addNanoidShimToContext(context); + await addUuidShimToContext(context); +}; + +module.exports = addLibraryShimsToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/nanoid.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/nanoid.js new file mode 100644 index 000000000..3481c816d --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/nanoid.js @@ -0,0 +1,23 @@ +const ivm = require('isolated-vm'); +const { nanoid } = require('nanoid'); + +const addNanoidShimToContext = async (context) => { + await context.evalClosure( + ` + globalThis.nanoid = {}; + globalThis.nanoid.nanoid = () => $0.applySync(undefined); + globalThis.requireObject = { + ...globalThis.requireObject, + nanoid: globalThis.nanoid + } + `, + [ + () => { + return new ivm.ExternalCopy(nanoid()).copyInto({ release: true }); + } + ], + { arguments: { reference: true } } + ); +}; + +module.exports = addNanoidShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid-min.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid-min.js new file mode 100644 index 000000000..2e1c010e5 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid-min.js @@ -0,0 +1,39 @@ +const ivm = require('isolated-vm'); +const uuid = require('uuid'); +const { MAX, NIL } = uuid; + +const addUuidShimToContext = async (context) => { + await context.evalClosure( + ` + globalThis.uuid = {}; + globalThis.uuid.MAX = $0; + globalThis.uuid.NIL = $1; + ${['version', 'parse', 'stringify', 'v1', 'v1ToV6', 'v3', 'v4', 'v5', 'v6', 'v6ToV1', 'v7', 'validate'] + ?.map( + (fn, idx) => + `globalThis.uuid.${fn} = (...args) => $${ + idx + 2 + }.applySync(undefined, args?.map(arg=>JSON.stringify(arg)));` + ) + .join('\n')} + globalThis.requireObject = { + ...globalThis.requireObject, + uuid: globalThis.uuid, + } + `, + [ + new ivm.ExternalCopy(MAX).copyInto({ release: true }), + new ivm.ExternalCopy(NIL).copyInto({ release: true }), + ...['version', 'parse', 'stringify', 'v1', 'v1ToV6', 'v3', 'v4', 'v5', 'v6', 'v6ToV1', 'v7', 'validate']?.map( + (fn) => + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(uuid[fn](...args)).copyInto({ release: true }); + } + ) + ], + { arguments: { reference: true } } + ); +}; + +module.exports = addUuidShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid.js new file mode 100644 index 000000000..151498bb5 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/lib/uuid.js @@ -0,0 +1,83 @@ +const ivm = require('isolated-vm'); +const { MAX, NIL, parse, stringify, v1, v1ToV6, v3, v4, v5, v6, v6ToV1, v7, validate, version } = require('uuid'); + +const addUuidShimToContext = async (context) => { + await context.evalClosure( + ` + globalThis.uuid = {}; + globalThis.uuid.MAX = $0; + globalThis.uuid.NIL = $1; + globalThis.uuid.version = (...args) => $2.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.parse = (...args) => $3.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.stringify = (...args) => $4.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v1 = (...args) => $5.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v1ToV6 = (...args) => $6.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v3 = (...args) => $7.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v4 = (...args) => $8.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v5 = (...args) => $9.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v6 = (...args) => $10.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v6ToV1 = (...args) => $11.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.v7 = (...args) => $12.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.uuid.validate = (...args) => $13.applySync(undefined, args?.map(arg=>JSON.stringify(arg))); + globalThis.requireObject = { + ...globalThis.requireObject, + uuid: globalThis.uuid, + } + `, + [ + new ivm.ExternalCopy(MAX).copyInto({ release: true }), + new ivm.ExternalCopy(NIL).copyInto({ release: true }), + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(version(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(parse(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(stringify(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v1(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v1ToV6(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v3(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v4(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v5(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v6(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v6ToV1(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(v7(...args)).copyInto({ release: true }); + }, + (...argStrings) => { + let args = argStrings?.map((arg) => JSON.parse(arg)); + return new ivm.ExternalCopy(validate(...args)).copyInto({ release: true }); + } + ], + { arguments: { reference: true } } + ); +}; + +module.exports = addUuidShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/sleep.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/sleep.js new file mode 100644 index 000000000..14ce92e30 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/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; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/shims/test.js b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/test.js new file mode 100644 index 000000000..126f9b346 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/shims/test.js @@ -0,0 +1,57 @@ +const addTestShimToContext = async (context, __brunoTestResults) => { + context.global.setSync('addResult', function (v) { + __brunoTestResults.addResult(v); + }); + + context.global.setSync('getResults', function () { + return __brunoTestResults.getResults(); + }); + + context.evalSync(` + global.expect = require('chai').expect; + global.assert = require('chai').assert; + + global.__brunoTestResults = { + addResult: global.addResult, + getResults: global.getResults, + } + + + global.DummyChaiAssertionError = class DummyChaiAssertionError extends Error { + constructor(message, props, ssf) { + super(message); + this.name = "AssertionError"; + Object.assign(this, props); + } + } + + global.Test = (__brunoTestResults) => async (description, callback) => { + try { + await callback(); + __brunoTestResults.addResult({ description, status: "pass" }); + } catch (error) { + if (error instanceof DummyChaiAssertionError) { + const { message, actual, expected } = error; + __brunoTestResults.addResult({ + description, + status: "fail", + error: message, + actual, + expected, + }); + } else { + __brunoTestResults.addResult({ + description, + status: "fail", + error: error.message || "An unexpected error occurred.", + }); + } + console.log(error); + } + }; + + global.test = Test(__brunoTestResults); + `); +}; + +module.exports = addTestShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm copy/utils/bundle-libraries.js b/packages/bruno-js/src/sandbox/isolatedvm copy/utils/bundle-libraries.js new file mode 100644 index 000000000..2d650ecca --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm copy/utils/bundle-libraries.js @@ -0,0 +1,84 @@ +const rollup = require('rollup'); +const { nodeResolve } = require('@rollup/plugin-node-resolve'); +const commonjs = require('@rollup/plugin-commonjs'); +const fs = require('fs'); +const { terser } = require('rollup-plugin-terser'); + +const bundleLibraries = async () => { + const codeScript = ` + import { expect, assert } from 'chai'; + import { Buffer } from "buffer"; + import moment from "moment"; + import btoa from "btoa"; + import atob from "atob"; + globalThis.expect = expect; + globalThis.assert = assert; + globalThis.moment = moment; + globalThis.btoa = btoa; + globalThis.atob = atob; + globalThis.Buffer = Buffer; + globalThis.requireObject = { + 'chai': { expect, assert }, + 'moment': moment, + 'buffer': { Buffer }, + 'btoa': btoa, + 'atob': atob, + }; +`; + + const config = { + input: { + input: 'inline-code', + plugins: [ + { + name: 'inline-code-plugin', + resolveId(id) { + if (id === 'inline-code') { + return id; + } + return null; + }, + load(id) { + if (id === 'inline-code') { + return codeScript; + } + return null; + } + }, + nodeResolve({ + preferBuiltins: false, + browser: false + }), + commonjs(), + terser() + ] + }, + output: { + file: './src/bundle-browser-rollup.js', + format: 'iife', + name: 'MyBundle' + } + }; + + try { + const bundle = await rollup.rollup(config.input); + const { output } = await bundle.generate(config.output); + fs.writeFileSync( + './src/bundle-browser-rollup.js', + ` + const getBundledCode = () => { + return function(){ + ${output?.map((o) => o.code).join('\n')} + }() + } + module.exports = getBundledCode; + ` + ); + } catch (error) { + console.error('Error while bundling:', error); + } +}; + +bundleLibraries(); + +module.exports = bundleLibraries; diff --git a/packages/bruno-js/src/sandbox/isolatedvm/index.js b/packages/bruno-js/src/sandbox/isolatedvm/index.js index cb35a3024..804cab2df 100644 --- a/packages/bruno-js/src/sandbox/isolatedvm/index.js +++ b/packages/bruno-js/src/sandbox/isolatedvm/index.js @@ -1,10 +1,17 @@ const ivm = require('isolated-vm'); const addBruShimToContext = require('./shims/bru'); +const addQuickBruShimToContext = require('./shims/bru-quick'); + const addBrunoRequestShimToContext = require('./shims/bruno-request'); -const addConsoleShimToContext = require('./shims/console'); +const addQuickBrunoRequestShimToContext = require('./shims/bruno-request-quick'); + const addBrunoResponseShimToContext = require('./shims/bruno-response'); -const addTestShimToContext = require('./shims/test'); +const addQuickBrunoResponseShimToContext = require('./shims/bruno-response-quick'); + +const addConsoleShimToContext = require('./shims/console'); +const addTestShimToContext = require('./shims/test-quick'); const addLibraryShimsToContext = require('./shims/lib'); +const { getQuickJS, newQuickJSAsyncWASMModule } = require('quickjs-emscripten'); // execute `npm run build:isolated-vm:inbuilt-modules` if the below file doesn't exist const getBundledCode = require('../../bundle-browser-rollup'); @@ -72,93 +79,151 @@ const executeInIsolatedVMAsync = async ({ return toNumber(externalScript); } let result; - const isolate = new ivm.Isolate(); try { - const context = await isolate.createContext(); - await context.global.set('global', context.global.derefInto()); + // const QuickJS = await getQuickJS() + // const vm = QuickJS.newContext(); - context.evalSync(` - let bru = {}; - let req = {}; - let res = {}; + const module = await newQuickJSAsyncWASMModule() + const runtime = module.newRuntime() + const vm = runtime.newContext() + // context.evalSync(` + // let bru = {}; + // let req = {}; + // let res = {}; + // let console = {}; + // global.requireObject = {}; + // `); + + const bundledCode = getBundledCode?.toString() || ''; + let bundledScript = `(${bundledCode})()`; + + bundledScript += ` + globalThis.require = (module) => { + return globalThis.requireObject[module]; + } + `; + + bundledScript += ` + let bru = { + cwd: __bruno__cwd, + getEnvName: __bruno__getEnvName, + getProcessEnv: __bruno__getProcessEnv, + getEnvVar: __bruno__getEnvVar, + setEnvVar: __bruno__setEnvVar, + getVar: __bruno__getVar, + setVar: __bruno__setVar, + setNextRequest: __bruno__setNextRequest, + visualize: __bruno__visualize, + getSecretVar: __bruno__getSecretVar + }; + let req = { + url: __bruno__req__url, + method: __bruno__req__method, + headers: __bruno__req__headers, + body: __bruno__req__body, + timeout: __bruno__req__timeout, + getUrl: __bruno__req__getUrl, + setUrl: __bruno__req__setUrl, + getMethod: __bruno__req__getMethod, + getAuthMode: __bruno__req__getAuthMode, + setMethod: __bruno__req__setMethod, + getHeaders: __bruno__req__getHeaders, + setHeaders: __bruno__req__setHeaders, + getHeader: __bruno__req__getHeader, + setHeader: __bruno__req__setHeader + getBody: __bruno__req__getBody, + setBody: __bruno__req__setBody, + setMaxRedirects: __bruno__req__setMaxRedirects, + getTimeout: __bruno__req__getTimeout, + setTimeout: __bruno__req__setTimeout + }; + let res = { + status: __bruno__res__status, + headers: __bruno__res__headers, + body: __bruno__res__body, + responseTime: __bruno__res__responseTime, + getStatus: __bruno__res__getStatus, + getHeader: __bruno__res__getHeader, + getHeaders: __bruno__res__getHeaders, + getBody: __bruno__res__getBody, + getResponseTime: __bruno__res__getResponseTime + }; let console = {}; - global.requireObject = {}; - `); - - context.global.setSync('log', function (...args) { - console.debug(...args); - }); - - try { - const bundledCode = getBundledCode?.toString() || ''; - await context.eval(`(${bundledCode})()`); - } catch (err) { - console.debug('Error bundling libraries', err); - } + `; const { bru, req, res, test, __brunoTestResults, console: consoleFn } = externalContext; - bru && addBruShimToContext(context, bru); - req && addBrunoRequestShimToContext(context, req); - res && addBrunoResponseShimToContext(context, res); - consoleFn && addConsoleShimToContext(context, consoleFn); - addSleepShimToContext(context); + bru && addQuickBruShimToContext(vm, bru); + req && addQuickBrunoRequestShimToContext(vm, req); + res && addQuickBrunoResponseShimToContext(vm, res); + // consoleFn && addConsoleShimToContext(context, consoleFn); + // addSleepShimToContext(context); - await context.eval( - ` - global.require = (module) => { - return global.requireObject[module]; + // await addLibraryShimsToContext(context); + + test && __brunoTestResults && addTestShimToContext(vm, __brunoTestResults); + + bundledScript += ` + globalThis.expect = require('chai').expect; + globalThis.assert = require('chai').assert; + + globalThis.__brunoTestResults = { + addResult: globalThis.__bruno__addResult, + getResults: globalThis.__bruno__getResults, + } + + globalThis.DummyChaiAssertionError = class DummyChaiAssertionError extends Error { + constructor(message, props, ssf) { + super(message); + this.name = "AssertionError"; + Object.assign(this, props); } - ` - ); + } - await addLibraryShimsToContext(context); - - test && __brunoTestResults && (await addTestShimToContext(context, __brunoTestResults)); - - context.global.setSync('setResult', function (arg) { - result = arg; - }); - - 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:'); + globalThis.Test = (__brunoTestResults) => (description, callback) => { try { - ${externalScript} + callback(); + __brunoTestResults.addResult({ description, status: "pass" }); } catch (error) { - console?.debug && console.debug('isolated-vm:execution-end:with-error', error?.message); + if (error instanceof DummyChaiAssertionError) { + const { message, actual, expected } = error; + __brunoTestResults.addResult({ + description, + status: "fail", + error: message, + actual, + expected, + }); + } else { + globalThis.__bruno__addResult({ + description, + status: "fail", + error: error.message || "An unexpected error occurred.", + }); + } + // console.log(error); } - console?.debug && console.debug('isolated-vm:execution-end:'); - resolve(); - }); + }; + + globalThis.test = Test(__brunoTestResults); `; - const templateLiteralText = ` - let value = \`${externalScript}\`; - setResult(value); - `; + bundledScript += externalScript; - const jsExpressionText = ` - let value = ${externalScript}; - setResult(value); - `; - - let scriptText = - scriptType === 'template-literal' - ? templateLiteralText - : scriptType === 'expression' - ? jsExpressionText - : jsScriptText; - - const script = await isolate.compileScript(scriptText); - await script.run(context); + const result = await vm.evalCodeAsync(bundledScript); + console.log('Result:', result); + if (result.error) { + console.log("Execution failed:", vm.dump(result.error)) + result.error.dispose() + } else { + console.log("Success:", vm.dump(result.value)) + result.value.dispose(); + } + vm.dispose(); return result; } catch (error) { console.error('Error executing the script!', error); } - isolate.dispose(); }; module.exports = { diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/bru-quick.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/bru-quick.js new file mode 100644 index 000000000..dd29c8435 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/bru-quick.js @@ -0,0 +1,65 @@ +const { marshallToVm } = require('../utils'); + +const addBruShimToContext = (vm, bru) => { + let cwd = vm.newFunction('cwd', function () { + return marshallToVm(bru.cwd(), vm); + }); + vm.setProp(vm.global, "__bruno__cwd", cwd) + cwd.dispose(); + + let getEnvName = vm.newFunction('getEnvName', function () { + return marshallToVm(bru.getEnvName(), vm); + }); + vm.setProp(vm.global, "__bruno__getEnvName", getEnvName); + getEnvName.dispose(); + + let getProcessEnv = vm.newFunction('getProcessEnv', function (key) { + return marshallToVm(bru.getProcessEnv(vm.dump(key)), vm); + }); + vm.setProp(vm.global, "__bruno__getProcessEnv", getProcessEnv); + getProcessEnv.dispose(); + + let getEnvVar = vm.newFunction('getEnvVar', function (key) { + return marshallToVm(bru.getEnvVar(vm.dump(key)), vm); + }); + vm.setProp(vm.global, "__bruno__getEnvVar", getEnvVar); + getEnvVar.dispose(); + + let setEnvVar = vm.newFunction('setEnvVar', function (key, value) { + bru.setEnvVar(vm.dump(key), vm.dump(value)); + }); + vm.setProp(vm.global, "__bruno__setEnvVar", setEnvVar); + setEnvVar.dispose(); + + let getVar = vm.newFunction('getVar', function (key) { + return marshallToVm(bru.getVar(vm.dump(key)), vm); + }); + vm.setProp(vm.global, "__bruno__getVar", getVar); + getVar.dispose(); + + let setVar = vm.newFunction('setVar', function (key, value) { + bru.setVar(vm.dump(key), vm.dump(value)); + }); + vm.setProp(vm.global, "__bruno__setVar", setVar); + setVar.dispose(); + + let setNextRequest = vm.newFunction('setNextRequest', function (nextRequest) { + bru.setNextRequest(vm.dump(nextRequest)); + }); + vm.setProp(vm.global, "__bruno__setNextRequest", setNextRequest); + setNextRequest.dispose(); + + let visualize = vm.newFunction('visualize', function (htmlString) { + bru.visualize(vm.dump(htmlString)); + }); + vm.setProp(vm.global, "__bruno__visualize", visualize); + visualize.dispose(); + + let getSecretVar = vm.newFunction('getSecretVar', function (key) { + return marshallToVm(bru.getSecretVar(vm.dump(key)), vm); + }); + vm.setProp(vm.global, "__bruno__getSecretVar", getSecretVar); + getSecretVar.dispose(); +}; + +module.exports = addBruShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-request-quick.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-request-quick.js new file mode 100644 index 000000000..03848284c --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-request-quick.js @@ -0,0 +1,107 @@ +const { marshallToVm } = require('../utils'); + +const addBrunoRequestShimToContext = (vm, req) => { + const url = marshallToVm(req.getUrl(), vm); + const method = marshallToVm(req.getMethod(), vm); + const headers = marshallToVm(req.getHeaders(), vm); + const body = marshallToVm(req.getBody(), vm); + const timeout = marshallToVm(req.getTimeout(), vm); + + vm.setProp(vm.global, '__bruno__requrl', url); + vm.setProp(vm.global, '__bruno__reqmethod', method); + vm.setProp(vm.global, '__bruno__reqheaders', headers); + vm.setProp(vm.global, '__bruno__reqbody', body); + vm.setProp(vm.global, '__bruno__reqtimeout', timeout); + + url.dispose(); + method.dispose(); + headers.dispose(); + body.dispose(); + timeout.dispose(); + + // let getUrl = vm.newFunction('__bruno__req__getUrl', function () { + // return marshallToVm(req.getUrl(), vm); + // }); + // vm.setProp(vm.global, 'getUrl', getUrl); + // getUrl.dispose(); + + // let setUrl = vm.newFunction('__bruno__req__setUrl', function (url) { + // req.setUrl(vm.dump(url)); + // }); + // vm.setProp(vm.global, 'setUrl', setUrl); + // setUrl.dispose(); + + // let getMethod = vm.newFunction('__bruno__req__getMethod', function () { + // return marshallToVm(req.getMethod(), vm); + // }); + // vm.setProp(vm.global, 'getMethod', getMethod); + // getMethod.dispose(); + + // let getAuthMode = vm.newFunction('__bruno__req__getAuthMode', function () { + // return marshallToVm(req.getAuthMode(), vm); + // }); + // vm.setProp(vm.global, 'getAuthMode', getAuthMode); + // getAuthMode.dispose(); + + // let setMethod = vm.newFunction('__bruno__req__setMethod', function (method) { + // req.setMethod(vm.dump(method)); + // }); + // vm.setProp(vm.global, 'setMethod', setMethod); + // setMethod.dispose(); + + // let getHeaders = vm.newFunction('__bruno__req__getHeaders', function () { + // return marshallToVm(req.getHeaders(), vm); + // }); + // vm.setProp(vm.global, 'getHeaders', getHeaders); + // getHeaders.dispose(); + + // let setHeaders = vm.newFunction('__bruno__req__setHeaders', function (headers) { + // req.setHeaders(vm.dump(headers)); + // }); + // vm.setProp(vm.global, 'setHeaders', setHeaders); + // setHeaders.dispose(); + + // let getHeader = vm.newFunction('__bruno__req__getHeader', function (name) { + // return marshallToVm(req.getHeader(vm.dump(name)), vm); + // }); + // vm.setProp(vm.global, 'getHeader', getHeader); + // getHeader.dispose(); + + // let setHeader = vm.newFunction('__bruno__req__setHeader', function (name, value) { + // req.setHeader(vm.dump(name), vm.dump(value)); + // }); + // vm.setProp(vm.global, 'setHeader', setHeader); + // setHeader.dispose(); + + // let getBody = vm.newFunction('__bruno__req__getBody', function () { + // return marshallToVm(req.getBody(), vm); + // }); + // vm.setProp(vm.global, 'getBody', getBody); + // getBody.dispose(); + + // let setBody = vm.newFunction('__bruno__req__setBody', function (data) { + // req.setBody(vm.dump(data)); + // }); + // vm.setProp(vm.global, 'setBody', setBody); + // setBody.dispose(); + + // let setMaxRedirects = vm.newFunction('__bruno__req__setMaxRedirects', function (maxRedirects) { + // req.setMaxRedirects(vm.dump(maxRedirects)); + // }); + // vm.setProp(vm.global, 'setMaxRedirects', setMaxRedirects); + // setMaxRedirects.dispose(); + + // let getTimeout = vm.newFunction('__bruno__req__getTimeout', function () { + // return marshallToVm(req.getTimeout(), vm); + // }); + // vm.setProp(vm.global, 'getTimeout', getTimeout); + // getTimeout.dispose(); + + // let setTimeout = vm.newFunction('__bruno__req__setTimeout', function (timeout) { + // req.setTimeout(vm.dump(timeout)); + // }); + // vm.setProp(vm.global, 'setTimeout', setTimeout); + // setTimeout.dispose(); +}; + +module.exports = addBrunoRequestShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-response-quick.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-response-quick.js new file mode 100644 index 000000000..c7909b2c6 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/bruno-response-quick.js @@ -0,0 +1,51 @@ + +const { marshallToVm } = require('../utils'); + +const addBrunoResponseShimToContext = (vm, res) => { + const status = marshallToVm(res?.status, vm); + const headers = marshallToVm(res?.headers, vm); + const body = marshallToVm(res?.body, vm); + const responseTime = marshallToVm(res?.responseTime, vm); + + vm.setProp(vm.global, '__bruno__res__status', status); + vm.setProp(vm.global, '__bruno__res__headers', headers); + vm.setProp(vm.global, '__bruno__res__body', body); + vm.setProp(vm.global, '__bruno__res__responseTime', responseTime); + + status.dispose(); + headers.dispose(); + body.dispose(); + responseTime.dispose(); + + let getStatus = vm.newFunction('getStatus', function () { + return marshallToVm(res.getStatus(), vm); + }); + vm.setProp(vm.global, '__bruno__res__getStatus', getStatus); + getStatus.dispose(); + + let getHeader = vm.newFunction('getHeader', function (name) { + return marshallToVm(res.getHeader(vm.dump(name)), vm); + }); + vm.setProp(vm.global, '__bruno__res__getHeader', getHeader); + getHeader.dispose(); + + let getHeaders = vm.newFunction('getHeaders', function () { + return marshallToVm(res.getHeaders(), vm); + }); + vm.setProp(vm.global, '__bruno__res__getHeaders', getHeaders); + getHeaders.dispose(); + + let getBody = vm.newFunction('getBody', function () { + return marshallToVm(res.getBody(), vm); + }); + vm.setProp(vm.global, '__bruno__res__getBody', getBody); + getBody.dispose(); + + let getResponseTime = vm.newFunction('getResponseTime', function () { + return marshallToVm(res.getResponseTime(), vm); + }); + vm.setProp(vm.global, '__bruno__res__getResponseTime', getResponseTime); + getResponseTime.dispose(); +}; + +module.exports = addBrunoResponseShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm/shims/test-quick.js b/packages/bruno-js/src/sandbox/isolatedvm/shims/test-quick.js new file mode 100644 index 000000000..38d2971a6 --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/shims/test-quick.js @@ -0,0 +1,17 @@ +const { marshallToVm } = require('../utils'); + +const addBruShimToContext = (vm, __brunoTestResults) => { + let addResult = vm.newFunction('addResult', function (v) { + __brunoTestResults.addResult(vm.dump(v)); + }); + vm.setProp(vm.global, "__bruno__addResult", addResult); + addResult.dispose(); + + let getResults = vm.newFunction('getResults', function () { + return marshallToVm(__brunoTestResults.getResults(), vm); + }); + vm.setProp(vm.global, "__bruno__getResults", getResults); + getResults.dispose(); +}; + +module.exports = addBruShimToContext; diff --git a/packages/bruno-js/src/sandbox/isolatedvm/utils/bundle-libraries.js b/packages/bruno-js/src/sandbox/isolatedvm/utils/bundle-libraries.js index f909ec405..2d650ecca 100644 --- a/packages/bruno-js/src/sandbox/isolatedvm/utils/bundle-libraries.js +++ b/packages/bruno-js/src/sandbox/isolatedvm/utils/bundle-libraries.js @@ -11,13 +11,13 @@ const bundleLibraries = async () => { import moment from "moment"; import btoa from "btoa"; import atob from "atob"; - global.expect = expect; - global.assert = assert; - global.moment = moment; - global.btoa = btoa; - global.atob = atob; - global.Buffer = Buffer; - global.requireObject = { + globalThis.expect = expect; + globalThis.assert = assert; + globalThis.moment = moment; + globalThis.btoa = btoa; + globalThis.atob = atob; + globalThis.Buffer = Buffer; + globalThis.requireObject = { 'chai': { expect, assert }, 'moment': moment, 'buffer': { Buffer }, diff --git a/packages/bruno-js/src/sandbox/isolatedvm/utils/index.js b/packages/bruno-js/src/sandbox/isolatedvm/utils/index.js new file mode 100644 index 000000000..17a16fadd --- /dev/null +++ b/packages/bruno-js/src/sandbox/isolatedvm/utils/index.js @@ -0,0 +1,31 @@ +const marshallToVm = (value, vm) => { + if (value === undefined) { + return vm.undefined; + } + + if (typeof value === "string") { + return vm.newString(value); + } else if (typeof value === "number") { + return vm.newNumber(value); + } else if (typeof value === "boolean") { + return vm.newBoolean(value); + } else if (typeof value === "object") { + if (Array.isArray(value)) { + const arr = vm.newArray(); + for (let i = 0; i < value.length; i++) { + vm.setElement(arr, i, marshallToVm(value[i])); + } + return arr; + } else { + const obj = vm.newObject(); + for (const key in value) { + vm.setProp(obj, key, marshallToVm(value[key])); + } + return obj; + } + } +}; + +module.exports = { + marshallToVm +}; diff --git a/packages/bruno-tests/collection/environments/Prod.bru b/packages/bruno-tests/collection/environments/Prod.bru index 7f4bf5d5f..4bea1e77a 100644 --- a/packages/bruno-tests/collection/environments/Prod.bru +++ b/packages/bruno-tests/collection/environments/Prod.bru @@ -8,6 +8,3 @@ vars { foo: bar testSetEnvVar: bruno-29653 } -vars:secret [ - bruno -] diff --git a/packages/bruno-tests/collection/ping.bru b/packages/bruno-tests/collection/ping.bru index bbefac464..281c75074 100644 --- a/packages/bruno-tests/collection/ping.bru +++ b/packages/bruno-tests/collection/ping.bru @@ -5,56 +5,28 @@ meta { } get { - url: {{host}}/ping + url: https://testbench-sanity.usebruno.com/ping body: none auth: none } -auth:awsv4 { - accessKeyId: a - secretAccessKey: b - sessionToken: c - service: d - region: e - profileName: f -} - -vars:pre-request { - m4: true - pong: pong -} - -assert { - res.status: eq 200 - res.responseTime: lte 2000 - res.body: eq {{pong}} +script:pre-request { + // const moment = require("moment"); + // function a() { + // return moment().format("hh:mm"); + // return test; + // } + // a(); } tests { - test("should ping pong", function() { - const data = res.getBody(); - expect(data).to.equal(bru.getRequestVar("pong")); + test("should get var in scripts", function() { + expect("bruno-test-87267").to.equal("bruno-test-87267"); }); -} - -docs { - # API Documentation - - ## Introduction - - Welcome to the API documentation for [Your API Name]. This document provides instructions on how to make requests to the API and covers available authentication methods. - - ## Authentication - - Before making requests to the API, you need to authenticate your application. [Your API Name] supports the following authentication methods: - - ### API Key - - To use API key authentication, include your API key in the request headers as follows: - - ```http - GET /api/endpoint - Host: api.example.com - Authorization: Bearer YOUR_API_KEY - + // const moment = require("moment"); + // function a() { + // return moment().format("hh:mm"); + // return test; + // } + // a(); } diff --git a/packages/bruno-tests/collection/scripting/api/bru/getEnvName.bru b/packages/bruno-tests/collection/scripting/api/bru/getEnvName.bru index f05052508..4e7c37ec3 100644 --- a/packages/bruno-tests/collection/scripting/api/bru/getEnvName.bru +++ b/packages/bruno-tests/collection/scripting/api/bru/getEnvName.bru @@ -10,7 +10,6 @@ get { auth: none } - script:pre-request { const envName = bru.getEnvName(); bru.setVar("testEnvName", envName); @@ -21,4 +20,4 @@ tests { const testEnvName = bru.getVar("testEnvName"); expect(testEnvName).to.equal("Prod"); }); -} \ No newline at end of file +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/setVar.bru b/packages/bruno-tests/collection/scripting/api/bru/setVar.bru index cfd701dcd..a155117c9 100644 --- a/packages/bruno-tests/collection/scripting/api/bru/setVar.bru +++ b/packages/bruno-tests/collection/scripting/api/bru/setVar.bru @@ -10,7 +10,6 @@ get { auth: none } - script:post-response { bru.setVar("testSetVar", "bruno-test-87267") } @@ -20,4 +19,4 @@ tests { const testSetVar = bru.getVar("testSetVar"); expect(testSetVar).to.equal("bruno-test-87267"); }); -} \ No newline at end of file +}