@usebruno/js 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -12
- package/src/bru.js +37 -15
- package/src/bruno-request.js +66 -8
- package/src/bruno-response.js +15 -2
- package/src/runtime/assert-runtime.js +5 -2
- package/src/runtime/script-runtime.js +18 -207
- package/src/runtime/test-runtime.js +12 -106
- package/src/runtime/vars-runtime.js +3 -2
- package/src/sandbox/node-vm/cjs-loader.js +369 -0
- package/src/sandbox/node-vm/constants.js +99 -0
- package/src/sandbox/node-vm/index.js +58 -188
- package/src/sandbox/node-vm/index.spec.js +1198 -0
- package/src/sandbox/node-vm/utils.js +42 -0
- package/src/sandbox/quickjs/index.js +2 -2
- package/src/sandbox/quickjs/shims/bru.js +9 -3
- package/src/sandbox/quickjs/shims/bruno-request.js +27 -0
- package/src/sandbox/quickjs/shims/lib/crypto-utils.js +7 -9
- package/src/sandbox/quickjs/shims/lib/crypto-utils.spec.js +6 -6
- package/src/sandbox/quickjs/shims/lib/utils.js +1 -1
- package/src/utils/results.js +4 -4
- package/src/utils.js +6 -6
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const nodeModule = require('node:module');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if a module is a Node.js builtin
|
|
6
|
+
* @param {string} moduleName - Module name to check
|
|
7
|
+
* @returns {boolean} True if module is a builtin
|
|
8
|
+
*/
|
|
9
|
+
function isBuiltinModule(moduleName) {
|
|
10
|
+
const normalized = moduleName.startsWith('node:') ? moduleName.slice(5) : moduleName;
|
|
11
|
+
return nodeModule.builtinModules.includes(normalized);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validate that a path is within allowed context roots
|
|
16
|
+
* @param {string} normalizedPath - Normalized file path
|
|
17
|
+
* @param {Array<string>} additionalContextRootsAbsolute - Allowed roots
|
|
18
|
+
* @returns {boolean} True if path is within allowed roots
|
|
19
|
+
*/
|
|
20
|
+
function isPathWithinAllowedRoots(normalizedPath, additionalContextRootsAbsolute) {
|
|
21
|
+
return additionalContextRootsAbsolute.some((allowedRoot) => {
|
|
22
|
+
const normalizedAllowedRoot = path.normalize(allowedRoot);
|
|
23
|
+
const relativePath = path.relative(normalizedAllowedRoot, normalizedPath);
|
|
24
|
+
return !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ScriptError extends Error {
|
|
29
|
+
constructor(error, script) {
|
|
30
|
+
super(error.message);
|
|
31
|
+
this.name = 'ScriptError';
|
|
32
|
+
this.originalError = error;
|
|
33
|
+
this.script = script;
|
|
34
|
+
this.stack = error.stack;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
isBuiltinModule,
|
|
40
|
+
isPathWithinAllowedRoots,
|
|
41
|
+
ScriptError
|
|
42
|
+
};
|
|
@@ -24,7 +24,7 @@ const toNumber = (value) => {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const removeQuotes = (str) => {
|
|
27
|
-
if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith(
|
|
27
|
+
if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith('\'') && str.endsWith('\''))) {
|
|
28
28
|
return str.slice(1, -1);
|
|
29
29
|
}
|
|
30
30
|
return str;
|
|
@@ -36,7 +36,7 @@ const executeQuickJsVm = ({ script: externalScript, context: externalContext, sc
|
|
|
36
36
|
}
|
|
37
37
|
externalScript = externalScript?.trim();
|
|
38
38
|
|
|
39
|
-
if(scriptType === 'template-literal') {
|
|
39
|
+
if (scriptType === 'template-literal') {
|
|
40
40
|
if (!isNaN(Number(externalScript))) {
|
|
41
41
|
const number = Number(externalScript);
|
|
42
42
|
|
|
@@ -24,6 +24,12 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
24
24
|
vm.setProp(bruObject, 'getCollectionName', getCollectionName);
|
|
25
25
|
getCollectionName.dispose();
|
|
26
26
|
|
|
27
|
+
let isSafeMode = vm.newFunction('isSafeMode', function () {
|
|
28
|
+
return marshallToVm(bru.isSafeMode(), vm);
|
|
29
|
+
});
|
|
30
|
+
vm.setProp(bruObject, 'isSafeMode', isSafeMode);
|
|
31
|
+
isSafeMode.dispose();
|
|
32
|
+
|
|
27
33
|
let getProcessEnv = vm.newFunction('getProcessEnv', function (key) {
|
|
28
34
|
return marshallToVm(bru.getProcessEnv(vm.dump(key)), vm);
|
|
29
35
|
});
|
|
@@ -341,7 +347,7 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
341
347
|
const promise = vm.newPromise();
|
|
342
348
|
const dumpedUrl = vm.dump(url);
|
|
343
349
|
const dumpedNameOrObj = vm.dump(nameOrCookieObj);
|
|
344
|
-
|
|
350
|
+
|
|
345
351
|
// Check if the second argument is an object (cookie object case)
|
|
346
352
|
if (typeof dumpedNameOrObj === 'object' && dumpedNameOrObj !== null) {
|
|
347
353
|
// Cookie object case: setCookie(url, cookieObject, callback)
|
|
@@ -363,7 +369,7 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
363
369
|
}
|
|
364
370
|
});
|
|
365
371
|
}
|
|
366
|
-
|
|
372
|
+
|
|
367
373
|
promise.settled.then(vm.runtime.executePendingJobs);
|
|
368
374
|
return promise.handle;
|
|
369
375
|
});
|
|
@@ -371,7 +377,7 @@ const addBruShimToContext = (vm, bru) => {
|
|
|
371
377
|
|
|
372
378
|
const _setCookiesFn = vm.newFunction('_setCookies', (url, cookiesArray) => {
|
|
373
379
|
const promise = vm.newPromise();
|
|
374
|
-
|
|
380
|
+
|
|
375
381
|
nativeJar.setCookies(vm.dump(url), vm.dump(cookiesArray), (err) => {
|
|
376
382
|
if (err) {
|
|
377
383
|
promise.reject(marshallToVm(cleanJson(err), vm));
|
|
@@ -9,6 +9,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
|
|
9
9
|
const body = marshallToVm(req.getBody(), vm);
|
|
10
10
|
const timeout = marshallToVm(req.getTimeout(), vm);
|
|
11
11
|
const name = marshallToVm(req.getName(), vm);
|
|
12
|
+
const pathParams = marshallToVm(req.getPathParams(), vm);
|
|
12
13
|
const tags = marshallToVm(req.getTags(), vm);
|
|
13
14
|
|
|
14
15
|
vm.setProp(reqObject, 'url', url);
|
|
@@ -17,6 +18,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
|
|
17
18
|
vm.setProp(reqObject, 'body', body);
|
|
18
19
|
vm.setProp(reqObject, 'timeout', timeout);
|
|
19
20
|
vm.setProp(reqObject, 'name', name);
|
|
21
|
+
vm.setProp(reqObject, 'pathParams', pathParams);
|
|
20
22
|
vm.setProp(reqObject, 'tags', tags);
|
|
21
23
|
|
|
22
24
|
url.dispose();
|
|
@@ -25,6 +27,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
|
|
25
27
|
body.dispose();
|
|
26
28
|
timeout.dispose();
|
|
27
29
|
name.dispose();
|
|
30
|
+
pathParams.dispose();
|
|
28
31
|
tags.dispose();
|
|
29
32
|
|
|
30
33
|
let getUrl = vm.newFunction('getUrl', function () {
|
|
@@ -39,6 +42,24 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
|
|
39
42
|
vm.setProp(reqObject, 'setUrl', setUrl);
|
|
40
43
|
setUrl.dispose();
|
|
41
44
|
|
|
45
|
+
let getHost = vm.newFunction('getHost', function () {
|
|
46
|
+
return marshallToVm(req.getHost(), vm);
|
|
47
|
+
});
|
|
48
|
+
vm.setProp(reqObject, 'getHost', getHost);
|
|
49
|
+
getHost.dispose();
|
|
50
|
+
|
|
51
|
+
let getPath = vm.newFunction('getPath', function () {
|
|
52
|
+
return marshallToVm(req.getPath(), vm);
|
|
53
|
+
});
|
|
54
|
+
vm.setProp(reqObject, 'getPath', getPath);
|
|
55
|
+
getPath.dispose();
|
|
56
|
+
|
|
57
|
+
let getQueryString = vm.newFunction('getQueryString', function () {
|
|
58
|
+
return marshallToVm(req.getQueryString(), vm);
|
|
59
|
+
});
|
|
60
|
+
vm.setProp(reqObject, 'getQueryString', getQueryString);
|
|
61
|
+
getQueryString.dispose();
|
|
62
|
+
|
|
42
63
|
let getMethod = vm.newFunction('getMethod', function () {
|
|
43
64
|
return marshallToVm(req.getMethod(), vm);
|
|
44
65
|
});
|
|
@@ -57,6 +78,12 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
|
|
57
78
|
vm.setProp(reqObject, 'getName', getName);
|
|
58
79
|
getName.dispose();
|
|
59
80
|
|
|
81
|
+
let getPathParams = vm.newFunction('getPathParams', function () {
|
|
82
|
+
return marshallToVm(req.getPathParams(), vm);
|
|
83
|
+
});
|
|
84
|
+
vm.setProp(reqObject, 'getPathParams', getPathParams);
|
|
85
|
+
getPathParams.dispose();
|
|
86
|
+
|
|
60
87
|
let setMethod = vm.newFunction('setMethod', function (method) {
|
|
61
88
|
req.setMethod(vm.dump(method));
|
|
62
89
|
});
|
|
@@ -10,7 +10,7 @@ const addCryptoUtilsShimToContext = async (vm) => {
|
|
|
10
10
|
let randomBytesHandle = vm.newFunction('randomBytes', function (sizeHandle) {
|
|
11
11
|
try {
|
|
12
12
|
let size = vm.dump(sizeHandle);
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
if (typeof size !== 'number') {
|
|
15
15
|
throw new TypeError('The "size" argument must be of type number');
|
|
16
16
|
}
|
|
@@ -30,15 +30,14 @@ const addCryptoUtilsShimToContext = async (vm) => {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const buffer = crypto.randomBytes(size);
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
const byteArray = Array.from(buffer);
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
return marshallToVm(byteArray, vm);
|
|
37
|
-
|
|
38
37
|
} catch (error) {
|
|
39
38
|
const vmError = vm.newError(error.message);
|
|
40
39
|
vm.setProp(vmError, 'name', vm.newString(error.name));
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
throw vmError;
|
|
43
42
|
}
|
|
44
43
|
});
|
|
@@ -48,7 +47,7 @@ const addCryptoUtilsShimToContext = async (vm) => {
|
|
|
48
47
|
// Receive the serialized array data directly
|
|
49
48
|
const serializedArray = vm.dump(arrayHandle);
|
|
50
49
|
const typedArray = deserializeTypedArray(serializedArray);
|
|
51
|
-
|
|
50
|
+
|
|
52
51
|
if (typedArray.length === 0) {
|
|
53
52
|
return marshallToVm([], vm);
|
|
54
53
|
}
|
|
@@ -62,11 +61,10 @@ const addCryptoUtilsShimToContext = async (vm) => {
|
|
|
62
61
|
const byteArray = Array.from(typedArray);
|
|
63
62
|
|
|
64
63
|
return marshallToVm(byteArray, vm);
|
|
65
|
-
|
|
66
64
|
} catch (error) {
|
|
67
65
|
const vmError = vm.newError(error.message);
|
|
68
66
|
vm.setProp(vmError, 'name', vm.newString(error.name));
|
|
69
|
-
|
|
67
|
+
|
|
70
68
|
throw vmError;
|
|
71
69
|
}
|
|
72
70
|
});
|
|
@@ -101,4 +99,4 @@ const addCryptoUtilsShimToContext = async (vm) => {
|
|
|
101
99
|
`);
|
|
102
100
|
};
|
|
103
101
|
|
|
104
|
-
module.exports = addCryptoUtilsShimToContext;
|
|
102
|
+
module.exports = addCryptoUtilsShimToContext;
|
|
@@ -27,7 +27,7 @@ describe('crypto-utils shims tests', () => {
|
|
|
27
27
|
const handle = vm.unwrapResult(result);
|
|
28
28
|
const type = vm.dump(handle);
|
|
29
29
|
handle.dispose();
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
expect(type).toBe('function');
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -36,7 +36,7 @@ describe('crypto-utils shims tests', () => {
|
|
|
36
36
|
const handle = vm.unwrapResult(result);
|
|
37
37
|
const type = vm.dump(handle);
|
|
38
38
|
handle.dispose();
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
expect(type).toBe('function');
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ describe('crypto-utils shims tests', () => {
|
|
|
45
45
|
const handle = vm.unwrapResult(result);
|
|
46
46
|
const length = vm.dump(handle);
|
|
47
47
|
handle.dispose();
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
expect(length).toBe(8);
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -54,7 +54,7 @@ describe('crypto-utils shims tests', () => {
|
|
|
54
54
|
const handle = vm.unwrapResult(result);
|
|
55
55
|
const hexLength = vm.dump(handle);
|
|
56
56
|
handle.dispose();
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
expect(hexLength).toBe(8); // 4 bytes = 8 hex chars
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -67,7 +67,7 @@ describe('crypto-utils shims tests', () => {
|
|
|
67
67
|
const handle = vm.unwrapResult(result);
|
|
68
68
|
const length = vm.dump(handle);
|
|
69
69
|
handle.dispose();
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
expect(length).toBe(5);
|
|
72
72
|
});
|
|
73
|
-
});
|
|
73
|
+
});
|
package/src/utils/results.js
CHANGED
|
@@ -7,7 +7,7 @@ const getResultsSummary = (results) => {
|
|
|
7
7
|
total: results.length,
|
|
8
8
|
passed: 0,
|
|
9
9
|
failed: 0,
|
|
10
|
-
skipped: 0
|
|
10
|
+
skipped: 0
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
results.forEach((r) => {
|
|
@@ -34,7 +34,7 @@ const setupBruTestMethods = (bru, __brunoTestResults, assertionResults) => {
|
|
|
34
34
|
const summary = getResultsSummary(results);
|
|
35
35
|
return {
|
|
36
36
|
summary,
|
|
37
|
-
results: results.map(r => ({
|
|
37
|
+
results: results.map((r) => ({
|
|
38
38
|
status: r.status,
|
|
39
39
|
description: r.description,
|
|
40
40
|
expected: r.expected,
|
|
@@ -49,7 +49,7 @@ const setupBruTestMethods = (bru, __brunoTestResults, assertionResults) => {
|
|
|
49
49
|
const summary = getResultsSummary(results);
|
|
50
50
|
return {
|
|
51
51
|
summary,
|
|
52
|
-
results: results.map(r => ({
|
|
52
|
+
results: results.map((r) => ({
|
|
53
53
|
status: r.status,
|
|
54
54
|
lhsExpr: r.lhsExpr,
|
|
55
55
|
rhsExpr: r.rhsExpr,
|
|
@@ -77,4 +77,4 @@ module.exports = {
|
|
|
77
77
|
getResultsSummary,
|
|
78
78
|
createBruTestResultMethods,
|
|
79
79
|
setupBruTestMethods
|
|
80
|
-
};
|
|
80
|
+
};
|
package/src/utils.js
CHANGED
|
@@ -90,7 +90,7 @@ const evaluateJsTemplateLiteral = (templateLiteral, context) => {
|
|
|
90
90
|
return templateLiteral.slice(1, -1);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
if (templateLiteral.startsWith(
|
|
93
|
+
if (templateLiteral.startsWith('\'') && templateLiteral.endsWith('\'')) {
|
|
94
94
|
return templateLiteral.slice(1, -1);
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -129,7 +129,7 @@ const createResponseParser = (response = {}) => {
|
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
|
-
* Objects that are created inside
|
|
132
|
+
* Objects that are created inside developer mode execution context result in an serialization error when sent to the renderer process
|
|
133
133
|
* Error sending from webFrameMain: Error: Failed to serialize arguments
|
|
134
134
|
* at s.send (node:electron/js2c/browser_init:169:631)
|
|
135
135
|
* at g.send (node:electron/js2c/browser_init:165:2156)
|
|
@@ -201,7 +201,7 @@ const uuid = () => {
|
|
|
201
201
|
const customNanoId = customAlphabet(urlAlphabet, 21);
|
|
202
202
|
|
|
203
203
|
return customNanoId();
|
|
204
|
-
}
|
|
204
|
+
};
|
|
205
205
|
|
|
206
206
|
const appendAwaitToTestFunc = (str) => {
|
|
207
207
|
return str.replace(/(?<!\.\s*)(?<!await\s)(test\()/g, 'await $1');
|
|
@@ -211,18 +211,18 @@ const cleanCircularJson = (data) => {
|
|
|
211
211
|
try {
|
|
212
212
|
// Handle circular references by keeping track of seen objects
|
|
213
213
|
const seen = new WeakSet();
|
|
214
|
-
|
|
214
|
+
|
|
215
215
|
const replacer = (key, value) => {
|
|
216
216
|
// Skip non-objects and null
|
|
217
217
|
if (typeof value !== 'object' || value === null) {
|
|
218
218
|
return value;
|
|
219
219
|
}
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
// Detect circular reference
|
|
222
222
|
if (seen.has(value)) {
|
|
223
223
|
return '[Circular Reference]';
|
|
224
224
|
}
|
|
225
|
-
|
|
225
|
+
|
|
226
226
|
seen.add(value);
|
|
227
227
|
return value;
|
|
228
228
|
};
|