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
First part:
If you eval the following in global scope, you'll get all properties including the non-enumerable ones.
Object.getOwnPropertyNames(this)
(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.