A sandbox for executing JavaScript in Java apps using the Nashorn engine.
Also see Graal JS Sandbox and Rhino Sandbox.
Part of the Java Delight Suite.
Note: Use version 0.3.x if you are using a Java version older than Java 20.
The sandbox by default blocks access to all Java classes.
Classes, which should be used in JavaScript, must be explicitly allowed.
NashornSandbox sandbox = NashornSandboxes.create();
sandbox.allow(File.class);
sandbox.eval("var File = Java.type('java.io.File'); File;")
Or you can inject your Java object as a JS global variable
NashornSandbox sandbox = NashornSandboxes.create();
sandbox.inject("fromJava", new Object());
sandbox.eval("fromJava.getClass();");
The sandbox also allows limiting the CPU time and memory usage of scripts. This allows terminating scripts which contain infinite loops and other problematic code.
NashornSandbox sandbox = NashornSandboxes.create();
sandbox.setMaxCPUTime(100);
sandbox.setMaxMemory(50*1024);
sandbox.setMaxPreparedStatements(30); // because preparing scripts for execution is expensive
sandbox.setExecutor(Executors.newSingleThreadExecutor());
sandbox.eval("var o={}, i=0; while (true) {o[i++]='abc';};");
This code will raise a ScriptCPUAbuseException.
The sandbox beautifies the JavaScript code for this and injects additional statements into the submitted code. It is thus possible that the original line numbers from
the submitted JS code are not preserved. To debug the code, which is generated by the sandbox, activate its debug mode as follows using a log4j.properties
file (see log4j.properties):
log4j.logger.delight.nashornsandbox.NashornSandbox=DEBUG
This will output the generated JS on the console as follows:
--- Running JS ---
var \__it = Java.type('delight.nashornsandbox.internal.InterruptTest');var \__if=function(){\__it.test();};
while(true) {__if();
i = i+1;
}
--- JS END ---
The sandbox also allows precompiling frequently used scripts. Using a precompiled script can substantially increase execution times.
NashornSandbox sandbox = NashornSandboxes.create();
CompiledScript script = sandbox.compile("1 + 1");
int result1 = (int) sandbox.eval(script);
int result2 = (int) sandbox.eval(script);
Just add the following dependency to your projects.
<dependency>
<groupId>org.javadelight</groupId>
<artifactId>delight-nashorn-sandbox</artifactId>
<version>[insert latest version]</version>
</dependency>
Note that up to version v.0.1.31 the library would only work with Java versions lower than 13. To make the library work with Java version 13 and above, please use a library version 0.2.0+. The compatibility with v0.2.0 with lower versions of Java is still experimental. If you encounter issues, specifically in Java 1.8 please use a 0.1.x version (and please report any issues).
This artifact is available on Maven Central.
If you are looking for a JAR with all dependencies, you can also download it from here.
Eduardo Velasques: API extensions to block/allow Rhino system functions; Capability to block/allow variables after Sandbox has been created.
Marcin Gołębski: Major refactoring and performance improvements. Among other things improved the performance for JS evaluation and better handling of monitoring for threads for possible CPU abuse (#23).
Marco Ellwanger: Initial support for GraalJS engine by implementing sandbox implementation backed by GraalJS.
Olivier Bourgain: Detection for JDK version and ability to use standalone Nashorn for JDK versions in which it is not included.
busterace: Perform code injection using AST rather than regex (#157)
allowNoBraces
no longer supported, since not required.allowNoBraces
to true
since the check easily leads to false positives (Issue #102 and Issue #104) --no-java
engine parameter (see issue #57)exit
is blocked; Allowing for|while|do when they are given in quoted strings in JavaScript (see issue #47).do
, while
and for
in comments might cause BracesExceptions (see bug #34)monitor.wait(0)
(see issue 30)protected
rather than private
(see issue #19)