microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.76k stars 148 forks source link

Option to disable the code execution from string (eval and function) #605

Open simontom opened 20 hours ago

simontom commented 20 hours ago

Hey folks, our scenario is "customer scriptable multitenant" runtime. We want to be very cautious what to allow.

In order to make it a bit more safer, we need the ClearScript API to disable eval and other ECMAScript APIs that convert strings into code (e.g., the Function CTOR).

I see there is some flag in V8 (pro'ly) resulting right in the behaviour we crave for: "--disallow-code-generation-from-strings"

We were able "disable" eval exectuting the following script:

eval = function() {
    throw new Error("The 'eval' function is disabled.");
}

This is what I've tried with function CTOR so far

test('use function for code generation', async (t) => {
    // Has no effect
    (function () { }).constructor = null;
    // so ....
    // These are the main problem
    const FuncCtor = (function () { }).constructor;
    const AsyncFuncCtor = (async function () { }).constructor;

    const log1 = new FuncCtor('str', 'console.log(str);');
    log1('Hello, world! 1');

    const fetchURL = new AsyncFuncCtor('url', 'return await fetch(url);');
    await fetchURL("https://www.google.com")
        .then((res) => res.text())
        .then((text) => text.slice(0, 100))
        .then(console.log);

    // This is fine, throws (but the lines above....)
    Function = null;
    const log2 = new Function('str', 'console.log(str);');
    log2('Hello, world! 2');
});
ClearScriptLib commented 14 hours ago

Hi @simontom,

How about something like this:

engine.Execute(@"(() => {
    const AsyncFunction = (async () => {}).constructor;
    const ctor = (function () { throw new Error('Function constructors are disabled'); }).bind();
    Object.defineProperty(Function.prototype, 'constructor', { value: ctor });
    Object.defineProperty(AsyncFunction.prototype, 'constructor', { value: ctor });
    Function = new Proxy(Function, { construct: ctor, apply: ctor });
})()");

Please let us know if that works for you. Thanks!