feat: safe mode quickjs updates (#2866)

* wip: safe mode updates

* wip: safe mode quickjs updates

* wip: safe mode updates

* wip: safe mode updates

* wip: safe mode updates
This commit is contained in:
lohit 2024-08-20 19:14:03 +05:30 committed by GitHub
parent 35376305ae
commit 64cb45c09a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 113 additions and 91 deletions

8
package-lock.json generated
View File

@ -8340,6 +8340,12 @@
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"node_modules/crypto-js-3.1.9-1": {
"name": "crypto-js",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz",
"integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q=="
},
"node_modules/crypto-random-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
@ -20595,12 +20601,14 @@
"chai": "^4.3.7",
"chai-string": "^1.5.0",
"crypto-js": "^4.1.1",
"crypto-js-3.1.9-1": "npm:crypto-js@^3.1.9-1",
"json-query": "^2.2.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nanoid": "3.3.4",
"node-fetch": "^2.7.0",
"node-vault": "^0.10.2",
"path": "^0.12.7",
"quickjs-emscripten": "^0.29.2",
"uuid": "^9.0.0"
},

View File

@ -25,12 +25,14 @@
"chai": "^4.3.7",
"chai-string": "^1.5.0",
"crypto-js": "^4.1.1",
"crypto-js-3.1.9-1": "npm:crypto-js@^3.1.9-1",
"json-query": "^2.2.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nanoid": "3.3.4",
"node-fetch": "^2.7.0",
"node-vault": "^0.10.2",
"path": "^0.12.7",
"quickjs-emscripten": "^0.29.2",
"uuid": "^9.0.0"
},

View File

@ -84,6 +84,9 @@ class TestRuntime {
};
}
// add 'await' prefix to the test function calls
testsFile = testsFile.replace(/^(?!\s*await\s)(test\([^)]*\))/gm, 'await $1');
const context = {
test,
bru,

View File

@ -11,18 +11,22 @@ const bundleLibraries = async () => {
import moment from "moment";
import btoa from "btoa";
import atob from "atob";
import * as CryptoJS from "crypto-js-3.1.9-1";
globalThis.expect = expect;
globalThis.assert = assert;
globalThis.moment = moment;
globalThis.btoa = btoa;
globalThis.atob = atob;
globalThis.Buffer = Buffer;
globalThis.CryptoJS = CryptoJS;
globalThis.requireObject = {
...(globalThis.requireObject || {}),
'chai': { expect, assert },
'moment': moment,
'buffer': { Buffer },
'btoa': btoa,
'atob': atob,
'crypto-js': CryptoJS
};
`;

View File

@ -9,6 +9,7 @@ const { newQuickJSWASMModule, memoizePromiseFactory } = require('quickjs-emscrip
// execute `npm run sandbox:bundle-libraries` if the below file doesn't exist
const getBundledCode = require('../bundle-browser-rollup');
const addPathShimToContext = require('./shims/lib/path');
let QuickJSSyncContext;
const loader = memoizePromiseFactory(() => newQuickJSWASMModule());
@ -64,14 +65,14 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
const vm = module.newContext();
const bundledCode = getBundledCode?.toString() || '';
const moduleLoaderCode = function() {
const moduleLoaderCode = function () {
return `
globalThis.require = (mod) => {
let lib = globalThis.requireObject[mod];
if (lib) {
return lib;
}
else {
else if(mod?.startsWith('.') || mod?.startsWith?.(bru.cwd())){
// fetch local module
let localModuleCode = globalThis.__brunoLoadLocalModule(mod);
@ -79,7 +80,12 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
(function (){
const initModuleExportsCode = "const module = { exports: {} };"
const copyModuleExportsCode = "\\n;globalThis.requireObject[mod] = module.exports;";
eval(initModuleExportsCode + localModuleCode + copyModuleExportsCode);
const patchedRequire = ${`
"\\n;" +
"let require = (subModule) => globalThis.require(path.resolve(bru.cwd(), mod, '..', subModule))" +
"\\n;"
`}
eval(initModuleExportsCode + patchedRequire + localModuleCode + copyModuleExportsCode);
})();
// resolve module
@ -103,6 +109,7 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
res && addBrunoResponseShimToContext(vm, res);
consoleFn && addConsoleShimToContext(vm, consoleFn);
addLocalModuleLoaderShimToContext(vm, collectionPath);
addPathShimToContext(vm);
await addLibraryShimsToContext(vm);
@ -135,6 +142,7 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
return;
} catch (error) {
console.error('Error executing the script!', error);
throw new Error(error);
}
};

View File

@ -1,13 +1,13 @@
const addAxiosShimToContext = require('./axios');
const addNanoidShimToContext = require('./nanoid');
const addNodeFetchShimToContext = require('./node-fetch');
const addPathShimToContext = require('./path');
const addUuidShimToContext = require('./uuid');
const addLibraryShimsToContext = async (vm) => {
await addNanoidShimToContext(vm);
await addAxiosShimToContext(vm);
await addNodeFetchShimToContext(vm);
await addUuidShimToContext(vm);
await addPathShimToContext(vm);
};
module.exports = addLibraryShimsToContext;

View File

@ -1,41 +0,0 @@
const fetch = require('node-fetch');
const { cleanJson } = require('../../../../utils');
const { marshallToVm } = require('../../utils');
const addNodeFetchShimToContext = async (vm) => {
const nodeFetchHandle = vm.newFunction('node_fetch', (...args) => {
const nativeArgs = args.map(vm.dump);
const promise = vm.newPromise();
fetch(...nativeArgs)
.then(async (response) => {
const { status, headers } = response || {};
const data = await response.json();
promise.resolve(marshallToVm(cleanJson({ status, headers, data }), vm));
})
.catch((err) => {
promise.resolve(
marshallToVm(
cleanJson({
message: err.message
}),
vm
)
);
});
promise.settled.then(vm.runtime.executePendingJobs);
return promise.handle;
});
nodeFetchHandle.consume((handle) => vm.setProp(vm.global, `__bruno__node_fetch`, handle));
vm.evalCode(
`
globalThis.nodeFetch = __bruno__node_fetch;
globalThis.requireObject = {
...globalThis.requireObject,
'node-fetch': globalThis.nodeFetch,
}
`
);
};
module.exports = addNodeFetchShimToContext;

View File

@ -0,0 +1,28 @@
const path = require('path');
const { marshallToVm } = require('../../utils');
const fns = ['resolve'];
const addPathShimToContext = async (vm) => {
fns.forEach((fn) => {
let fnHandle = vm.newFunction(fn, function (...args) {
const nativeArgs = args.map(vm.dump);
return marshallToVm(path[fn](...nativeArgs), vm);
});
vm.setProp(vm.global, `__bruno__path__${fn}`, fnHandle);
fnHandle.dispose();
});
vm.evalCode(
`
globalThis.path = {};
${fns?.map((fn, idx) => `globalThis.path.${fn} = __bruno__path__${fn}`).join('\n')}
globalThis.requireObject = {
...(globalThis.requireObject || {}),
path: globalThis.path,
}
`
);
};
module.exports = addPathShimToContext;

View File

@ -1,4 +1,4 @@
const { PI } = require('./lib/constants');
const { PI } = require('./constants');
const sum = (a, b) => a + b;
const areaOfCircle = (radius) => PI * radius * radius;
@ -6,4 +6,4 @@ const areaOfCircle = (radius) => PI * radius * radius;
module.exports = {
sum,
areaOfCircle
};
};

View File

@ -9,8 +9,3 @@ get {
body: none
auth: none
}
script:pre-request {
var CryptoJS = require("crypto-js");
console.log(CryptoJS);
}

View File

@ -0,0 +1,33 @@
meta {
name: crypto-js-pre-request-script
type: http
seq: 1
}
get {
url: {{host}}/ping
body: none
auth: none
}
script:pre-request {
var CryptoJS = require("crypto-js");
// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();
// Decrypt
var bytes = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);
bru.setVar('crypto-test-message', originalText);
}
tests {
test("crypto message", function() {
const data = bru.getVar('crypto-test-message');
bru.setVar('crypto-test-message', null);
expect(data).to.eql('my message');
});
}

View File

@ -13,5 +13,14 @@ get {
script:pre-request {
const { nanoid } = require("nanoid");
req.setHeader("transaction-id", nanoid());
bru.setVar("nanoid-test-id", nanoid());
}
tests {
test("nanoid var", function() {
const id = bru.getVar('nanoid-test-id');
let isValidNanoid = /^[a-zA-Z0-9_-]{21}$/.test(id)
bru.setVar('nanoid-test-id', null);
expect(isValidNanoid).to.eql(true);
});
}

View File

@ -1,36 +0,0 @@
meta {
name: node-fetch-pre-req-script
type: http
seq: 1
}
get {
url: {{host}}/ping
body: none
auth: none
}
script:pre-request {
const fetch = require("node-fetch");
const url = "https://testbench-sanity.usebruno.com/api/echo/json";
const response = await fetch(url, {
method: 'post',
body: JSON.stringify({hello:'bruno'}),
headers: {'Content-Type': 'application/json'}
});
req.setBody(response.data);
req.setMethod("POST");
req.setUrl(url);
}
tests {
test("req.getBody()", function() {
const data = res.getBody();
expect(data).to.eql({
"hello": "bruno"
});
});
}

View File

@ -13,5 +13,14 @@ get {
script:pre-request {
const { v4 } = require("uuid");
req.setHeader("transaction-id", v4());
bru.setVar("uuid-test-id", v4());
}
tests {
test("uuid var", function() {
const id = bru.getVar('uuid-test-id');
let isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id);
bru.setVar('uuid-test-id', null);
expect(isValidUuid).to.eql(true);
});
}