JEP 202: Nashorn Class Filter

OwnerSundararajan Athijegannathan
Created2014/05/22 06:51
Updated2015/03/03 04:24
TypeFeature
StatusClosed / Delivered
Componentcore-libs / jdk.nashorn
ScopeJDK
Discussionnashorn dash dev at openjdk dot java dot net
EffortS
DurationS
Priority3
Reviewed byJim Laskey
Endorsed byBrian Goetz
Release8u40
Issue8043717

Summary

Provide fine-grained control over access to Java classes from JavaScript code.

Goals

Non-goals

The API will not:

Motivation

There are several global objects such as Packages, java, org, com, javax, javafx, org, net, JavaImporter and Java that provide easy access to Java classes from JavaScript code in Nashorn. With a security manager on, the expected Java security policy is enforced: Anonymous scripts are granted the same permissions as are granted to untrusted classes, or else a script loaded from a URL is granted the permissions associated with that URL.

When using Nashorn with the –no-java option, the above global package objects are not provided, effectively preventing all Java package and class access from scripts. A script has either complete Java access, subject to security restrictions, or else no access with –no-java.

The Rhino JavaScript engine provides finer-grained access control via the user defined ClassShutter class. Rhino-embedding Java applications can choose to expose a subset of Java classes to scripts. Nashorn should provide similar fine-grained Java API.

Server-side JavaScript frameworks are an important target for the Nashorn script engine. Such frameworks may occasionally have to run scripts from untrusted sources and therefore limit access to Java APIs. While security-manager based sandboxing helps here, Nashorn-embedding server-side applications often need finer control. An API should make it possible to restrict Java class access to a subset of Java classes.

Description

A new interface, jdk.nashorn.api.scripting.ClassFilter will be defined. Nashorn-embedding applications such as server-side JavaScript frameworks may choose to implement ClassFilter. To provide a user-implemented ClassFilter to Nashorn, the client application will have to use the Nashorn-specific API to instantiate the Nashorn script engine.

New API added to class jdk.nashorn.api.scripting.NashornScriptEngineFactory:

/**
  * Create a new Script engine initialized by given class filter.
  *
  * @return newly created script engine.
  * @throws NullPointerException if {@code classFilter} is {@code null}
  * @throws SecurityException
  *         if the security manager's {@code checkPermission}
  *         denies {@code RuntimePermission("nashorn.setConfig")}
  */
 public ScriptEngine getScriptEngine(final ClassFilter classFilter)

/**
 * Create a new Script engine initialized by given arguments.
 *
 * @param args arguments array passed to script engine.
 * @param appLoader class loader to be used as script "application" class loader.
 * @param classFilter class filter to use.
 * @return newly created script engine.
 * @throws NullPointerException if {@code args} or {@code classFilter} is {@code null}
 * @throws SecurityException
 *         if the security manager's {@code checkPermission}
 *         denies {@code RuntimePermission("nashorn.setConfig")}
 */
 public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter)

New interface jdk.nashorn.api.scripting.ClassFilter:

public interface ClassFilter {
     /**
      * Should the Java class of the specified name be exposed to scripts?
      * @param className is the fully qualified name of the java class being checked. This will not be null. Only non-array class names will be passed.
      * @return true if the java class can be exposed to scripts false otherwise
      */  
     public boolean exposeToScripts(String className);
}

The Nashorn script interface to Java packages and classes is via the Packages object and the Java.type function. The Packages object allows Java classes and packages be accessed via the familiar "dot-dot" syntax. The Java.type function accepts the string name of a fully-qualified Java type and returns the corresponding Class object. Both of these APIs will check if there is a class filter set in the Nashorn script engine. If a class filter is present then the interface will query it to see if the class can be exposed to scripts. If the filter returns false then the Java.type function will throw ClassNotFoundException and the Packages API will treat a dotted name as a package name rather than a class name.

Example:

var Vector = java.util.Vector;
var Thread = Java.type(`java.lang.Thread`);

If the class filter prevents access to java.util.Vector and java.lang.Thread then in the first line java.util.Vector will be treated as a package name instead of being resolved as a Java class, and in the second line the invocation of Java.type will result in a ClassNotFoundException being thrown.

If a security manager is present then Nashorn allows a script to use the Java reflection APIs (i.e., java.lang.reflect and java.lang.invoke) only if the script has the nashorn.javaReflection run-time permission. If a class filter is present then Nashorn will prevent access to reflective APIs even when a security manager is not present. It does not make sense to use a class filter if reflection is available, because a script can use the Class.forName(String) method to circumvent the class filter.

Testing

There are over 1,000 Nashorn script tests in the Nashorn repository. There are tests accessing the javax.script API as well as the Nashorn-specific APIs in the jdk.nashorn.api.scripting package. The latter will be extended to include class-filter tests.

Risks and Assumptions

The new API will be added to the existing jdk.nashorn.api.scripting package. Users will have to use this non-standard, JDK-specific API in order to implement class filters.