View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3.internal.introspection;
18  
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Objects;
22  
23  import org.apache.commons.jexl3.JexlArithmetic;
24  import org.apache.commons.jexl3.JexlOperator;
25  import org.apache.commons.jexl3.introspection.JexlMethod;
26  import org.apache.commons.jexl3.introspection.JexlPropertyGet;
27  import org.apache.commons.jexl3.introspection.JexlPropertySet;
28  import org.apache.commons.jexl3.introspection.JexlSandbox;
29  import org.apache.commons.jexl3.introspection.JexlUberspect;
30  
31  /**
32   * An uberspect that controls usage of properties, methods and constructors through a sandbox.
33   *
34   * @since 3.0
35   */
36  public final class SandboxUberspect implements JexlUberspect {
37  
38      /**
39       * Identity equality.
40       * <p>Spotbugs just <em>hates</em> string identity...</p>
41       *
42       * @param lhs left hand side
43       * @param rhs right hand side
44       * @return true if left is identical to right
45       */
46      private static boolean eq(final Object lhs, final Object rhs) {
47          return lhs == rhs;
48      }
49  
50      /** The base uberspect. */
51      private final JexlUberspect uberspect;
52  
53      /**  The sandbox. */
54      private final JexlSandbox sandbox;
55  
56      /**
57       * A constructor for JexlSandbox uberspect.
58       *
59       * @param theUberspect The JexlUberspect to sandbox
60       * @param theSandbox The sandbox which is copied to avoid changes at runtime
61       */
62      public SandboxUberspect(final JexlUberspect theUberspect, final JexlSandbox theSandbox) {
63          Objects.requireNonNull(theSandbox, "theSandbox");
64          Objects.requireNonNull(theUberspect, "theUberspect");
65          this.uberspect = theUberspect;
66          this.sandbox = theSandbox.copy();
67      }
68  
69      @Override
70      public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
71          return uberspect.getArithmetic(arithmetic);
72      }
73  
74      @Override
75      public Class<?> getClassByName(final String className) {
76          return uberspect.getClassByName(className);
77      }
78  
79      @Override
80      public ClassLoader getClassLoader() {
81          return uberspect.getClassLoader();
82      }
83  
84      @Override
85      public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
86          final String className;
87          if (ctorHandle instanceof Class<?>) {
88              className = sandbox.execute((Class<?>) ctorHandle, "");
89          } else if (ctorHandle != null) {
90              className = sandbox.execute(ctorHandle.toString(), "");
91          } else {
92              className = null;
93          }
94          return className != null && className != JexlSandbox.NULL ? uberspect.getConstructor(className, args) : null;
95      }
96  
97      @Override
98      public Iterator<?> getIterator(final Object obj) {
99          if (obj != null) {
100             // route iteration through the sandbox: consult the "iterator" method permission for the
101             // object's class so an explicitly restricted class cannot be iterated over (f006).
102             final Class<?> clazz = obj instanceof Class<?> ? (Class<?>) obj : obj.getClass();
103             final String actual = sandbox.execute(clazz, "iterator");
104             if (actual != null && actual != JexlSandbox.NULL) {
105                 return uberspect.getIterator(obj);
106             }
107         }
108         return null;
109     }
110 
111     @Override
112     public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
113         if (obj != null && method != null) {
114             final Class<?> clazz = obj instanceof Class ? (Class<?>) obj : obj.getClass();
115             final String actual = sandbox.execute(clazz, method);
116             if (actual != null && actual != JexlSandbox.NULL) {
117                 return uberspect.getMethod(obj, actual, args);
118             }
119         }
120         return null;
121     }
122 
123     @Override
124     public JexlPropertyGet getPropertyGet(final List<PropertyResolver> resolvers,
125                                           final Object obj,
126                                           final Object identifier) {
127         if (obj != null) {
128             final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
129             if (identifier != null) {
130                 final String property = identifier.toString();
131                 final String actual = sandbox.read(clazz, property);
132                 if (actual != null) {
133                     // no transformation, strict equality: use identifier before string conversion
134                     final Object pty = eq(actual, property) ? identifier : actual;
135                     return uberspect.getPropertyGet(resolvers, obj, pty);
136                 }
137             } else {
138                 final String actual = sandbox.read(clazz, null);
139                 if (actual != JexlSandbox.NULL) {
140                      return uberspect.getPropertyGet(resolvers, obj, null);
141                 }
142             }
143         }
144         return null;
145     }
146 
147     @Override
148     public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
149         return getPropertyGet(null, obj, identifier);
150     }
151 
152     @Override
153     public JexlPropertySet getPropertySet(final List<PropertyResolver> resolvers,
154                                           final Object obj,
155                                           final Object identifier,
156                                           final Object arg) {
157         if (obj != null) {
158             final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
159             if (identifier != null) {
160                 final String property = identifier.toString();
161                 final String actual = sandbox.write(clazz, property);
162                 if (actual != null) {
163                     // no transformation, strict equality: use identifier before string conversion
164                     final Object pty = eq(actual, property) ? identifier : actual;
165                     return uberspect.getPropertySet(resolvers, obj, pty, arg);
166                 }
167             } else {
168                 final String actual = sandbox.write(clazz, null);
169                 if (actual != JexlSandbox.NULL) {
170                     return uberspect.getPropertySet(resolvers, obj, null, arg);
171                 }
172             }
173         }
174         return null;
175     }
176 
177     @Override
178     public JexlPropertySet getPropertySet(final Object obj,final Object identifier,final Object arg) {
179         return getPropertySet(null, obj, identifier, arg);
180     }
181 
182     @Override
183     public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
184         return uberspect.getResolvers(op, obj);
185     }
186 
187     @Override
188     public int getVersion() {
189         return uberspect.getVersion();
190     }
191 
192     @Override
193     public void setClassLoader(final ClassLoader loader) {
194         uberspect.setClassLoader(loader);
195     }
196 }