Safe Mode using isolated-vm (#2511)

feat: safe mode using isolated vm
This commit is contained in:
lohit
2024-08-10 16:05:57 +05:30
committed by GitHub
parent 7e305be817
commit 26f8dd7a7b
43 changed files with 9681 additions and 19602 deletions

View File

@@ -28,9 +28,13 @@ const fetch = require('node-fetch');
const chai = require('chai');
const CryptoJS = require('crypto-js');
const NodeVault = require('node-vault');
const { isolatedVMAsyncInstance, executeInIsolatedVMAsync } = require('../sandbox/isolatedvm');
class ScriptRuntime {
constructor() {}
constructor(props) {
this.runtime = props?.runtime || 'vm2';
this.mode = props?.mode || 'developer';
}
// This approach is getting out of hand
// Need to refactor this to use a single arg (object) instead of 7
@@ -44,6 +48,14 @@ class ScriptRuntime {
processEnvVars,
scriptingConfig
) {
if (this.mode === 'restricted') {
return {
request,
envVariables: cleanJson(envVariables),
collectionVariables: cleanJson(collectionVariables),
nextRequestName: undefined
};
}
const requestVariables = request?.requestVariables || {};
const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, requestVariables);
const req = new BrunoRequest(request);
@@ -86,43 +98,68 @@ class ScriptRuntime {
};
}
const vm = new NodeVM({
sandbox: context,
require: {
context: 'sandbox',
external: true,
root: [collectionPath, ...additionalContextRootsAbsolute],
mock: {
// node libs
path,
stream,
util,
url,
http,
https,
punycode,
zlib,
// 3rd party libs
ajv,
'ajv-formats': addFormats,
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
axios,
chai,
'node-fetch': fetch,
'crypto-js': CryptoJS,
...whitelistedModules,
fs: allowScriptFilesystemAccess ? fs : undefined,
'node-vault': NodeVault
if (this.mode == 'safe') {
// Reuses the same instance of IsolatedVMAsync
// TODO: Test for performance
// await isolatedVMAsyncInstance.execute({
// script,
// context,
// modules: {}, // todo: module support?
// scriptType: 'jsScript'
// });
await executeInIsolatedVMAsync({
script: script,
context: context,
modules: {},
scriptType: 'jsScript'
});
} else {
const vm = new NodeVM({
sandbox: context,
require: {
context: 'sandbox',
external: true,
root: [collectionPath, ...additionalContextRootsAbsolute],
mock: {
// node libs
path,
stream,
util,
url,
http,
https,
punycode,
zlib,
// 3rd party libs
ajv,
'ajv-formats': addFormats,
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
axios,
chai,
'node-fetch': fetch,
'crypto-js': CryptoJS,
...whitelistedModules,
fs: allowScriptFilesystemAccess ? fs : undefined,
'node-vault': NodeVault
}
}
}
});
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
await asyncVM();
});
const asyncVM = vm.run(
`module.exports = async () => {
console?.debug && console.debug('vm2:pre-request:execution-start');
${script}
console?.debug && console.debug('vm2:pre-request:execution-end:');
}`,
path.join(collectionPath, 'vm.js')
);
await asyncVM();
}
return {
request,
envVariables: cleanJson(envVariables),
@@ -142,6 +179,14 @@ class ScriptRuntime {
processEnvVars,
scriptingConfig
) {
if (this.mode === 'restricted') {
return {
request,
envVariables: cleanJson(envVariables),
collectionVariables: cleanJson(collectionVariables),
nextRequestName: undefined
};
}
const requestVariables = request?.requestVariables || {};
const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, requestVariables);
const req = new BrunoRequest(request);
@@ -176,47 +221,73 @@ class ScriptRuntime {
log: customLogger('log'),
info: customLogger('info'),
warn: customLogger('warn'),
error: customLogger('error')
error: customLogger('error'),
debug: customLogger('debug')
};
}
const vm = new NodeVM({
sandbox: context,
require: {
context: 'sandbox',
external: true,
root: [collectionPath],
mock: {
// node libs
path,
stream,
util,
url,
http,
https,
punycode,
zlib,
// 3rd party libs
ajv,
'ajv-formats': addFormats,
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
axios,
'node-fetch': fetch,
'crypto-js': CryptoJS,
...whitelistedModules,
fs: allowScriptFilesystemAccess ? fs : undefined,
'node-vault': NodeVault
if (this.mode == 'safe') {
// Reuses the same instance of IsolatedVMAsync
// TODO: Test for performance
// await isolatedVMAsyncInstance.execute({
// script,
// context,
// modules: {}, // todo: module support?
// scriptType: 'jsScript'
// });
await executeInIsolatedVMAsync({
script: script,
context: context,
modules: {},
scriptType: 'jsScript'
});
} else {
// DEVELOPER MODE
const vm = new NodeVM({
sandbox: context,
require: {
context: 'sandbox',
external: true,
root: [collectionPath],
mock: {
// node libs
path,
stream,
util,
url,
http,
https,
punycode,
zlib,
// 3rd party libs
ajv,
'ajv-formats': addFormats,
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
axios,
'node-fetch': fetch,
'crypto-js': CryptoJS,
...whitelistedModules,
fs: allowScriptFilesystemAccess ? fs : undefined,
'node-vault': NodeVault
}
}
}
});
});
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
await asyncVM();
const asyncVM = vm.run(
`module.exports = async () => {
console?.debug && console.debug('vm2:post-response:execution-start:');
${script}
console?.debug && console.debug('vm2:post-response:execution-end:');
}`,
path.join(collectionPath, 'vm.js')
);
await asyncVM();
}
return {
response,