Isolation
Why should i use it ? How often do you see libraries which mutates global variables Or how often
do you check libraries actins ? Library provides script isolation in custom contexts to solve this
issues. Also, isolation prevents global scope and prototypes pollution.
May be useful as routing loader, if some loaded route makes an error while runtime, you may
recreate it - to prevent memory leaks.
Installation
Warning ! Required scripts must be commonjs syntax
npm i isolation --save
Basic Usage
-
Prevent intentionally damage
It will stop tricky users code, while you don't allow it.
const Isolation = require('isolation');
const routes = Isolation.read('./routes', { access: { sandbox: module => module !== 'fs' } });
const dangerousLibrary = require('unchecked-dangerous-library');
const fs = require('fs');
fs.rm(process.cwd(), { recursive: true });
-
Prevent unintentionally damage
This solves problem where libraries used to mutate at global variables.
const Isolation = require('isolation');
Isolation.read('./routes');
console.log('All works fine');
console('Here it not works');
const dangerousLibrary = require('unchecked-dangerous-library');
global.console = msg => process.stdout.write(msg);
console('Here it works fine');
Details
Access controll
You may control access to some modules or paths of your application
const option = { access: pathOrModule => pathOrModule === 'fs' || pathOrModule.endsWith('.js') };
Isolation.execute('module.exports = require("fs")');
Isolation.read('./path/to/script.js');
const option2 = {
access: {
internal: path => true,
sandbox: module => {},
},
};
If access doesn't provided sandbox submodules would'nt be accessible and reader will read all
files in directed repository
Common js
Isolation supports only commonjs syntax from v1.1.0
That's because currently node.vm
doesn't support ecmascript syntax
const Isolation = require('isolation');
console.log(new Isolation(`module.exports = { field: 'value' };`).execute());
console.log(Isolation.execute(`module.exports = (a, b) => a + b;`)(2 + 2));
Isolation.execute(`module.exports = async (a, b) => a + b;`)(2 + 2).then(console.log);
Context API
You can create custom context or use default presets with context api.
import { Context, CreateContextOptions } from 'node:vm';
{
OPTIONS: CreateContextOptions,
EMPTY: Context,
COMMON: Context,
NODE: Context,
(ctx: object, preventEscape: boolean): Context
}
const { sandbox, execute } = require('isolation');
const custom = sandbox({ console });
execute(`console.log(123);`, { ctx: custom });
execute(`console.log(123);`);
This will allow you to provide your custom variables to the context without requiring any module and
with link safety. Also it can allow you to change program behavior with somthing like:
const ctx = Isolation.sandbox({ a: 1000, b: 10 });
const prepared = Isolation.prepare(`module.exports = a - b`, { ctx });
prepared.execute();
prepared.execute({ ...ctx, a: 0 });
prepared.execute({ ...ctx, b: 7 });
Reader API
Reader allow you to run scripts from files
-
read
Allow you to read files or directories
You should use specific methods to have better performance. Option prepare
allow you
to run script later
const { require: read } = require('isolation');
read('./path/to/script.js').then(console.log);
read('./path/to').then(console.log);
read('./path/to', { prepare: true }).then(console.log);
By default reader works with nested directories, to disable this behavior you can do:
const { require: read } = require('isolation');
read('./path/to', {}, false);
-
read.script
Allow you to read single file
const { require: read } = require('isolation');
read.script('./path/to/script.js').then(console.log);
read.script('./path/to/script.js', { prepare: true }).then(console.log);
-
read.dir
Allow you to read a directory
const { require: read } = require('isolation');
read.script('./path/to').then(console.log);
read.script('./path/to', { prepare: true }).then(console.log); Output: { script: Script {} }
read.script('./path/to', {}, false).then(console.log);
Other useful information
-
Script from string You can run any script from string, just like eval, but in custom VM
container. But you shouldn't use it for unknown script evaluation, it may create security
issues.
const Isolation = require('isolation');
console.log(Isolation.execute(`module.exports = (a, b) => a + b;`)(2 + 2));
Isolation.execute(`module.exports = async (a, b) => a + b;`)(2 + 2).then(console.log);
-
Library substitution For example it can be use to provide custom fs module, with your strict
methods
const { execute: exec } = require('isolation');
const src = `
const fs = require('fs');
module.exports = fs.readFile('Isolation.js');
`;
const result = exec(src, {
access: {
sandbox: module => ({ fs: { readFile: (filename) => filename + ' Works !' } })[module];
},
});
console.log(result);
Script Options
- filename: Stands for the name of the module, by default it's empty string
- dir: Stands for the name of the module directory, by default
process.cwd()
- npmIsolation: Use it if you want to isolate your npm modules in vm context, default false.
- ctx: See Context API
- access: See Access API
- prepare: Works only with read API. Functions, where this option provided, will return
intermediate object and you will be able to finish execution later. Script has alternative to this
option -
prepare method
. - script & run This options allow you to configure VM.Script initialization & execution.
Copyright & contributors
Copyright © 2023 Astrohelm contributors.
This library is MIT licensed license.
And it is part of Astrohelm solutions.