Rhino code injection

This commit is contained in:
luchua-bc
2021-04-29 02:59:41 +00:00
parent 9deaace756
commit b0b5338359
14 changed files with 1589 additions and 1 deletions

View File

@@ -0,0 +1,39 @@
public class RhinoInjection extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
Context ctx = Context.enter();
try {
{
// BAD: allow arbitrary Java and JavaScript code to be executed
Scriptable scope = ctx.initStandardObjects();
}
{
// GOOD: enable the safe mode
Scriptable scope = ctx.initSafeStandardObjects();
}
{
// GOOD: enforce a constraint on allowed classes
Scriptable scope = ctx.initStandardObjects();
ctx.setClassShutter(new ClassShutter() {
public boolean visibleToScripts(String className) {
if(className.startsWith("com.example.")) {
return true;
}
return false;
}
});
}
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
response.getWriter().print(Context.toString(result));
} catch(RhinoException ex) {
response.getWriter().println(ex.getMessage());
} finally {
Context.exit();
}
}
}

View File

@@ -0,0 +1,51 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Rhino is a JavaScript engine written fully in Java and managed by the Mozilla Foundation.
It serves as an embedded scripting engine inside Java applications which allows
Java-to-JavaScript interoperability and provides a seamless integration between the two
languages. If an expression is built using attacker-controlled data, and then evaluated in
a powerful context, it may allow the attacker to run arbitrary code.
</p>
<p>
Typically an expression is evaluated in the powerful context initialized with
<code>initStandardObjects</code> that allows an expression of arbitrary Java code to
execute in the JVM.
</p>
</overview>
<recommendation>
<p>
In general, including user input in a Rhino expression should be avoided.
If user input must be included in the expression, it should be then evaluated in a safe
context that doesn't allow arbitrary code invocation.
</p>
</recommendation>
<example>
<p>
The following example shows two ways of using Rhino expression. In the 'BAD' case,
an unsafe context is initialized with <code>initStandardObjects</code>. In the 'GOOD' case,
a safe context is initialized with <code>initSafeStandardObjects</code> or
<code>setClassShutter</code>.
</p>
<sample src="RhinoInjection.java" />
</example>
<references>
<li>
Mozilla Rhino:
<a href="https://github.com/mozilla/rhino">Rhino: JavaScript in Java</a>
</li>
<li>
Rhino Sandbox:
<a href="https://github.com/javadelight/delight-rhino-sandbox">A sandbox to execute JavaScript code with Rhino in Java.</a>
</li>
<li>
GuardRails:
<a href="https://docs.guardrails.io/docs/en/vulnerabilities/java/insecure_use_of_dangerous_function">Code Injection</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,17 @@
/**
* @name Injection in Mozilla Rhino JavaScript Engine
* @description Evaluation of a user-controlled JavaScript or Java expression in Rhino
* JavaScript Engine may lead to remote code execution.
* @kind path-problem
* @id java/rhino-injection
* @tags security
* external/cwe/cwe-094
*/
import java
import RhinoInjection
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, RhinoInjectionConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Rhino injection from $@.", source.getNode(), " user input"

View File

@@ -0,0 +1,56 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
/** The class `org.mozilla.javascript.Context`. */
class Context extends RefType {
Context() { this.hasQualifiedName("org.mozilla.javascript", "Context") }
}
/**
* A method that evaluates a Rhino expression.
*/
class EvaluateExpressionMethod extends Method {
EvaluateExpressionMethod() {
this.getDeclaringType().getAnAncestor*() instanceof Context and
(
hasName("evaluateString") or
hasName("evaluateReader")
)
}
}
/**
* A taint-tracking configuration for unsafe user input that is used to evaluate
* a Rhino expression.
*/
class RhinoInjectionConfig extends TaintTracking::Configuration {
RhinoInjectionConfig() { this = "RhinoInjectionConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource
or
source instanceof LocalUserInput
}
override predicate isSink(DataFlow::Node sink) { sink instanceof EvaluateExpressionSink }
}
/**
* A sink for Rhino code injection vulnerabilities.
*/
class EvaluateExpressionSink extends DataFlow::ExprNode {
EvaluateExpressionSink() {
exists(MethodAccess ea, EvaluateExpressionMethod m | m = ea.getMethod() |
this.asExpr() = ea.getArgument(1) and // The second argument is the JavaScript or Java input
not exists(MethodAccess ca |
(
ca.getMethod().hasName("initSafeStandardObjects") // safe mode
or
ca.getMethod().hasName("setClassShutter") // `ClassShutter` constraint is enforced
) and
ea.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess()
)
)
}
}

View File

@@ -0,0 +1,7 @@
edges
| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code |
nodes
| RhinoServlet.java:25:23:25:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| RhinoServlet.java:29:55:29:58 | code | semmle.label | code |
#select
| RhinoServlet.java:29:55:29:58 | code | RhinoServlet.java:25:23:25:50 | getParameter(...) : String | RhinoServlet.java:29:55:29:58 | code | Rhino injection from $@. | RhinoServlet.java:25:23:25:50 | getParameter(...) | user input |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-094/RhinoInjection.ql

View File

@@ -0,0 +1,78 @@
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mozilla.javascript.ClassShutter;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.RhinoException;
/**
* Servlet implementation class RhinoServlet
*/
public class RhinoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public RhinoServlet() {
super();
}
// BAD: allow arbitrary Java and JavaScript code to be executed
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
Context ctx = Context.enter();
try {
Scriptable scope = ctx.initStandardObjects();
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
response.getWriter().print(Context.toString(result));
} catch(RhinoException ex) {
response.getWriter().println(ex.getMessage());
} finally {
Context.exit();
}
}
// GOOD: enable the safe mode
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
Context ctx = Context.enter();
try {
Scriptable scope = ctx.initSafeStandardObjects();
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
response.getWriter().print(Context.toString(result));
} catch(RhinoException ex) {
response.getWriter().println(ex.getMessage());
} finally {
Context.exit();
}
}
// GOOD: enforce a constraint on allowed classes
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
String code = request.getParameter("code");
Context ctx = Context.enter();
try {
Scriptable scope = ctx.initStandardObjects();
ctx.setClassShutter(new ClassShutter() {
public boolean visibleToScripts(String className) {
if(className.startsWith("com.example.")) {
return true;
}
return false;
}
});
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
response.getWriter().print(Context.toString(result));
} catch(RhinoException ex) {
response.getWriter().println(ex.getMessage());
} finally {
Context.exit();
}
}
}

View File

@@ -1,2 +1,2 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13

View File

@@ -0,0 +1,56 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// API class
package org.mozilla.javascript;
/**
Embeddings that wish to filter Java classes that are visible to scripts
through the LiveConnect, should implement this interface.
@see Context#setClassShutter(ClassShutter)
@since 1.5 Release 4
@author Norris Boyd
*/
public interface ClassShutter {
/**
* Return true iff the Java class with the given name should be exposed
* to scripts.
* <p>
* An embedding may filter which Java classes are exposed through
* LiveConnect to JavaScript scripts.
* <p>
* Due to the fact that there is no package reflection in Java,
* this method will also be called with package names. There
* is no way for Rhino to tell if "Packages.a.b" is a package name
* or a class that doesn't exist. What Rhino does is attempt
* to load each segment of "Packages.a.b.c": It first attempts to
* load class "a", then attempts to load class "a.b", then
* finally attempts to load class "a.b.c". On a Rhino installation
* without any ClassShutter set, and without any of the
* above classes, the expression "Packages.a.b.c" will result in
* a [JavaPackage a.b.c] and not an error.
* <p>
* With ClassShutter supplied, Rhino will first call
* visibleToScripts before attempting to look up the class name. If
* visibleToScripts returns false, the class name lookup is not
* performed and subsequent Rhino execution assumes the class is
* not present. So for "java.lang.System.out.println" the lookup
* of "java.lang.System" is skipped and thus Rhino assumes that
* "java.lang.System" doesn't exist. So then for "java.lang.System.out",
* Rhino attempts to load the class "java.lang.System.out" because
* it assumes that "java.lang.System" is a package name.
* <p>
* @param fullClassName the full name of the class (including the package
* name, with '.' as a delimiter). For example the
* standard string class is "java.lang.String"
* @return whether or not to reveal this class to scripts
*/
public boolean visibleToScripts(String fullClassName);
}

View File

@@ -0,0 +1,623 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// API class
package org.mozilla.javascript;
import java.io.Closeable;
import java.io.IOException;
import java.io.Reader;
import java.util.Locale;
/**
* This class represents the runtime context of an executing script.
*
* Before executing a script, an instance of Context must be created
* and associated with the thread that will be executing the script.
* The Context will be used to store information about the executing
* of the script such as the call stack. Contexts are associated with
* the current thread using the {@link #call(ContextAction)}
* or {@link #enter()} methods.<p>
*
* Different forms of script execution are supported. Scripts may be
* evaluated from the source directly, or first compiled and then later
* executed. Interactive execution is also supported.<p>
*
* Some aspects of script execution, such as type conversions and
* object creation, may be accessed directly through methods of
* Context.
*
* @see Scriptable
* @author Norris Boyd
* @author Brendan Eich
*/
public class Context
implements Closeable
{
/**
* Creates a new Context. The context will be associated with the {@link
* ContextFactory#getGlobal() global context factory}.
*
* Note that the Context must be associated with a thread before
* it can be used to execute a script.
* @deprecated this constructor is deprecated because it creates a
* dependency on a static singleton context factory. Use
* {@link ContextFactory#enter()} or
* {@link ContextFactory#call(ContextAction)} instead. If you subclass
* this class, consider using {@link #Context(ContextFactory)} constructor
* instead in the subclasses' constructors.
*/
@Deprecated
public Context()
{
}
/**
* Creates a new context. Provided as a preferred super constructor for
* subclasses in place of the deprecated default public constructor.
* @param factory the context factory associated with this context (most
* likely, the one that created the context). Can not be null. The context
* features are inherited from the factory, and the context will also
* otherwise use its factory's services.
* @throws IllegalArgumentException if factory parameter is null.
*/
protected Context(ContextFactory factory)
{
}
/**
* Get the current Context.
*
* The current Context is per-thread; this method looks up
* the Context associated with the current thread. <p>
*
* @return the Context associated with the current thread, or
* null if no context is associated with the current
* thread.
* @see ContextFactory#enterContext()
* @see ContextFactory#call(ContextAction)
*/
public static Context getCurrentContext()
{
return null;
}
/**
* Same as calling {@link ContextFactory#enterContext()} on the global
* ContextFactory instance.
* @return a Context associated with the current thread
* @see #getCurrentContext()
* @see #exit()
* @see #call(ContextAction)
*/
public static Context enter()
{
return null;
}
/**
* Get a Context associated with the current thread, using
* the given Context if need be.
* <p>
* The same as <code>enter()</code> except that <code>cx</code>
* is associated with the current thread and returned if
* the current thread has no associated context and <code>cx</code>
* is not associated with any other thread.
* @param cx a Context to associate with the thread if possible
* @return a Context associated with the current thread
* @deprecated use {@link ContextFactory#enterContext(Context)} instead as
* this method relies on usage of a static singleton "global" ContextFactory.
* @see ContextFactory#enterContext(Context)
* @see ContextFactory#call(ContextAction)
*/
@Deprecated
public static Context enter(Context cx)
{
return null;
}
static final Context enter(Context cx, ContextFactory factory)
{
return null;
}
/**
* Exit a block of code requiring a Context.
*
* Calling <code>exit()</code> will remove the association between
* the current thread and a Context if the prior call to
* {@link ContextFactory#enterContext()} on this thread newly associated a
* Context with this thread. Once the current thread no longer has an
* associated Context, it cannot be used to execute JavaScript until it is
* again associated with a Context.
* @see ContextFactory#enterContext()
*/
public static void exit()
{
}
@Override
public void close() {
}
/**
* Return {@link ContextFactory} instance used to create this Context.
*/
public final ContextFactory getFactory()
{
return null;
}
/**
* Checks if this is a sealed Context. A sealed Context instance does not
* allow to modify any of its properties and will throw an exception
* on any such attempt.
* @see #seal(Object sealKey)
*/
public final boolean isSealed()
{
return false;
}
/**
* Seal this Context object so any attempt to modify any of its properties
* including calling {@link #enter()} and {@link #exit()} methods will
* throw an exception.
* <p>
* If <code>sealKey</code> is not null, calling
* {@link #unseal(Object sealKey)} with the same key unseals
* the object. If <code>sealKey</code> is null, unsealing is no longer possible.
*
* @see #isSealed()
* @see #unseal(Object)
*/
public final void seal(Object sealKey)
{
}
/**
* Unseal previously sealed Context object.
* The <code>sealKey</code> argument should not be null and should match
* <code>sealKey</code> suplied with the last call to
* {@link #seal(Object)} or an exception will be thrown.
*
* @see #isSealed()
* @see #seal(Object sealKey)
*/
public final void unseal(Object sealKey)
{
}
/**
* Get the current language version.
* <p>
* The language version number affects JavaScript semantics as detailed
* in the overview documentation.
*
* @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
*/
public final int getLanguageVersion()
{
return -1;
}
/**
* Set the language version.
*
* <p>
* Setting the language version will affect functions and scripts compiled
* subsequently. See the overview documentation for version-specific
* behavior.
*
* @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
*/
public void setLanguageVersion(int version)
{
}
public static boolean isValidLanguageVersion(int version)
{
return false;
}
public static void checkLanguageVersion(int version)
{
}
/**
* Get the implementation version.
*
* <p>
* The implementation version is of the form
* <pre>
* "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
* </pre>
* where <i>name</i> is the name of the product, <i>langVer</i> is
* the language version, <i>relNum</i> is the release number, and
* <i>date</i> is the release date for that specific
* release in the form "yyyy mm dd".
*
* @return a string that encodes the product, language version, release
* number, and date.
*/
public final String getImplementationVersion() {
return null;
}
/**
* Initialize the standard objects.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.
*
* @return the initialized scope
*/
public final ScriptableObject initStandardObjects()
{
return null;
}
/**
* Initialize the standard objects, leaving out those that offer access directly
* to Java classes. This sets up "scope" to have access to all the standard
* JavaScript classes, but does not create global objects for any top-level
* Java packages. In addition, the "Packages," "JavaAdapter," and
* "JavaImporter" classes, and the "getClass" function, are not
* initialized.
*
* The result of this function is a scope that may be safely used in a "sandbox"
* environment where it is not desirable to give access to Java code from JavaScript.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.
*
* @return the initialized scope
*/
public final ScriptableObject initSafeStandardObjects()
{
return null;
}
/**
* Initialize the standard objects.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @return the initialized scope. The method returns the value of the scope
* argument if it is not null or newly allocated scope object which
* is an instance {@link ScriptableObject}.
*/
public final Scriptable initStandardObjects(ScriptableObject scope)
{
return null;
}
/**
* Initialize the standard objects, leaving out those that offer access directly
* to Java classes. This sets up "scope" to have access to all the standard
* JavaScript classes, but does not create global objects for any top-level
* Java packages. In addition, the "Packages," "JavaAdapter," and
* "JavaImporter" classes, and the "getClass" function, are not
* initialized.
*
* The result of this function is a scope that may be safely used in a "sandbox"
* environment where it is not desirable to give access to Java code from JavaScript.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @return the initialized scope. The method returns the value of the scope
* argument if it is not null or newly allocated scope object which
* is an instance {@link ScriptableObject}.
*/
public final Scriptable initSafeStandardObjects(ScriptableObject scope)
{
return null;
}
/**
* Initialize the standard objects.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.<p>
*
* This form of the method also allows for creating "sealed" standard
* objects. An object that is sealed cannot have properties added, changed,
* or removed. This is useful to create a "superglobal" that can be shared
* among several top-level objects. Note that sealing is not allowed in
* the current ECMA/ISO language specification, but is likely for
* the next version.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @param sealed whether or not to create sealed standard objects that
* cannot be modified.
* @return the initialized scope. The method returns the value of the scope
* argument if it is not null or newly allocated scope object.
* @since 1.4R3
*/
public ScriptableObject initStandardObjects(ScriptableObject scope,
boolean sealed)
{
return null;
}
/**
* Initialize the standard objects, leaving out those that offer access directly
* to Java classes. This sets up "scope" to have access to all the standard
* JavaScript classes, but does not create global objects for any top-level
* Java packages. In addition, the "Packages," "JavaAdapter," and
* "JavaImporter" classes, and the "getClass" function, are not
* initialized.
*
* The result of this function is a scope that may be safely used in a "sandbox"
* environment where it is not desirable to give access to Java code from JavaScript.
*
* Creates instances of the standard objects and their constructors
* (Object, String, Number, Date, etc.), setting up 'scope' to act
* as a global object as in ECMA 15.1.<p>
*
* This method must be called to initialize a scope before scripts
* can be evaluated in that scope.<p>
*
* This method does not affect the Context it is called upon.<p>
*
* This form of the method also allows for creating "sealed" standard
* objects. An object that is sealed cannot have properties added, changed,
* or removed. This is useful to create a "superglobal" that can be shared
* among several top-level objects. Note that sealing is not allowed in
* the current ECMA/ISO language specification, but is likely for
* the next version.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @param sealed whether or not to create sealed standard objects that
* cannot be modified.
* @return the initialized scope. The method returns the value of the scope
* argument if it is not null or newly allocated scope object.
* @since 1.7.6
*/
public ScriptableObject initSafeStandardObjects(ScriptableObject scope,
boolean sealed)
{
return null;
}
/**
* Get the singleton object that represents the JavaScript Undefined value.
*/
public static Object getUndefinedValue()
{
return null;
}
/**
* Evaluate a JavaScript source string.
*
* The provided source name and line number are used for error messages
* and for producing debug information.
*
* @param scope the scope to execute in
* @param source the JavaScript source
* @param sourceName a string describing the source, such as a filename
* @param lineno the starting line number
* @param securityDomain an arbitrary object that specifies security
* information about the origin or owner of the script. For
* implementations that don't care about security, this value
* may be null.
* @return the result of evaluating the string
* @see org.mozilla.javascript.SecurityController
*/
public final Object evaluateString(Scriptable scope, String source,
String sourceName, int lineno,
Object securityDomain)
{
return null;
}
/**
* Evaluate a reader as JavaScript source.
*
* All characters of the reader are consumed.
*
* @param scope the scope to execute in
* @param in the Reader to get JavaScript source from
* @param sourceName a string describing the source, such as a filename
* @param lineno the starting line number
* @param securityDomain an arbitrary object that specifies security
* information about the origin or owner of the script. For
* implementations that don't care about security, this value
* may be null.
* @return the result of evaluating the source
*
* @exception IOException if an IOException was generated by the Reader
*/
public final Object evaluateReader(Scriptable scope, Reader in,
String sourceName, int lineno,
Object securityDomain)
throws IOException
{
return null;
}
/**
* Convert the value to a JavaScript boolean value.
* <p>
* See ECMA 9.2.
*
* @param value a JavaScript value
* @return the corresponding boolean value converted using
* the ECMA rules
*/
public static boolean toBoolean(Object value)
{
return false;
}
/**
* Convert the value to a JavaScript Number value.
* <p>
* Returns a Java double for the JavaScript Number.
* <p>
* See ECMA 9.3.
*
* @param value a JavaScript value
* @return the corresponding double value converted using
* the ECMA rules
*/
public static double toNumber(Object value)
{
return -1;
}
/**
* Convert the value to a JavaScript String value.
* <p>
* See ECMA 9.8.
* <p>
* @param value a JavaScript value
* @return the corresponding String value converted using
* the ECMA rules
*/
public static String toString(Object value)
{
return null;
}
/**
* Convert the value to an JavaScript object value.
* <p>
* Note that a scope must be provided to look up the constructors
* for Number, Boolean, and String.
* <p>
* See ECMA 9.9.
* <p>
* Additionally, arbitrary Java objects and classes will be
* wrapped in a Scriptable object with its Java fields and methods
* reflected as JavaScript properties of the object.
*
* @param value any Java object
* @param scope global scope containing constructors for Number,
* Boolean, and String
* @return new JavaScript object
*/
public static Scriptable toObject(Object value, Scriptable scope)
{
return null;
}
/**
* Convenient method to convert java value to its closest representation
* in JavaScript.
* <p>
* If value is an instance of String, Number, Boolean, Function or
* Scriptable, it is returned as it and will be treated as the corresponding
* JavaScript type of string, number, boolean, function and object.
* <p>
* Note that for Number instances during any arithmetic operation in
* JavaScript the engine will always use the result of
* <code>Number.doubleValue()</code> resulting in a precision loss if
* the number can not fit into double.
* <p>
* If value is an instance of Character, it will be converted to string of
* length 1 and its JavaScript type will be string.
* <p>
* The rest of values will be wrapped as LiveConnect objects
* by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
* Object obj, Class staticType)} as in:
* <pre>
* Context cx = Context.getCurrentContext();
* return cx.getWrapFactory().wrap(cx, scope, value, null);
* </pre>
*
* @param value any Java object
* @param scope top scope object
* @return value suitable to pass to any API that takes JavaScript values.
*/
public static Object javaToJS(Object value, Scriptable scope)
{
return null;
}
/**
* Convert a JavaScript value into the desired type.
* Uses the semantics defined with LiveConnect3 and throws an
* Illegal argument exception if the conversion cannot be performed.
* @param value the JavaScript value to convert
* @param desiredType the Java type to convert to. Primitive Java
* types are represented using the TYPE fields in the corresponding
* wrapper class in java.lang.
* @return the converted value
* @throws EvaluatorException if the conversion cannot be performed
*/
public static Object jsToJava(Object value, Class<?> desiredType)
{
return null;
}
/**
* Set the LiveConnect access filter for this context.
* <p> {@link ClassShutter} may only be set if it is currently null.
* Otherwise a SecurityException is thrown.
* @param shutter a ClassShutter object
* @throws SecurityException if there is already a ClassShutter
* object for this Context
*/
public synchronized final void setClassShutter(ClassShutter shutter)
{
}
final synchronized ClassShutter getClassShutter()
{
return null;
}
public interface ClassShutterSetter {
public void setClassShutter(ClassShutter shutter);
public ClassShutter getClassShutter();
}
public final synchronized ClassShutterSetter getClassShutterSetter() {
return null;
}
}

View File

@@ -0,0 +1,314 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// API class
package org.mozilla.javascript;
/**
* Factory class that Rhino runtime uses to create new {@link Context}
* instances. A <code>ContextFactory</code> can also notify listeners
* about context creation and release.
* <p>
* When the Rhino runtime needs to create new {@link Context} instance during
* execution of {@link Context#enter()} or {@link Context}, it will call
* {@link #makeContext()} of the current global ContextFactory.
* See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}.
* <p>
* It is also possible to use explicit ContextFactory instances for Context
* creation. This is useful to have a set of independent Rhino runtime
* instances under single JVM. See {@link #call(ContextAction)}.
* <p>
* The following example demonstrates Context customization to terminate
* scripts running more then 10 seconds and to provide better compatibility
* with JavaScript code using MSIE-specific features.
* <pre>
* import org.mozilla.javascript.*;
*
* class MyFactory extends ContextFactory
* {
*
* // Custom {@link Context} to store execution time.
* private static class MyContext extends Context
* {
* long startTime;
* }
*
* static {
* // Initialize GlobalFactory with custom factory
* ContextFactory.initGlobal(new MyFactory());
* }
*
* // Override {@link #makeContext()}
* protected Context makeContext()
* {
* MyContext cx = new MyContext();
* // Make Rhino runtime to call observeInstructionCount
* // each 10000 bytecode instructions
* cx.setInstructionObserverThreshold(10000);
* return cx;
* }
*
* // Override {@link #hasFeature(Context, int)}
* public boolean hasFeature(Context cx, int featureIndex)
* {
* // Turn on maximum compatibility with MSIE scripts
* switch (featureIndex) {
* case {@link Context#FEATURE_NON_ECMA_GET_YEAR}:
* return true;
*
* case {@link Context#FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME}:
* return true;
*
* case {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER}:
* return true;
*
* case {@link Context#FEATURE_PARENT_PROTO_PROPERTIES}:
* return false;
* }
* return super.hasFeature(cx, featureIndex);
* }
*
* // Override {@link #observeInstructionCount(Context, int)}
* protected void observeInstructionCount(Context cx, int instructionCount)
* {
* MyContext mcx = (MyContext)cx;
* long currentTime = System.currentTimeMillis();
* if (currentTime - mcx.startTime &gt; 10*1000) {
* // More then 10 seconds from Context creation time:
* // it is time to stop the script.
* // Throw Error instance to ensure that script will never
* // get control back through catch or finally.
* throw new Error();
* }
* }
*
* // Override {@link #doTopCall(Callable,
Context, Scriptable,
Scriptable, Object[])}
* protected Object doTopCall(Callable callable,
* Context cx, Scriptable scope,
* Scriptable thisObj, Object[] args)
* {
* MyContext mcx = (MyContext)cx;
* mcx.startTime = System.currentTimeMillis();
*
* return super.doTopCall(callable, cx, scope, thisObj, args);
* }
*
* }
* </pre>
*/
public class ContextFactory
{
/**
* Listener of {@link Context} creation and release events.
*/
public interface Listener
{
/**
* Notify about newly created {@link Context} object.
*/
public void contextCreated(Context cx);
/**
* Notify that the specified {@link Context} instance is no longer
* associated with the current thread.
*/
public void contextReleased(Context cx);
}
/**
* Get global ContextFactory.
*
* @see #hasExplicitGlobal()
* @see #initGlobal(ContextFactory)
*/
public static ContextFactory getGlobal()
{
return null;
}
/**
* Check if global factory was set.
* Return true to indicate that {@link #initGlobal(ContextFactory)} was
* already called and false to indicate that the global factory was not
* explicitly set.
*
* @see #getGlobal()
* @see #initGlobal(ContextFactory)
*/
public static boolean hasExplicitGlobal()
{
return false;
}
/**
* Set global ContextFactory.
* The method can only be called once.
*
* @see #getGlobal()
* @see #hasExplicitGlobal()
*/
public synchronized static void initGlobal(ContextFactory factory)
{
}
public interface GlobalSetter {
public void setContextFactoryGlobal(ContextFactory factory);
public ContextFactory getContextFactoryGlobal();
}
public synchronized static GlobalSetter getGlobalSetter() {
return null;
}
/**
* Create new {@link Context} instance to be associated with the current
* thread.
* This is a callback method used by Rhino to create {@link Context}
* instance when it is necessary to associate one with the current
* execution thread. <code>makeContext()</code> is allowed to call
* {@link Context#seal(Object)} on the result to prevent
* {@link Context} changes by hostile scripts or applets.
*/
protected Context makeContext()
{
return null;
}
/**
* Implementation of {@link Context#hasFeature(int featureIndex)}.
* This can be used to customize {@link Context} without introducing
* additional subclasses.
*/
protected boolean hasFeature(Context cx, int featureIndex)
{
return false;
}
/**
* Get ClassLoader to use when searching for Java classes.
* Unless it was explicitly initialized with
* {@link #initApplicationClassLoader(ClassLoader)} the method returns
* null to indicate that Thread.getContextClassLoader() should be used.
*/
public final ClassLoader getApplicationClassLoader()
{
return null;
}
/**
* Set explicit class loader to use when searching for Java classes.
*
* @see #getApplicationClassLoader()
*/
public final void initApplicationClassLoader(ClassLoader loader)
{
}
/**
* Checks if this is a sealed ContextFactory.
* @see #seal()
*/
public final boolean isSealed()
{
return false;
}
/**
* Seal this ContextFactory so any attempt to modify it like to add or
* remove its listeners will throw an exception.
* @see #isSealed()
*/
public final void seal()
{
}
/**
* Get a context associated with the current thread, creating one if need
* be. The Context stores the execution state of the JavaScript engine, so
* it is required that the context be entered before execution may begin.
* Once a thread has entered a Context, then getCurrentContext() may be
* called to find the context that is associated with the current thread.
* <p>
* Calling <code>enterContext()</code> will return either the Context
* currently associated with the thread, or will create a new context and
* associate it with the current thread. Each call to
* <code>enterContext()</code> must have a matching call to
* {@link Context#exit()}.
* <pre>
* Context cx = contextFactory.enterContext();
* try {
* ...
* cx.evaluateString(...);
* } finally {
* Context.exit();
* }
* </pre>
* Instead of using <code>enterContext()</code>, <code>exit()</code> pair consider
* using {@link #call(ContextAction)} which guarantees proper association
* of Context instances with the current thread.
* With this method the above example becomes:
* <pre>
* ContextFactory.call(new ContextAction() {
* public Object run(Context cx) {
* ...
* cx.evaluateString(...);
* return null;
* }
* });
* </pre>
* @return a Context associated with the current thread
* @see Context#getCurrentContext()
* @see Context#exit()
* @see #call(ContextAction)
*/
public Context enterContext()
{
return null;
}
/**
* @deprecated use {@link #enterContext()} instead
* @return a Context associated with the current thread
*/
@Deprecated
public final Context enter()
{
return null;
}
/**
* @deprecated Use {@link Context#exit()} instead.
*/
@Deprecated
public final void exit()
{
}
/**
* Get a Context associated with the current thread, using the given
* Context if need be.
* <p>
* The same as <code>enterContext()</code> except that <code>cx</code>
* is associated with the current thread and returned if the current thread
* has no associated context and <code>cx</code> is not associated with any
* other thread.
* @param cx a Context to associate with the thread if possible
* @return a Context associated with the current thread
* @see #enterContext()
* @see #call(ContextAction)
* @throws IllegalStateException if <code>cx</code> is already associated
* with a different thread
*/
public final Context enterContext(Context cx)
{
return null;
}
}

View File

@@ -0,0 +1,15 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
/**
* The class of exceptions thrown by the JavaScript engine.
*/
public abstract class RhinoException extends RuntimeException
{
}

View File

@@ -0,0 +1,304 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// API class
package org.mozilla.javascript;
/**
* This is interface that all objects in JavaScript must implement.
* The interface provides for the management of properties and for
* performing conversions.
* <p>
* Host system implementors may find it easier to extend the ScriptableObject
* class rather than implementing Scriptable when writing host objects.
* <p>
* There are many static methods defined in ScriptableObject that perform
* the multiple calls to the Scriptable interface needed in order to
* manipulate properties in prototype chains.
* <p>
*
* @see org.mozilla.javascript.ScriptableObject
* @author Norris Boyd
* @author Nick Thompson
* @author Brendan Eich
*/
public interface Scriptable {
/**
* Get the name of the set of objects implemented by this Java class.
* This corresponds to the [[Class]] operation in ECMA and is used
* by Object.prototype.toString() in ECMA.<p>
* See ECMA 8.6.2 and 15.2.4.2.
*/
public String getClassName();
/**
* Get a named property from the object.
*
* Looks property up in this object and returns the associated value
* if found. Returns NOT_FOUND if not found.
* Note that this method is not expected to traverse the prototype
* chain. This is different from the ECMA [[Get]] operation.
*
* Depending on the property selector, the runtime will call
* this method or the form of <code>get</code> that takes an
* integer:
* <table>
* <tr><th>JavaScript code</th><th>Java code</th></tr>
* <tr><td>a.b </td><td>a.get("b", a)</td></tr>
* <tr><td>a["foo"] </td><td>a.get("foo", a)</td></tr>
* <tr><td>a[3] </td><td>a.get(3, a)</td></tr>
* <tr><td>a["3"] </td><td>a.get(3, a)</td></tr>
* <tr><td>a[3.0] </td><td>a.get(3, a)</td></tr>
* <tr><td>a["3.0"] </td><td>a.get("3.0", a)</td></tr>
* <tr><td>a[1.1] </td><td>a.get("1.1", a)</td></tr>
* <tr><td>a[-4] </td><td>a.get(-4, a)</td></tr>
* </table>
* <p>
* The values that may be returned are limited to the following:
* <UL>
* <LI>java.lang.Boolean objects</LI>
* <LI>java.lang.String objects</LI>
* <LI>java.lang.Number objects</LI>
* <LI>org.mozilla.javascript.Scriptable objects</LI>
* <LI>null</LI>
* <LI>The value returned by Context.getUndefinedValue()</LI>
* <LI>NOT_FOUND</LI>
* </UL>
* @param name the name of the property
* @param start the object in which the lookup began
* @return the value of the property (may be null), or NOT_FOUND
* @see org.mozilla.javascript.Context#getUndefinedValue
*/
public Object get(String name, Scriptable start);
/**
* Get a property from the object selected by an integral index.
*
* Identical to <code>get(String, Scriptable)</code> except that
* an integral index is used to select the property.
*
* @param index the numeric index for the property
* @param start the object in which the lookup began
* @return the value of the property (may be null), or NOT_FOUND
* @see org.mozilla.javascript.Scriptable#get(String,Scriptable)
*/
public Object get(int index, Scriptable start);
/**
* Indicates whether or not a named property is defined in an object.
*
* Does not traverse the prototype chain.<p>
*
* The property is specified by a String name
* as defined for the <code>get</code> method.<p>
*
* @param name the name of the property
* @param start the object in which the lookup began
* @return true if and only if the named property is found in the object
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, String)
*/
public boolean has(String name, Scriptable start);
/**
* Indicates whether or not an indexed property is defined in an object.
*
* Does not traverse the prototype chain.<p>
*
* The property is specified by an integral index
* as defined for the <code>get</code> method.<p>
*
* @param index the numeric index for the property
* @param start the object in which the lookup began
* @return true if and only if the indexed property is found in the object
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, int)
*/
public boolean has(int index, Scriptable start);
/**
* Sets a named property in this object.
* <p>
* The property is specified by a string name
* as defined for <code>get</code>.
* <p>
* The possible values that may be passed in are as defined for
* <code>get</code>. A class that implements this method may choose
* to ignore calls to set certain properties, in which case those
* properties are effectively read-only.<p>
* For properties defined in a prototype chain,
* use <code>putProperty</code> in ScriptableObject. <p>
* Note that if a property <i>a</i> is defined in the prototype <i>p</i>
* of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause
* <code>set</code> to be called on the prototype <i>p</i> with
* <i>o</i> as the <i>start</i> parameter.
* To preserve JavaScript semantics, it is the Scriptable
* object's responsibility to modify <i>o</i>. <p>
* This design allows properties to be defined in prototypes and implemented
* in terms of getters and setters of Java values without consuming slots
* in each instance.
* <p>
* The values that may be set are limited to the following:
* <UL>
* <LI>java.lang.Boolean objects</LI>
* <LI>java.lang.String objects</LI>
* <LI>java.lang.Number objects</LI>
* <LI>org.mozilla.javascript.Scriptable objects</LI>
* <LI>null</LI>
* <LI>The value returned by Context.getUndefinedValue()</LI>
* </UL><p>
* Arbitrary Java objects may be wrapped in a Scriptable by first calling
* <code>Context.toObject</code>. This allows the property of a JavaScript
* object to contain an arbitrary Java object as a value.<p>
* Note that <code>has</code> will be called by the runtime first before
* <code>set</code> is called to determine in which object the
* property is defined.
* Note that this method is not expected to traverse the prototype chain,
* which is different from the ECMA [[Put]] operation.
* @param name the name of the property
* @param start the object whose property is being set
* @param value value to set the property to
* @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object)
* @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
*/
public void put(String name, Scriptable start, Object value);
/**
* Sets an indexed property in this object.
* <p>
* The property is specified by an integral index
* as defined for <code>get</code>.<p>
*
* Identical to <code>put(String, Scriptable, Object)</code> except that
* an integral index is used to select the property.
*
* @param index the numeric index for the property
* @param start the object whose property is being set
* @param value value to set the property to
* @see org.mozilla.javascript.Scriptable#has(int, Scriptable)
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, int, Object)
* @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
*/
public void put(int index, Scriptable start, Object value);
/**
* Removes a property from this object.
* This operation corresponds to the ECMA [[Delete]] except that
* the no result is returned. The runtime will guarantee that this
* method is called only if the property exists. After this method
* is called, the runtime will call Scriptable.has to see if the
* property has been removed in order to determine the boolean
* result of the delete operator as defined by ECMA 11.4.1.
* <p>
* A property can be made permanent by ignoring calls to remove
* it.<p>
* The property is specified by a String name
* as defined for <code>get</code>.
* <p>
* To delete properties defined in a prototype chain,
* see deleteProperty in ScriptableObject.
* @param name the identifier for the property
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, String)
*/
public void delete(String name);
/**
* Removes a property from this object.
*
* The property is specified by an integral index
* as defined for <code>get</code>.
* <p>
* To delete properties defined in a prototype chain,
* see deleteProperty in ScriptableObject.
*
* Identical to <code>delete(String)</code> except that
* an integral index is used to select the property.
*
* @param index the numeric index for the property
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
* @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, int)
*/
public void delete(int index);
/**
* Get the prototype of the object.
* @return the prototype
*/
public Scriptable getPrototype();
/**
* Set the prototype of the object.
* @param prototype the prototype to set
*/
public void setPrototype(Scriptable prototype);
/**
* Get the parent scope of the object.
* @return the parent scope
*/
public Scriptable getParentScope();
/**
* Set the parent scope of the object.
* @param parent the parent scope to set
*/
public void setParentScope(Scriptable parent);
/**
* Get an array of property ids.
*
* Not all property ids need be returned. Those properties
* whose ids are not returned are considered non-enumerable.
*
* @return an array of Objects. Each entry in the array is either
* a java.lang.String or a java.lang.Number
*/
public Object[] getIds();
/**
* Get the default value of the object with a given hint.
* The hints are String.class for type String, Number.class for type
* Number, Scriptable.class for type Object, and Boolean.class for
* type Boolean. <p>
*
* A <code>hint</code> of null means "no hint".
*
* See ECMA 8.6.2.6.
*
* @param hint the type hint
* @return the default value
*/
public Object getDefaultValue(Class<?> hint);
/**
* The instanceof operator.
*
* <p>
* The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to
* be called.
*
* <p>
* The return value is implementation dependent so that embedded host objects can
* return an appropriate value. See the JS 1.3 language documentation for more
* detail.
*
* <p>This operator corresponds to the proposed EMCA [[HasInstance]] operator.
*
* @param instance The value that appeared on the LHS of the instanceof
* operator
*
* @return an implementation dependent value
*/
public boolean hasInstance(Scriptable instance);
}

View File

@@ -0,0 +1,27 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// API class
package org.mozilla.javascript;
/**
* This is the default implementation of the Scriptable interface. This
* class provides convenient default behavior that makes it easier to
* define host objects.
* <p>
* Various properties and methods of JavaScript objects can be conveniently
* defined using methods of ScriptableObject.
* <p>
* Classes extending ScriptableObject must define the getClassName method.
*
* @see org.mozilla.javascript.Scriptable
* @author Norris Boyd
*/
public abstract class ScriptableObject implements Scriptable
{
}