isolated-vm icon indicating copy to clipboard operation
isolated-vm copied to clipboard

How to import modules

Open nithiwatter opened this issue 1 year ago • 9 comments

What is the best way in general to import modules like crypto and fetch without making wrapper methods. Currently, we are passing these methods via the references. However, assuming that crypto has many methods, it would be a bit of work to make wrappers for all methods of this module. Thank you!

nithiwatter avatar Apr 22 '23 01:04 nithiwatter

I managed to import all methods of lodash programmatically by looping through them, setting them to the global context, and reassigning them to a new variable. My method only works for static methods though, class instances using "this" will be broken. Not sure if it helps.

            const myLibs = { lodash, someOtherStaticLib };
            const isolate = new ivm.Isolate({ memoryLimit: 8 });
            const context = isolate.createContextSync();
            const jail = context.global;
            jail.setSync('global', jail.derefInto());
            jail.setSync('ivm', ivm);
            let scriptLines: string[] = [];
            if (myLibs) {
                Object.entries(myLibs).forEach(([libName, libValue]) => {
                    // creating empty object with the name of the lib
                    scriptLines.push(`globalThis.${libName} = {}`);
                    const lib = libValue as Record<string, unknown>;
                    // reconstructing the lib by iterating through all of its methods, setting them to the global object, and reassigning them to the created object
                    Object.keys(lib).forEach((method) => {
                        if (Object.prototype.hasOwnProperty.call(lib, method) && typeof lib[method] === 'function') {
                            jail.setSync(`ref_${libName}_${method}`, lib[method]);
                            scriptLines.push(`globalThis.${libName}.${method} = ref_${libName}_${method};`);
                        }
                    });
                });
            }
            // these lines allow to retrieve non-primary values (otherwise arrays and objects return undefined)
            scriptLines.push(`const evalResult = eval(${JSON.stringify(statement)});`);
            scriptLines.push(`new ivm.Reference(evalResult);`);
            const result = isolate.compileScriptSync(scriptLines.join('\n')).runSync(context);
            const value = result.copySync();

jooleeanh avatar Aug 01 '23 07:08 jooleeanh

Why not just evaluate the source of lodash from within the isolate?

laverdet avatar Aug 01 '23 07:08 laverdet

Are you referring to this method ? https://github.com/laverdet/isolated-vm/issues/199#issuecomment-695114062 In my case, I was using vm2 before it got deprecated, and am trying to replicate the ability to import libraries dynamically, but while static objects seem to work fine, I haven't had any success with class instances (it breaks when hitting a "this.[method]"). Do you think it's possible ?

jooleeanh avatar Aug 01 '23 09:08 jooleeanh

It helps to think of isolated-vm like a browser. How would you transfer an instantiated class from nodejs to Chrome? You can't! In the case of lodash the answer is to just send the code to isolated-vm in the same way would send it to a client.

laverdet avatar Aug 01 '23 17:08 laverdet

Thanks @laverdet for the awesome tool and thanks for giving that webpack solution in the other thread: https://github.com/laverdet/isolated-vm/issues/199#issuecomment-695114062

I prefer requiring modules to be loaded into the context ahead of time (rather than dynamically). Seems like a safer runtime design

shoesCodeFor avatar Aug 02 '23 18:08 shoesCodeFor

Hey @laverdet, I started migrating from vm2 and now currently I'm trying to import module like this:

const code = `import Component from '/build/model/event/component.js';`;
const module = await isolate.compileModule(code);

      await module.instantiate(context, (s, r) => {
          let m = fs.readFileSync(s).toString()
          return isolate.compileModuleSync(m)
      })

        module.evaluateSync()
        jail.setSync("component", module.namespace.derefInto({release:true}))

component.js file:

export default class Component {
    constructor() {}
    method1() {}
    method2() {}
}

I'm not getting errors, but now I don't know how to use this module, for example, running this code new Component() gives me an error Component is not defined.

What am I doing wrong here?

kilgaloon avatar Aug 22 '23 09:08 kilgaloon

Your code is incomplete. I can't help you unless you post a complete example.

laverdet avatar Aug 23 '23 18:08 laverdet

Well lets say we build a module like in the code example I provided and now you want to use it in the code:

const hostile = isolate.compileScriptSync(`
	const c = new Component();
`);

hostile.run(context).catch(err => console.error(err));

or

const hostile = isolate.compileScriptSync(`
	const c = new component();
`);

hostile.run(context).catch(err => console.error(err));

This will throw Component is not defined.

kilgaloon avatar Aug 24 '23 11:08 kilgaloon

You are posting code fragments. You need to post a complete program. I can't just guess what the rest of the code looks like. Put yourself in my shoes, I can't troubleshoot the program that you haven't given me.

laverdet avatar Aug 24 '23 16:08 laverdet