isolated-vm
isolated-vm copied to clipboard
How to pass in an async function to an isolate and await it
I just pushed 3.0.0 to npm with some new features which improve situations like this. The previous example could be rewritten like this:
const ivm = require('isolated-vm') const isolate = new ivm.Isolate() const context = isolate.createContextSync() async function runCode() { const fn = await context.eval('(function untrusted() { return Promise.resolve(123); })', { reference: true }) return fn.result.apply(undefined, [], { result: { promise: true } }) } runCode().then(value => console.log(value)) .catch(error => console.error(error))
Let me know if it works out for you~
Originally posted by @laverdet in https://github.com/laverdet/isolated-vm/issues/125#issuecomment-566750394
I saw this and was able to get this to work. However, I'm wondering how I can pass a async function into an isolate and run it within the isolate and return the results. For example,
const isolate = new ivm.Isolate();
const context = await isolate.createContext();
const code=`
(async function main() {
const a = (await p()) + "2"; // throws an error saying that #<Promise> could not be cloned
return a;
})`
async function runCode() {
await context.global.set(
"p",
new ivm.Callback(
async () => {
console.log("started this");
const b = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("resolved");
}, 500);
});
return await b;
},
{ async: true }
));
const fn = await context.eval(code, { reference: true });
return await fn.apply(undefined, [], {
result: { promise: true},
});;
}
const res = await runCode()
console.log(res)
Expected result: "resolved2"
attached are logs as well pictures of the code that is being run on my local machine with logging that was removed from the above versions
typeof p: function
p to string function context.global.set.isolated_vm_1.default.Callback.async() { [native code] }
started this
throwing error inside TypeError: #<Promise> could not be cloned.
at (<isolated-vm boundary>)
at main (<isolated-vm>:6:21)
res starting
c resolved
Was able to get it to work with
class FetchWrapper {
static methods = [this.fetch];
static async fetch(url: RequestInfo, init?: RequestInit | undefined) {
const response = await fetch.apply(this, [url, init]);
const res = await response.text();
return res;
}
}
context.evalClosure(
`
fetch = function (url, init) {
return $0.apply(undefined, [url, init], { arguments: { copy: true }, result: { promise: true } });
}
`,
FetchWrapper.methods,
{ arguments: { reference: true } }
);
const res = await context.eval(node.code, { promise: true });
const response = await fetch("http://url.com");
data = JSON.parse(response);
return data.param;
Ideally I wouldn't have to do the await response.text();
because it makes the passed in code different that just basic js, but works good enough
The example can be even simpler:
The main thing is that when running you need to tell the context is a promise.
const isolate = new ivm.Isolate({ memoryLimit: 124 });
const isolateSource = 'code here';
const context = isolate.createContextSync();
const hostile = isolate.compileScriptSync(isolateSource );
await hostile.run(context, {promise: true});
@justinmmott are you able to do context.global.set
with the fetch wrapper that you've created? If so do you mind sharing a code snippet of how you achieve that? Thanks in advance!