Giter Club home page Giter Club logo

Comments (5)

laverdet avatar laverdet commented on August 22, 2024

The timeout only applies to the initial synchronous execution. In this case your anonymous function is invoked, it invokes externalFunction which immediately returns a promise. Then myfunc() returns a promise and evaluation has finished.

The promise-related convenience utilities present a challenge when invoked against untrusted code which needs to timeout.

from isolated-vm.

JeanSamGirard avatar JeanSamGirard commented on August 22, 2024

This makes so much sense, can't believe I didn't notice that...

Also makes me realize that my workaround doesn't really work :

import ivm from 'isolated-vm';

const isolate = new ivm.Isolate();
const context = await isolate.createContext();

// Set a reference to a function outside of the isolate
context.global.set(
    'externalFunctionRef',
    new ivm.Reference(async (params) => {
        // Asynchronous code here (database query, http request, etc...)
    })
);

// Make a function in the isolate to abstract the call to the external function
context.evalIgnored(`
let started;
const externalFunction = async (params) => {
    if(Date.now() > started + 5000) throw "Timeout reject the promise";
    return await externalFunctionRef.apply(null, [params], {
        arguments: { copy: true },
        result: { copy: true, promise: true },
    });
};`);

// Later on...

// Run the actual code inside the isolate

const code = `started = Date.now(); // I inject this before the "untrusted code" (in my actual use case, the code is transpiled so I have control on the variable names used in the user code, they wouldn't be able to alter this value)

    const myfunc = async ()=>{
        let firstTime = true;
        while(true){
         if(firstTime) await externalFunction(); // myfunc will return a promise here
         firstTime = false; // The promise never resolves and can't be rejected by externalFunction because externalFunction doesn't get called anymore...
    }}; myfunc();`;

try {
    console.log(
        'RESULT: ' +
            (await context.eval(code, {
                timeout: 5000,
                promise: true,
                copy: true,
            }))
    );
} catch (e) {
    console.error(e);
}

I guess I could add the same timeout check that I do in externalFunction at the beginning of while and for loops during transpilation.

Would that cover everything to prevent infinite code execution ?

They could still write recursive code but they'll hit the max call stack size and stop eventually.

from isolated-vm.

JeanSamGirard avatar JeanSamGirard commented on August 22, 2024

Would calling isolate.dispose() force the pending promise to reject ?

from isolated-vm.

laverdet avatar laverdet commented on August 22, 2024

If you want to have a timeout which is cumulative between multiple entrances of the isolate, and also includes asynchronous wall time, then you'll need to handle that bookkeeping outside of isolated-vm.

The problem is not simple. For example you need to make sure that await Promise.all([ externalFunction(), externalFunction() ]) doesn't "charge" the user for 2x wall time.

My advice here is to pretend that { promise: true } does not exist and instead pass around your own resolver functions.

from isolated-vm.

JeanSamGirard avatar JeanSamGirard commented on August 22, 2024

Thanks for the help.

I think for my use case I'll go with this for now:

import ivm from 'isolated-vm';

const isolate = new ivm.Isolate();
const context = await isolate.createContext();

// Set a reference to a function outside of the isolate
context.global.set(
    'externalFunctionRef',
    new ivm.Reference(async (params) => {
        // Asynchronous code here (database query, http request, etc...)
    })
);

// Make a function in the isolate to abstract the call to the external function
context.evalIgnored(`
let started;
const externalFunction = async (params) => {
    if(Date.now() > started + 5000) throw "Timeout reject the promise";
    return await externalFunctionRef.apply(null, [params], {
        arguments: { copy: true },
        result: { copy: true, promise: true },
    });
};`);

// Later on...

// Run the actual code inside the isolate

const code = `started = Date.now(); // I inject this before the "untrusted code" (in my actual use case, the code is transpiled so I have control on the variable names used in the user code, they wouldn't be able to alter this value)

    const myfunc = async ()=>{
        let firstTime = true;
        while(true){
         if(firstTime) await externalFunction(); // myfunc will return a promise here
         firstTime = false; // The promise never resolves and can't be rejected by externalFunction because externalFunction doesn't get called anymore...
    }}; myfunc();`;

let promiseRef;

try {
    promiseRef = context.eval(code, {
        timeout: 5000,
        promise: true,
        copy: true,
    });

    console.log(
        'RESULT: ' +
            (await Promise.race([
                promiseRef,
                new Promise((resolve, reject) => {
                    setTimeout(() => reject('TIMED OUT'), 5000);
                }),
            ]))
    );
} catch (e) {
    console.error(e);
} finally {
    if (!isolate.isDisposed) isolate.dispose();
    setTimeout(() => console.log(promiseRef), 1000);
}

I don't mind the infinite loop running for a little while longer in the isolate as long as it doesn't stop my main process awaiting it and the Promise gets rejected when the isolate is disposed of.

from isolated-vm.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.