Search code examples
javanashorn

Disabling ECMAScript extensions in Nashorn


I need to run a simple script from inside my app, and users should be able to enter/modify this script itself. The script is intended to check/modify some data/fields set, that could be represented for instance as a Hashtable. The logic is simple - check some values and modify others depending on previous checks. The logic is simple but shall be flexible, and I have "legacy" C program that uses lua for that. I found luaj first, but then I discovered 😉 that modern Java supports ECMAScript out of the box, that's why I'm trying to use Nashorn. Then I found that Nashorn is too much integrated with jvm and this may lead to abuse. So, I tried to prevent misuse disabling some features. First, I found some command-line arguments, that prevents some of the extensions and the java classes availability in the scripts. I found as well, that Nashorn exposes some functions that potentially can be dangerous, like quit(), load() and so on. So, my code looks like that:

ScriptEngine engine=new NashornScriptEngineFactory().getScriptEngine(new String[]{"-nj", "-nse"}, null
        new ClassFilter()
        {
            @Override
            public boolean exposeToScripts(String s)
            {
                return false;
            }
        });
Bindings bd;
bd=engine.getBindings(ScriptContext.ENGINE_SCOPE);
bd.remove("loadWithNewGlobal");
bd.remove("load");
bd.remove("quit");
// ... and so on

As far as I understand, since "bd" is the reference, it deletes actual bindings and these functions cannot be called from inside script. But then I tried to list all bindings, and found there are none! Even before deletion, I see that the bd's size is "0" and the Bindings Map is empty:

System.out.printf("Bindings no: %d\n", bd.size());    
for(String key : bd.keySet())
{
    System.out.printf("'%s'='%s'\n", key, bd.get(key).toString());
}

Prints "0" and that's all.

So, I actually have two questions: 1) Why can't I see all bindings? (although I can delete them and I see that without this removal these functions can be called from script, but after I removed them they ain't available anymore and each call throws an exception) And how can I list what is bound then? 2) Is there anything else I forgot, that should be disabled in the scripting? (except, for sure, infinite loops and so on - users are more or less "trusted", but I'd like to prevent some "dangerous" actions anyway)

Thank you


Solution

  • First part:

    If you eval the following in global scope, you'll get all properties including the non-enumerable ones.

    Object.getOwnPropertyNames(this)
    

    See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames

    (Updated after a comment from "BblKTOP")

    Only (JS) enumerable properties are returned when iterating key set using javax.script.Bindings. But there is equivalent of Object.getOwnPropertyNames(this). That would be something like:

    import javax.script.*;
    import jdk.nashorn.api.scripting.*;
    
    public class Main {
       public static void main(String[] args) throws Exception {
          ScriptEngineManager m = new ScriptEngineManager();
          ScriptEngine e = m.getEngineByName("nashorn");
          ScriptObjectMirror thiz = (ScriptObjectMirror)e.eval("this");
          // ScriptObjectMirror.getOwnKeys(boolean) returns all own property names. 
          // "true" to return all non-enumerable property names as well.
          // This is equivalent to JS call:
          //     Object.getOwnPropertyNames(this)
          for (String key : thiz.getOwnKeys(true)) {
              System.out.println(key);
          }
       }
    }
    

    Second part:

    I think you seem to have covered. But you may want to make sure that you run with security manager on. With security manager set, all scripts "eval"-ed are treated as sandbox scripts and so no security sensitive operation is allowed.

    Another option for sandboxing: consider a domain specific language which you could translate to JavaScript.