@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
package/package.json
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebruno/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"src",
|
|
8
8
|
"package.json"
|
|
9
9
|
],
|
|
10
|
-
"peerDependencies": {
|
|
11
|
-
"@usebruno/vm2": "^3.9.13"
|
|
12
|
-
},
|
|
13
10
|
"scripts": {
|
|
14
11
|
"test": "node --experimental-vm-modules $(npx which jest) --testPathIgnorePatterns test.js",
|
|
15
12
|
"sandbox:bundle-libraries": "node ./src/sandbox/bundle-libraries.js",
|
|
16
13
|
"prepack": "npm run test"
|
|
17
14
|
},
|
|
18
15
|
"dependencies": {
|
|
19
|
-
"@usebruno/common": "0.
|
|
20
|
-
"@usebruno/query": "0.
|
|
16
|
+
"@usebruno/common": "0.18.0",
|
|
17
|
+
"@usebruno/query": "0.2.0",
|
|
21
18
|
"ajv": "^8.12.0",
|
|
22
19
|
"ajv-formats": "^2.1.1",
|
|
23
20
|
"atob": "^2.1.2",
|
|
@@ -28,12 +25,11 @@
|
|
|
28
25
|
"cheerio": "^1.0.0",
|
|
29
26
|
"crypto-js": "^4.2.0",
|
|
30
27
|
"json-query": "^2.2.2",
|
|
31
|
-
"jsonwebtoken": "^9.0.
|
|
28
|
+
"jsonwebtoken": "^9.0.3",
|
|
32
29
|
"lodash": "^4.17.21",
|
|
33
30
|
"moment": "^2.29.4",
|
|
34
31
|
"nanoid": "3.3.8",
|
|
35
|
-
"node-fetch": "2.7.0",
|
|
36
|
-
"node-vault": "^0.10.2",
|
|
32
|
+
"node-fetch": "^2.7.0",
|
|
37
33
|
"path": "^0.12.7",
|
|
38
34
|
"quickjs-emscripten": "^0.29.2",
|
|
39
35
|
"tv4": "^1.3.0",
|
|
@@ -46,8 +42,5 @@
|
|
|
46
42
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
47
43
|
"rollup": "3.29.5",
|
|
48
44
|
"rollup-plugin-terser": "^7.0.2"
|
|
49
|
-
},
|
|
50
|
-
"overrides": {
|
|
51
|
-
"@postman/tunnel-agent":"0.6.4"
|
|
52
45
|
}
|
|
53
46
|
}
|
package/src/bru.js
CHANGED
|
@@ -2,13 +2,32 @@ const { cloneDeep } = require('lodash');
|
|
|
2
2
|
const { uuid } = require('./utils');
|
|
3
3
|
const xmlFormat = require('xml-formatter');
|
|
4
4
|
const { interpolate: _interpolate } = require('@usebruno/common');
|
|
5
|
-
const { sendRequest } = require('@usebruno/requests').scripting;
|
|
5
|
+
const { sendRequest, createSendRequest } = require('@usebruno/requests').scripting;
|
|
6
6
|
const { jar: createCookieJar } = require('@usebruno/requests').cookies;
|
|
7
7
|
|
|
8
8
|
const variableNameRegex = /^[\w-.]*$/;
|
|
9
9
|
|
|
10
10
|
class Bru {
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} runtime - The runtime environment ('quickjs' or 'nodevm')
|
|
13
|
+
* @param {object} envVariables - Environment variables
|
|
14
|
+
* @param {object} runtimeVariables - Runtime variables
|
|
15
|
+
* @param {object} processEnvVars - Process environment variables
|
|
16
|
+
* @param {string} collectionPath - Path to the collection
|
|
17
|
+
* @param {function} historyLogger - History logger function
|
|
18
|
+
* @param {function} setVisualizations - Visualizations setter function
|
|
19
|
+
* @param {object} secretVariables - Secret variables
|
|
20
|
+
* @param {object} collectionVariables - Collection-level variables
|
|
21
|
+
* @param {object} folderVariables - Folder-level variables
|
|
22
|
+
* @param {object} requestVariables - Request-level variables
|
|
23
|
+
* @param {object} globalEnvironmentVariables - Global environment variables
|
|
24
|
+
* @param {object} oauth2CredentialVariables - OAuth2 credential variables
|
|
25
|
+
* @param {object} iterationDetails - Iteration details for runner
|
|
26
|
+
* @param {string} collectionName - Name of the collection
|
|
27
|
+
* @param {object} promptVariables - Prompt variables
|
|
28
|
+
* @param {object} certsAndProxyConfig - Configuration for bru.sendRequest (proxy, certs, TLS)
|
|
29
|
+
*/
|
|
30
|
+
constructor(runtime, envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, oauth2CredentialVariables, iterationDetails, collectionName, promptVariables, certsAndProxyConfig) {
|
|
12
31
|
this.envVariables = envVariables || {};
|
|
13
32
|
this.runtimeVariables = runtimeVariables || {};
|
|
14
33
|
this.promptVariables = promptVariables || {};
|
|
@@ -23,11 +42,13 @@ class Bru {
|
|
|
23
42
|
this.historyLogger = historyLogger;
|
|
24
43
|
this.setVisualizations = setVisualizations;
|
|
25
44
|
this.collectionName = collectionName;
|
|
26
|
-
|
|
45
|
+
// Use createSendRequest with config if provided, otherwise use default sendRequest
|
|
46
|
+
this.sendRequest = certsAndProxyConfig ? createSendRequest(certsAndProxyConfig) : sendRequest;
|
|
47
|
+
this.runtime = runtime;
|
|
27
48
|
this.cookies = {
|
|
28
49
|
jar: () => {
|
|
29
50
|
const cookieJar = createCookieJar();
|
|
30
|
-
|
|
51
|
+
|
|
31
52
|
return {
|
|
32
53
|
getCookie: (url, cookieName, callback) => {
|
|
33
54
|
const interpolatedUrl = this.interpolate(url);
|
|
@@ -82,7 +103,7 @@ class Bru {
|
|
|
82
103
|
iterationData: new IterationDataManager(iterationDetails?.iterationData),
|
|
83
104
|
iterationIndex: iterationDetails?.iterationIndex,
|
|
84
105
|
totalIterations: iterationDetails?.totalIterations
|
|
85
|
-
};
|
|
106
|
+
};
|
|
86
107
|
|
|
87
108
|
this.utils = {
|
|
88
109
|
minifyJson: (json) => {
|
|
@@ -128,7 +149,6 @@ class Bru {
|
|
|
128
149
|
}
|
|
129
150
|
};
|
|
130
151
|
}
|
|
131
|
-
|
|
132
152
|
|
|
133
153
|
interpolate = (strOrObj) => {
|
|
134
154
|
if (!strOrObj) return strOrObj;
|
|
@@ -179,7 +199,7 @@ class Bru {
|
|
|
179
199
|
if (!key) {
|
|
180
200
|
throw new Error('Creating a env variable without specifying a name is not allowed.');
|
|
181
201
|
}
|
|
182
|
-
|
|
202
|
+
|
|
183
203
|
if (variableNameRegex.test(key) === false) {
|
|
184
204
|
throw new Error(
|
|
185
205
|
`Variable name: "${key}" contains invalid characters! Names must only contain alpha-numeric characters, "-", "_", "."`
|
|
@@ -242,8 +262,8 @@ class Bru {
|
|
|
242
262
|
|
|
243
263
|
if (variableNameRegex.test(key) === false) {
|
|
244
264
|
throw new Error(
|
|
245
|
-
`Variable name: "${key}" contains invalid characters!`
|
|
246
|
-
|
|
265
|
+
`Variable name: "${key}" contains invalid characters!`
|
|
266
|
+
+ ' Names must only contain alpha-numeric characters, "-", "_", "."'
|
|
247
267
|
);
|
|
248
268
|
}
|
|
249
269
|
|
|
@@ -251,19 +271,19 @@ class Bru {
|
|
|
251
271
|
this.historyLogger({
|
|
252
272
|
uid: uuid(),
|
|
253
273
|
type: 'setVar()',
|
|
254
|
-
data: { key, value:
|
|
274
|
+
data: { key, value: value },
|
|
255
275
|
createdAt: new Date().toISOString()
|
|
256
276
|
});
|
|
257
277
|
}
|
|
258
278
|
|
|
259
|
-
this.runtimeVariables[key] =
|
|
279
|
+
this.runtimeVariables[key] = value;
|
|
260
280
|
}
|
|
261
281
|
|
|
262
282
|
getVar(key) {
|
|
263
283
|
if (variableNameRegex.test(key) === false) {
|
|
264
284
|
throw new Error(
|
|
265
|
-
`Variable name: "${key}" contains invalid characters!`
|
|
266
|
-
|
|
285
|
+
`Variable name: "${key}" contains invalid characters!`
|
|
286
|
+
+ ' Names must only contain alpha-numeric characters, "-", "_", "."'
|
|
267
287
|
);
|
|
268
288
|
}
|
|
269
289
|
|
|
@@ -298,12 +318,10 @@ class Bru {
|
|
|
298
318
|
this.nextRequest = nextRequest;
|
|
299
319
|
}
|
|
300
320
|
|
|
301
|
-
|
|
302
321
|
getSecretVar(key) {
|
|
303
322
|
return this.secretVariables?.[`$secrets.${key}`];
|
|
304
323
|
}
|
|
305
324
|
|
|
306
|
-
|
|
307
325
|
visualize(type, data) {
|
|
308
326
|
if (type == 'table') {
|
|
309
327
|
if (data?.provider == 'ag-grid') {
|
|
@@ -332,6 +350,10 @@ class Bru {
|
|
|
332
350
|
getCollectionName() {
|
|
333
351
|
return this.collectionName;
|
|
334
352
|
}
|
|
353
|
+
|
|
354
|
+
isSafeMode() {
|
|
355
|
+
return this.runtime === 'quickjs';
|
|
356
|
+
}
|
|
335
357
|
}
|
|
336
358
|
|
|
337
359
|
class IterationDataManager {
|
package/src/bruno-request.js
CHANGED
|
@@ -8,7 +8,7 @@ class BrunoRequest {
|
|
|
8
8
|
* - req.headers
|
|
9
9
|
* - req.timeout
|
|
10
10
|
* - req.body
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* Above shorthands are useful for accessing the request properties directly in the scripts
|
|
13
13
|
* It must be noted that the user cannot set these properties directly.
|
|
14
14
|
* They should use the respective setter methods to set these properties.
|
|
@@ -22,13 +22,14 @@ class BrunoRequest {
|
|
|
22
22
|
this.timeout = req.timeout;
|
|
23
23
|
this.historyLogger = historyLogger;
|
|
24
24
|
this.name = req.name;
|
|
25
|
+
this.pathParams = req.pathParams;
|
|
25
26
|
this.tags = req.tags || [];
|
|
26
27
|
/**
|
|
27
28
|
* We automatically parse the JSON body if the content type is JSON
|
|
28
29
|
* This is to make it easier for the user to access the body directly
|
|
29
|
-
*
|
|
30
|
+
*
|
|
30
31
|
* It must be noted that the request data is always a string and is what gets sent over the network
|
|
31
|
-
* If the user wants to access the raw data, they can use getBody({raw: true}) method
|
|
32
|
+
* If the user wants to access the raw data, they can use getBody({raw: true}) method
|
|
32
33
|
*/
|
|
33
34
|
const isJson = this.hasJSONContentType(this.req.headers);
|
|
34
35
|
if (isJson) {
|
|
@@ -45,9 +46,57 @@ class BrunoRequest {
|
|
|
45
46
|
this.req.url = url;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
getHost() {
|
|
50
|
+
try {
|
|
51
|
+
const url = new URL(this.req.url);
|
|
52
|
+
return url.host;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getPath() {
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(this.req.url);
|
|
61
|
+
let pathname = url.pathname;
|
|
62
|
+
|
|
63
|
+
// If path params exist, interpolate them into the pathname
|
|
64
|
+
if (this.req.pathParams && Array.isArray(this.req.pathParams)) {
|
|
65
|
+
pathname = pathname
|
|
66
|
+
.split('/')
|
|
67
|
+
.map((segment) => {
|
|
68
|
+
if (segment.startsWith(':')) {
|
|
69
|
+
const paramName = segment.slice(1);
|
|
70
|
+
const pathParam = this.req.pathParams.find((param) => param.name === paramName);
|
|
71
|
+
if (pathParam && pathParam.value) {
|
|
72
|
+
return pathParam.value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return segment;
|
|
76
|
+
})
|
|
77
|
+
.join('/');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return pathname;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getQueryString() {
|
|
87
|
+
try {
|
|
88
|
+
const url = new URL(this.req.url);
|
|
89
|
+
// Return query string without the leading '?'
|
|
90
|
+
return url.search ? url.search.substring(1) : '';
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
48
96
|
getMethod() {
|
|
49
97
|
return this.req.method;
|
|
50
98
|
}
|
|
99
|
+
|
|
51
100
|
getAuthMode() {
|
|
52
101
|
if (this.req?.oauth2) {
|
|
53
102
|
return 'oauth2';
|
|
@@ -104,7 +153,7 @@ class BrunoRequest {
|
|
|
104
153
|
|
|
105
154
|
/**
|
|
106
155
|
* Get the body of the request
|
|
107
|
-
*
|
|
156
|
+
*
|
|
108
157
|
* We automatically parse and return the JSON body if the content type is JSON
|
|
109
158
|
* If the user wants the raw body, they can pass the raw option as true
|
|
110
159
|
*/
|
|
@@ -128,7 +177,7 @@ class BrunoRequest {
|
|
|
128
177
|
* Otherwise
|
|
129
178
|
* - We set the request data as the data itself
|
|
130
179
|
* - We set the body property as the data itself
|
|
131
|
-
*
|
|
180
|
+
*
|
|
132
181
|
* If the user wants to override this behavior, they can pass the raw option as true
|
|
133
182
|
*/
|
|
134
183
|
setBody(data, options = {}) {
|
|
@@ -140,7 +189,7 @@ class BrunoRequest {
|
|
|
140
189
|
createdAt: new Date().toISOString()
|
|
141
190
|
});
|
|
142
191
|
}
|
|
143
|
-
|
|
192
|
+
|
|
144
193
|
if (options.raw) {
|
|
145
194
|
this.req.data = data;
|
|
146
195
|
this.body = data;
|
|
@@ -170,7 +219,7 @@ class BrunoRequest {
|
|
|
170
219
|
this.timeout = timeout;
|
|
171
220
|
this.req.timeout = timeout;
|
|
172
221
|
}
|
|
173
|
-
|
|
222
|
+
|
|
174
223
|
onFail(callback) {
|
|
175
224
|
if (typeof callback === 'function') {
|
|
176
225
|
this.req.onFailHandler = callback;
|
|
@@ -198,7 +247,6 @@ class BrunoRequest {
|
|
|
198
247
|
__isObject(obj) {
|
|
199
248
|
return obj !== null && typeof obj === 'object';
|
|
200
249
|
}
|
|
201
|
-
|
|
202
250
|
|
|
203
251
|
disableParsingResponseJson() {
|
|
204
252
|
this.req.__brunoDisableParsingResponseJson = true;
|
|
@@ -212,6 +260,16 @@ class BrunoRequest {
|
|
|
212
260
|
return this.req.name;
|
|
213
261
|
}
|
|
214
262
|
|
|
263
|
+
getPathParams() {
|
|
264
|
+
const params = Array.isArray(this.req.pathParams) ? this.req.pathParams : [];
|
|
265
|
+
|
|
266
|
+
return params.map((param) => ({
|
|
267
|
+
name: param.name,
|
|
268
|
+
value: param.value,
|
|
269
|
+
type: param.type
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
|
|
215
273
|
/**
|
|
216
274
|
* Get the tags associated with this request
|
|
217
275
|
* @returns {Array<string>} Array of tag strings
|
package/src/bruno-response.js
CHANGED
|
@@ -55,6 +55,20 @@ class BrunoResponse {
|
|
|
55
55
|
const clonedData = _.cloneDeep(data);
|
|
56
56
|
this.res.data = clonedData;
|
|
57
57
|
this.body = clonedData;
|
|
58
|
+
|
|
59
|
+
// Update dataBuffer to match the modified body
|
|
60
|
+
if (clonedData === null || clonedData === undefined) {
|
|
61
|
+
this.res.dataBuffer = Buffer.from('');
|
|
62
|
+
} else if (typeof clonedData === 'string') {
|
|
63
|
+
this.res.dataBuffer = Buffer.from(clonedData);
|
|
64
|
+
} else {
|
|
65
|
+
// For objects, stringify them
|
|
66
|
+
try {
|
|
67
|
+
this.res.dataBuffer = Buffer.from(JSON.stringify(clonedData));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
this.res.dataBuffer = Buffer.from('');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
// TODO: Refactor: dataBuffer size calculation should be handled in a shared utility so it can be passed and reused across the application
|
|
@@ -65,7 +79,7 @@ class BrunoResponse {
|
|
|
65
79
|
|
|
66
80
|
const { data, dataBuffer, headers } = this.res;
|
|
67
81
|
let bodySize = 0;
|
|
68
|
-
|
|
82
|
+
|
|
69
83
|
// Use raw received bytes
|
|
70
84
|
if (Buffer.isBuffer(dataBuffer)) {
|
|
71
85
|
bodySize = dataBuffer.length;
|
|
@@ -94,7 +108,6 @@ class BrunoResponse {
|
|
|
94
108
|
const headerSize = Buffer.byteLength(headerLines.join('\r\n'));
|
|
95
109
|
|
|
96
110
|
return { header: headerSize, body: bodySize, total: headerSize + bodySize };
|
|
97
|
-
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
getDataBuffer() {
|
|
@@ -241,7 +241,7 @@ const evaluateRhsOperand = (rhsOperand, operator, context, runtime) => {
|
|
|
241
241
|
|
|
242
242
|
class AssertRuntime {
|
|
243
243
|
constructor(props) {
|
|
244
|
-
this.runtime = props?.runtime || '
|
|
244
|
+
this.runtime = props?.runtime || 'quickjs';
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
runAssertions(assertions, request, response, envVariables, runtimeVariables, processEnvVars, historyLogger, secretVariables) {
|
|
@@ -257,7 +257,9 @@ class AssertRuntime {
|
|
|
257
257
|
return [];
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
const certsAndProxyConfig = request?.certsAndProxyConfig;
|
|
260
261
|
const bru = new Bru(
|
|
262
|
+
this.runtime,
|
|
261
263
|
envVariables,
|
|
262
264
|
runtimeVariables,
|
|
263
265
|
processEnvVars,
|
|
@@ -272,7 +274,8 @@ class AssertRuntime {
|
|
|
272
274
|
oauth2CredentialVariables,
|
|
273
275
|
iterationDetails,
|
|
274
276
|
undefined,
|
|
275
|
-
promptVariables
|
|
277
|
+
promptVariables,
|
|
278
|
+
certsAndProxyConfig
|
|
276
279
|
);
|
|
277
280
|
const req = new BrunoRequest(request, historyLogger);
|
|
278
281
|
const res = createResponseParser(response);
|
|
@@ -1,45 +1,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const http = require('http');
|
|
4
|
-
const https = require('https');
|
|
5
|
-
const stream = require('stream');
|
|
6
|
-
const util = require('util');
|
|
7
|
-
const zlib = require('zlib');
|
|
8
|
-
const url = require('url');
|
|
9
|
-
const punycode = require('punycode');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const { get } = require('lodash');
|
|
1
|
+
const chai = require('chai');
|
|
12
2
|
const Bru = require('../bru');
|
|
13
3
|
const BrunoRequest = require('../bruno-request');
|
|
14
4
|
const BrunoResponse = require('../bruno-response');
|
|
15
5
|
const { cleanJson } = require('../utils');
|
|
16
6
|
const { createBruTestResultMethods } = require('../utils/results');
|
|
17
7
|
const { runScriptInNodeVm } = require('../sandbox/node-vm');
|
|
18
|
-
|
|
19
|
-
// Inbuilt Library Support
|
|
20
|
-
const ajv = require('ajv');
|
|
21
|
-
const addFormats = require('ajv-formats');
|
|
22
|
-
const atob = require('atob');
|
|
23
|
-
const btoa = require('btoa');
|
|
24
|
-
const lodash = require('lodash');
|
|
25
|
-
const moment = require('moment');
|
|
26
|
-
const uuid = require('uuid');
|
|
27
|
-
const nanoid = require('nanoid');
|
|
28
|
-
const axios = require('axios');
|
|
29
|
-
const fetch = require('node-fetch');
|
|
30
|
-
const chai = require('chai');
|
|
31
|
-
const CryptoJS = require('crypto-js');
|
|
32
|
-
const NodeVault = require('node-vault');
|
|
33
|
-
const xml2js = require('xml2js');
|
|
34
|
-
const cheerio = require('cheerio');
|
|
35
|
-
const tv4 = require('tv4');
|
|
36
|
-
const jsonwebtoken = require('jsonwebtoken');
|
|
37
8
|
const { executeQuickJsVmAsync } = require('../sandbox/quickjs');
|
|
38
|
-
const { mixinTypedArrays } = require('../sandbox/mixins/typed-arrays');
|
|
39
9
|
|
|
40
10
|
class ScriptRuntime {
|
|
41
11
|
constructor(props) {
|
|
42
|
-
this.runtime = props?.runtime || '
|
|
12
|
+
this.runtime = props?.runtime || 'quickjs';
|
|
43
13
|
}
|
|
44
14
|
|
|
45
15
|
// This approach is getting out of hand
|
|
@@ -61,7 +31,7 @@ class ScriptRuntime {
|
|
|
61
31
|
let visualizations = [];
|
|
62
32
|
let setVisualizations = (data) => {
|
|
63
33
|
visualizations.push(data);
|
|
64
|
-
}
|
|
34
|
+
};
|
|
65
35
|
const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
|
|
66
36
|
const oauth2CredentialVariables = request?.oauth2CredentialVariables || {};
|
|
67
37
|
const collectionVariables = request?.collectionVariables || {};
|
|
@@ -70,27 +40,9 @@ class ScriptRuntime {
|
|
|
70
40
|
const promptVariables = request?.promptVariables || {};
|
|
71
41
|
const iterationDetails = request?.runnerIterationDetails || {};
|
|
72
42
|
const assertionResults = request?.assertionResults || [];
|
|
73
|
-
|
|
74
|
-
const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, oauth2CredentialVariables, iterationDetails, collectionName, promptVariables);
|
|
43
|
+
const certsAndProxyConfig = request?.certsAndProxyConfig;
|
|
44
|
+
const bru = new Bru(this.runtime, envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, oauth2CredentialVariables, iterationDetails, collectionName, promptVariables, certsAndProxyConfig);
|
|
75
45
|
const req = new BrunoRequest(request);
|
|
76
|
-
const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
|
|
77
|
-
const moduleWhitelist = get(scriptingConfig, 'moduleWhitelist', []);
|
|
78
|
-
const additionalContextRoots = get(scriptingConfig, 'additionalContextRoots', []);
|
|
79
|
-
const additionalContextRootsAbsolute = lodash
|
|
80
|
-
.chain(additionalContextRoots)
|
|
81
|
-
.map((acr) => (acr.startsWith('/') ? acr : path.join(collectionPath, acr)))
|
|
82
|
-
.value();
|
|
83
|
-
|
|
84
|
-
const whitelistedModules = {};
|
|
85
|
-
|
|
86
|
-
for (let module of moduleWhitelist) {
|
|
87
|
-
try {
|
|
88
|
-
whitelistedModules[module] = require(module);
|
|
89
|
-
} catch (e) {
|
|
90
|
-
// Ignore
|
|
91
|
-
console.warn(e);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
46
|
|
|
95
47
|
// extend bru with result getter methods
|
|
96
48
|
const { __brunoTestResults, test } = createBruTestResultMethods(bru, assertionResults, chai);
|
|
@@ -104,10 +56,6 @@ class ScriptRuntime {
|
|
|
104
56
|
__brunoTestResults: __brunoTestResults
|
|
105
57
|
};
|
|
106
58
|
|
|
107
|
-
if (this.runtime === 'vm2') {
|
|
108
|
-
mixinTypedArrays(context);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
59
|
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
|
112
60
|
const customLogger = (type) => {
|
|
113
61
|
return (...args) => {
|
|
@@ -149,70 +97,12 @@ class ScriptRuntime {
|
|
|
149
97
|
};
|
|
150
98
|
}
|
|
151
99
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
request,
|
|
161
|
-
envVariables: cleanJson(envVariables),
|
|
162
|
-
runtimeVariables: cleanJson(runtimeVariables),
|
|
163
|
-
visualizations,
|
|
164
|
-
persistentEnvVariables: bru.persistentEnvVariables,
|
|
165
|
-
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
166
|
-
results: cleanJson(__brunoTestResults.getResults()),
|
|
167
|
-
nextRequestName: bru.nextRequest,
|
|
168
|
-
skipRequest: bru.skipRequest,
|
|
169
|
-
stopExecution: bru.stopExecution
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// default runtime is vm2
|
|
174
|
-
const vm = new NodeVM({
|
|
175
|
-
sandbox: context,
|
|
176
|
-
require: {
|
|
177
|
-
context: 'sandbox',
|
|
178
|
-
builtin: [ "*" ],
|
|
179
|
-
external: true,
|
|
180
|
-
root: [collectionPath, ...additionalContextRootsAbsolute],
|
|
181
|
-
mock: {
|
|
182
|
-
// node libs
|
|
183
|
-
path,
|
|
184
|
-
stream,
|
|
185
|
-
util,
|
|
186
|
-
url,
|
|
187
|
-
http,
|
|
188
|
-
https,
|
|
189
|
-
punycode,
|
|
190
|
-
zlib,
|
|
191
|
-
// 3rd party libs
|
|
192
|
-
ajv,
|
|
193
|
-
'ajv-formats': addFormats,
|
|
194
|
-
atob,
|
|
195
|
-
btoa,
|
|
196
|
-
lodash,
|
|
197
|
-
moment,
|
|
198
|
-
uuid,
|
|
199
|
-
nanoid,
|
|
200
|
-
axios,
|
|
201
|
-
chai,
|
|
202
|
-
'node-fetch': fetch,
|
|
203
|
-
'crypto-js': CryptoJS,
|
|
204
|
-
xml2js: xml2js,
|
|
205
|
-
jsonwebtoken,
|
|
206
|
-
cheerio,
|
|
207
|
-
tv4,
|
|
208
|
-
...whitelistedModules,
|
|
209
|
-
fs: allowScriptFilesystemAccess ? fs : undefined,
|
|
210
|
-
'node-vault': NodeVault
|
|
211
|
-
}
|
|
212
|
-
}
|
|
100
|
+
// default runtime is `quickjs`
|
|
101
|
+
await executeQuickJsVmAsync({
|
|
102
|
+
script: script,
|
|
103
|
+
context: context,
|
|
104
|
+
collectionPath
|
|
213
105
|
});
|
|
214
|
-
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
|
|
215
|
-
await asyncVM();
|
|
216
106
|
|
|
217
107
|
return {
|
|
218
108
|
request,
|
|
@@ -246,7 +136,7 @@ class ScriptRuntime {
|
|
|
246
136
|
let visualizations = [];
|
|
247
137
|
let setVisualizations = (data) => {
|
|
248
138
|
visualizations.push(data);
|
|
249
|
-
}
|
|
139
|
+
};
|
|
250
140
|
const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
|
|
251
141
|
const oauth2CredentialVariables = request?.oauth2CredentialVariables || {};
|
|
252
142
|
const collectionVariables = request?.collectionVariables || {};
|
|
@@ -255,27 +145,10 @@ class ScriptRuntime {
|
|
|
255
145
|
const promptVariables = request?.promptVariables || {};
|
|
256
146
|
const iterationDetails = request?.runnerIterationDetails || {};
|
|
257
147
|
const assertionResults = request?.assertionResults || [];
|
|
258
|
-
const
|
|
148
|
+
const certsAndProxyConfig = request?.certsAndProxyConfig;
|
|
149
|
+
const bru = new Bru(this.runtime, envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables, oauth2CredentialVariables, iterationDetails, collectionName, promptVariables, certsAndProxyConfig);
|
|
259
150
|
const req = new BrunoRequest(request);
|
|
260
151
|
const res = new BrunoResponse(response);
|
|
261
|
-
const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
|
|
262
|
-
const moduleWhitelist = get(scriptingConfig, 'moduleWhitelist', []);
|
|
263
|
-
const additionalContextRoots = get(scriptingConfig, 'additionalContextRoots', []);
|
|
264
|
-
const additionalContextRootsAbsolute = lodash
|
|
265
|
-
.chain(additionalContextRoots)
|
|
266
|
-
.map((acr) => (acr.startsWith('/') ? acr : path.join(collectionPath, acr)))
|
|
267
|
-
.value();
|
|
268
|
-
|
|
269
|
-
const whitelistedModules = {};
|
|
270
|
-
|
|
271
|
-
for (let module of moduleWhitelist) {
|
|
272
|
-
try {
|
|
273
|
-
whitelistedModules[module] = require(module);
|
|
274
|
-
} catch (e) {
|
|
275
|
-
// Ignore
|
|
276
|
-
console.warn(e);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
152
|
|
|
280
153
|
// extend bru with result getter methods
|
|
281
154
|
const { __brunoTestResults, test } = createBruTestResultMethods(bru, assertionResults, chai);
|
|
@@ -290,10 +163,6 @@ class ScriptRuntime {
|
|
|
290
163
|
__brunoTestResults: __brunoTestResults
|
|
291
164
|
};
|
|
292
165
|
|
|
293
|
-
if (this.runtime === 'vm2') {
|
|
294
|
-
mixinTypedArrays(context);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
166
|
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
|
298
167
|
const customLogger = (type) => {
|
|
299
168
|
return (...args) => {
|
|
@@ -335,71 +204,13 @@ class ScriptRuntime {
|
|
|
335
204
|
};
|
|
336
205
|
}
|
|
337
206
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
response,
|
|
347
|
-
envVariables: cleanJson(envVariables),
|
|
348
|
-
persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
|
|
349
|
-
runtimeVariables: cleanJson(runtimeVariables),
|
|
350
|
-
visualizations,
|
|
351
|
-
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
|
|
352
|
-
results: cleanJson(__brunoTestResults.getResults()),
|
|
353
|
-
nextRequestName: bru.nextRequest,
|
|
354
|
-
skipRequest: bru.skipRequest,
|
|
355
|
-
stopExecution: bru.stopExecution
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// default runtime is vm2
|
|
360
|
-
const vm = new NodeVM({
|
|
361
|
-
sandbox: context,
|
|
362
|
-
require: {
|
|
363
|
-
context: 'sandbox',
|
|
364
|
-
builtin: [ "*" ],
|
|
365
|
-
external: true,
|
|
366
|
-
root: [collectionPath, ...additionalContextRootsAbsolute],
|
|
367
|
-
mock: {
|
|
368
|
-
// node libs
|
|
369
|
-
path,
|
|
370
|
-
stream,
|
|
371
|
-
util,
|
|
372
|
-
url,
|
|
373
|
-
http,
|
|
374
|
-
https,
|
|
375
|
-
punycode,
|
|
376
|
-
zlib,
|
|
377
|
-
// 3rd party libs
|
|
378
|
-
ajv,
|
|
379
|
-
'ajv-formats': addFormats,
|
|
380
|
-
atob,
|
|
381
|
-
btoa,
|
|
382
|
-
lodash,
|
|
383
|
-
moment,
|
|
384
|
-
uuid,
|
|
385
|
-
nanoid,
|
|
386
|
-
axios,
|
|
387
|
-
'node-fetch': fetch,
|
|
388
|
-
'crypto-js': CryptoJS,
|
|
389
|
-
'xml2js': xml2js,
|
|
390
|
-
jsonwebtoken,
|
|
391
|
-
cheerio,
|
|
392
|
-
tv4,
|
|
393
|
-
...whitelistedModules,
|
|
394
|
-
fs: allowScriptFilesystemAccess ? fs : undefined,
|
|
395
|
-
'node-vault': NodeVault
|
|
396
|
-
}
|
|
397
|
-
}
|
|
207
|
+
// default runtime is `quickjs`
|
|
208
|
+
await executeQuickJsVmAsync({
|
|
209
|
+
script: script,
|
|
210
|
+
context: context,
|
|
211
|
+
collectionPath
|
|
398
212
|
});
|
|
399
213
|
|
|
400
|
-
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
|
|
401
|
-
await asyncVM();
|
|
402
|
-
|
|
403
214
|
return {
|
|
404
215
|
response,
|
|
405
216
|
envVariables: cleanJson(envVariables),
|