Author: henrib
Date: Sun Jul 17 17:20:30 2011
New Revision: 1147683
URL: http://svn.apache.org/viewvc?rev=1147683&view=rev
Log:
JEXL-116: preamble to sandboxing
* Control over whether unknown variables, methods and constructors made
independent from JexlArithmetic leniency flag; JexlEngine thus regains a strict
flag that controls this aspect of the behavior.
* Added ReadonlyContext that, as its name stand, will not allow variables to
be set or created.
Added:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java
(with props)
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ObjectContext.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java?rev=1147683&r1=1147682&r2=1147683&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
Sun Jul 17 17:20:30 2011
@@ -106,7 +106,7 @@ public class Interpreter implements Pars
/** Strict interpreter flag. */
protected final boolean strict;
/** Silent intepreter flag. */
- protected boolean silent;
+ protected final boolean silent;
/** Cache executors. */
protected final boolean cache;
/** Registers or arguments. */
@@ -122,14 +122,26 @@ public class Interpreter implements Pars
* Creates an interpreter.
* @param jexl the engine creating this interpreter
* @param aContext the context to evaluate expression
+ * @deprecated
*/
public Interpreter(JexlEngine jexl, JexlContext aContext) {
+ this(jexl, aContext, !jexl.isLenient(), jexl.isSilent());
+ }
+
+ /**
+ * Creates an interpreter.
+ * @param jexl the engine creating this interpreter
+ * @param aContext the context to evaluate expression
+ * @param strictFlag whether this interpreter runs in strict mode
+ * @param simentFlag whether this interpreter runs in silent mode
+ */
+ public Interpreter(JexlEngine jexl, JexlContext aContext, boolean
strictFlag, boolean silentFlag) {
this.logger = jexl.logger;
this.uberspect = jexl.uberspect;
this.arithmetic = jexl.arithmetic;
this.functions = jexl.functions;
- this.strict = !this.arithmetic.isLenient();
- this.silent = jexl.silent;
+ this.strict = strictFlag;
+ this.silent = silentFlag;
this.cache = jexl.cache != null;
this.context = aContext;
this.functors = null;
@@ -152,16 +164,15 @@ public class Interpreter implements Pars
}
/**
- * Sets whether this interpreter throws JexlException during evaluation.
- * @param flag true means no JexlException will be thrown but will be
logged
- * as info through the Jexl engine logger, false allows them to be
thrown.
+ * Checks whether this interpreter considers unknown variables, methods
and constructors as errors.
+ * @return true if strict, false otherwise
*/
- public void setSilent(boolean flag) {
- this.silent = flag;
+ public boolean isStrict() {
+ return this.strict;
}
-
+
/**
- * Checks whether this interpreter throws JexlException during evaluation.
+ * Checks whether this interpreter throws JexlException when encountering
errors.
* @return true if silent, false otherwise
*/
public boolean isSilent() {
@@ -249,7 +260,7 @@ public class Interpreter implements Pars
*/
protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node,
Object left, Object right) {
if (xrt instanceof NullPointerException
- && JexlException.NULL_OPERAND == xrt.getMessage()) {
+ && (Object) JexlException.NULL_OPERAND == xrt.getMessage()) {
if (left == null) {
return node.jjtGetChild(0);
}
@@ -555,7 +566,11 @@ public class Interpreter implements Pars
variableName.append(property);
property = variableName.toString();
}
- context.set(String.valueOf(property), right);
+ try {
+ context.set(String.valueOf(property), right);
+ } catch(UnsupportedOperationException xsupport) {
+ throw new JexlException(node, "context is readonly",
xsupport);
+ }
return right;
}
}
@@ -777,7 +792,8 @@ public class Interpreter implements Pars
Object value = context.get(name);
if (value == null
&& !(node.jjtGetParent() instanceof ASTReference)
- && !context.has(name)) {
+ && !context.has(name)
+ && !isTernaryProtected(node)) {
JexlException xjexl = new JexlException(node, "undefined
variable " + name);
return unknownVariable(xjexl);
}
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java?rev=1147683&r1=1147682&r2=1147683&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
Sun Jul 17 17:20:30 2011
@@ -153,15 +153,19 @@ public class JexlEngine {
protected final Parser parser = new Parser(new StringReader(";"));
//$NON-NLS-1$
/**
* Whether expressions evaluated by this engine will throw exceptions
(false) or
- * return null (true). Default is false.
+ * return null (true) on errors. Default is false.
*/
protected boolean silent = false;
/**
+ * Whether this engine is in lenient or strict mode; if unspecified, use
the arithmetic lenient property.
+ */
+ protected Boolean strict = null;
+ /**
* Whether error messages will carry debugging information.
*/
protected boolean debug = true;
/**
- * The map of 'prefix:function' to object implementing the function.
+ * The map of 'prefix:function' to object implementing the functions.
*/
protected Map<String, Object> functions = Collections.emptyMap();
/**
@@ -273,11 +277,12 @@ public class JexlEngine {
}
/**
- * Sets whether this engine triggers errors during evaluation when null is
used as
- * an operand.
+ * Sets whether this engine considers unknown variables, methods and
constructors as errors or evaluates them
+ * as null.
* <p>This method is <em>not</em> thread safe; it should be called as an
optional step of the JexlEngine
* initialization code before expression creation & evaluation.</p>
- * <p>As of 2.0.2, you need a JexlThreadedArithmetic instance for this
call to succeed.</p>
+ * <p>As of 2.1, you need a JexlThreadedArithmetic instance for this call
to also modify the JexlArithmetic
+ * leniency behavior.</p>
* @see JexlEngine#setSilent
* @see JexlEngine#setDebug
* @param flag true means no JexlException will occur, false allows them
@@ -285,19 +290,38 @@ public class JexlEngine {
public void setLenient(boolean flag) {
if (arithmetic instanceof JexlThreadedArithmetic) {
JexlThreadedArithmetic.setLenient(Boolean.valueOf(flag));
- } else if (flag != isLenient()) {
- logger.warn("setLenient only has an effect when using a
JexlThreadedArithmetic");
+ } else {
+ strict = flag? Boolean.FALSE : Boolean.TRUE;
}
}
/**
- * Checks whether this engine triggers errors during evaluation when null
is used as
- * an operand.
+ * Checks whether this engine considers unknown variables, methods and
constructors as errors.
+ * <p>If not explicitly set, the arithmetic leniency value applies.</p>
* @return true if lenient, false if strict
*/
public boolean isLenient() {
- return this.arithmetic.isLenient();
+ return strict == null? arithmetic.isLenient() : !strict.booleanValue();
+ }
+
+ /**
+ * Sets whether this engine behaves in strict or lenient mode.
+ * Equivalent to setLenient(!flag).
+ * @param flag true for strict, false for lenient
+ */
+ public final void setStrict(boolean flag) {
+ setLenient(!flag);
}
+
+ /**
+ * Checks whether this engine behaves in strict or lenient mode.
+ * Equivalent to !isLenient().
+ * @return true for strict, false for lenient
+ */
+ public final boolean isStrict() {
+ return !isLenient();
+ }
+
/**
* Sets the class loader used to discover classes in 'new' expressions.
@@ -730,10 +754,18 @@ public class JexlEngine {
* @return an Interpreter
*/
protected Interpreter createInterpreter(JexlContext context) {
- if (context == null) {
- context = EMPTY_CONTEXT;
- }
- return new Interpreter(this, context);
+ return createInterpreter(context, isStrict(), isSilent());
+ }
+
+ /**
+ * Creates an interpreter.
+ * @param context a JexlContext; if null, the EMPTY_CONTEXT is used
instead.
+ * @param strictFlag whether the interpreter runs in strict mode
+ * @param simentFlag whether the interpreter runs in silent mode
+ * @return an Interpreter
+ */
+ protected Interpreter createInterpreter(JexlContext context, boolean
strictFlag, boolean silentFlag) {
+ return new Interpreter(this, context == null? EMPTY_CONTEXT : context,
strictFlag, silentFlag);
}
/**
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ObjectContext.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ObjectContext.java?rev=1147683&r1=1147682&r2=1147683&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ObjectContext.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ObjectContext.java
Sun Jul 17 17:20:30 2011
@@ -14,12 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.commons.jexl2;
/**
* Wraps an Object as a Jexl context.
- * @param <T> the object type to use
+ * @param <T> the wrapped object type to use
*/
public class ObjectContext<T> implements JexlContext {
private final JexlEngine jexl;
@@ -39,10 +38,12 @@ public class ObjectContext<T> implements
public Object get(String name) {
return jexl.getProperty(object, name);
}
+
/** {@inheritDoc} */
public void set(String name, Object value) {
jexl.setProperty(object, name, value);
}
+
/** {@inheritDoc} */
public boolean has(String name) {
return jexl.getUberspect().getPropertyGet(object, name, null) != null;
Added:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java?rev=1147683&view=auto
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java
(added)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java
Sun Jul 17 17:20:30 2011
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * A readonly context wrapper.
+ */
+public final class ReadonlyContext implements JexlContext {
+ /** The wrapped context. */
+ private final JexlContext base;
+
+ public ReadonlyContext(JexlContext theBase) {
+ base = theBase;
+ }
+
+ /** {@inheritDoc} */
+ public Object get(String name) {
+ return base.get(name);
+ }
+
+ /** {@inheritDoc} */
+ public void set(String name, Object value) {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ /** {@inheritDoc} */
+ public boolean has(String name) {
+ return base.has(name);
+ }
+}
Propchange:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/ReadonlyContext.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java?rev=1147683&r1=1147682&r2=1147683&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
Sun Jul 17 17:20:30 2011
@@ -76,9 +76,10 @@ public final class UnifiedJEXL {
/** The JEXL engine instance. */
private final JexlEngine jexl;
/** The expression cache. */
- private final JexlEngine.SoftCache<String,Expression> cache;
+ private final JexlEngine.SoftCache<String, Expression> cache;
/** The default cache size. */
private static final int CACHE_SIZE = 256;
+
/**
* Creates a new instance of UnifiedJEXL with a default size cache.
* @param aJexl the JexlEngine to use.
@@ -94,7 +95,7 @@ public final class UnifiedJEXL {
*/
public UnifiedJEXL(JexlEngine aJexl, int cacheSize) {
this.jexl = aJexl;
- this.cache = aJexl.new SoftCache<String,Expression>(cacheSize);
+ this.cache = aJexl.new SoftCache<String, Expression>(cacheSize);
}
/**
@@ -115,6 +116,7 @@ public final class UnifiedJEXL {
COMPOSITE(-1);
/** The index in arrays of expression counters for composite
expressions. */
private final int index;
+
/**
* Creates an ExpressionType.
* @param idx the index for this type in counters arrays.
@@ -195,7 +197,7 @@ public final class UnifiedJEXL {
* Clears the cache.
*/
public void clearCache() {
- synchronized(cache) {
+ synchronized (cache) {
cache.clear();
}
}
@@ -206,6 +208,7 @@ public final class UnifiedJEXL {
public static class Exception extends RuntimeException {
/** Serial version UID. */
private static final long serialVersionUID = -8201402995815975726L;
+
/**
* Creates a UnifiedJEXL.Exception.
* @param msg the exception message
@@ -222,6 +225,7 @@ public final class UnifiedJEXL {
public abstract class Expression {
/** The source of this expression (see {@link
UnifiedJEXL.Expression#prepare}). */
protected final Expression source;
+
/**
* Creates an expression.
* @param src the source expression if any
@@ -345,11 +349,11 @@ public final class UnifiedJEXL {
abstract Object evaluate(Interpreter interpreter);
}
-
/** A constant expression. */
private class ConstantExpression extends Expression {
/** The constant held by this expression. */
private final Object value;
+
/**
* Creates a constant expression.
* <p>
@@ -428,13 +432,13 @@ public final class UnifiedJEXL {
}
}
-
/** The base for Jexl based expressions. */
private abstract class JexlBasedExpression extends Expression {
/** The JEXL string for this expression. */
protected final CharSequence expr;
/** The JEXL node for this expression. */
protected final JexlNode node;
+
/**
* Creates a JEXL interpretable expression.
* @param theExpr the expression as a string
@@ -499,7 +503,6 @@ public final class UnifiedJEXL {
}
}
-
/** An immediate expression: ${jexl}. */
private class ImmediateExpression extends JexlBasedExpression {
/**
@@ -591,7 +594,7 @@ public final class UnifiedJEXL {
@Override
public Expression prepare(Interpreter interpreter) {
String value = interpreter.interpret(node).toString();
- JexlNode dnode = toNode(value, jexl.isDebug()? node.getInfo() :
null);
+ JexlNode dnode = toNode(value, jexl.isDebug() ? node.getInfo() :
null);
return new DeferredExpression(value, dnode, this);
}
@@ -602,13 +605,13 @@ public final class UnifiedJEXL {
}
}
-
/** A composite expression: "... ${...} ... #{...} ...". */
private class CompositeExpression extends Expression {
/** Bit encoded (deferred count > 0) bit 1, (immediate count > 0) bit
0. */
private final int meta;
/** The list of sub-expression resulting from parsing. */
private final Expression[] exprs;
+
/**
* Creates a composite expression.
* @param counters counters of expression per type
@@ -619,7 +622,7 @@ public final class UnifiedJEXL {
super(src);
this.exprs = list.toArray(new Expression[list.size()]);
this.meta = (counters[ExpressionType.DEFERRED.index] > 0 ? 2 : 0)
- | (counters[ExpressionType.IMMEDIATE.index] > 0 ? 1 : 0);
+ | (counters[ExpressionType.IMMEDIATE.index] > 0 ? 1 : 0);
}
/** {@inheritDoc} */
@@ -756,8 +759,10 @@ public final class UnifiedJEXL {
*/
Expression prepare(JexlContext context, Expression expr) {
try {
- Interpreter interpreter = jexl.createInterpreter(context);
- interpreter.setSilent(false);
+ if (context == null) {
+ context = JexlEngine.EMPTY_CONTEXT;
+ }
+ Interpreter interpreter = new Interpreter(jexl, context,
!jexl.isLenient(), false);
return expr.prepare(interpreter);
} catch (JexlException xjexl) {
Exception xuel = createException("prepare", expr, xjexl);
@@ -779,8 +784,7 @@ public final class UnifiedJEXL {
*/
Object evaluate(JexlContext context, Expression expr) {
try {
- Interpreter interpreter = jexl.createInterpreter(context);
- interpreter.setSilent(false);
+ Interpreter interpreter = jexl.createInterpreter(context,
!jexl.isLenient(), false);
return expr.evaluate(interpreter);
} catch (JexlException xjexl) {
Exception xuel = createException("evaluate", expr, xjexl);
@@ -801,7 +805,7 @@ public final class UnifiedJEXL {
private JexlNode toNode(CharSequence expression) {
return jexl.parse(expression, null);
}
-
+
/**
* Use the JEXL parser to create the AST for an expression.
* @param expression the expression to parse
@@ -837,7 +841,6 @@ public final class UnifiedJEXL {
return new Exception(strb.toString(), xany);
}
-
/** The different parsing states. */
private static enum ParseState {
/** Parsing a constant. */
Modified:
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java?rev=1147683&r1=1147682&r2=1147683&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
(original)
+++
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/VarTest.java
Sun Jul 17 17:20:30 2011
@@ -31,6 +31,37 @@ public class VarTest extends JexlTestCas
public VarTest(String testName) {
super(testName);
}
+
+ public void testStrict() throws Exception {
+ JexlContext map = new MapContext();
+ JexlContext ctxt = new ReadonlyContext(new MapContext());
+ JEXL.setStrict(true);
+ Script e;
+
+ e = JEXL.createScript("x");
+ try {
+ Object o = e.execute(ctxt);
+ fail("should have thrown an unknown var exception");
+ } catch(JexlException xjexl) {
+ // ok since we are strict and x does not exist
+ }
+ e = JEXL.createScript("x = 42");
+ try {
+ Object o = e.execute(ctxt);
+ fail("should have thrown a readonly context exception");
+ } catch(JexlException xjexl) {
+ // ok since we are strict and context is readonly
+ }
+
+ map.set("x", "fourty-two");
+ e = JEXL.createScript("x.theAnswerToEverything()");
+ try {
+ Object o = e.execute(ctxt);
+ fail("should have thrown an unknown method exception");
+ } catch(JexlException xjexl) {
+ // ok since we are strict and method does not exist
+ }
+ }
public void testLocalBasic() throws Exception {
Script e = JEXL.createScript("var x; x = 42");