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.introspection;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.HashSet;
26  import java.util.Objects;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.stream.Collectors;
30  
31  import org.apache.commons.jexl3.internal.introspection.PermissionsParser;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  /**
36   * This interface describes permissions used by JEXL introspection that constrain which
37   * packages/classes/constructors/fields/methods are made visible to JEXL scripts.
38   * <p>By specifying or implementing permissions, it is possible to constrain precisely which objects can be manipulated
39   * by JEXL, allowing users to enter their own expressions or scripts whilst maintaining tight control
40   * over what can be executed. JEXL introspection mechanism will check whether it is permitted to
41   * access a constructor, method or field before exposition to the {@link JexlUberspect}. The restrictions
42   * are applied in all cases, for any {@link org.apache.commons.jexl3.introspection.JexlUberspect.ResolverStrategy}.
43   * </p>
44   * <p><strong>Security disclaimer.</strong> Neither {@link #RESTRICTED} nor {@link #SECURE} is exhaustive, and neither
45   * must be considered completely safe or sufficient on its own for executing untrusted user input. They are hardened
46   * baselines, not guarantees. Any application that evaluates untrusted scripts <em>must</em> define its own tailored,
47   * strict whitelist of exactly the classes, methods and fields its scripts legitimately need - ideally by composing on
48   * top of {@link #NONE} (which denies everything) via {@link #create(String...)} / {@link #compose(String...)} - and
49   * audit the result with {@link #logging()}.</p>
50   * <p>This complements using a dedicated {@link ClassLoader} and/or {@link SecurityManager} - being deprecated -
51   * and possibly {@link JexlSandbox} with a simpler mechanism. The {@link org.apache.commons.jexl3.annotations.NoJexl}
52   * annotation processing is actually performed using the result of calling {@link #parse(String...)} with no arguments;
53   * implementations shall delegate calls to its methods for {@link org.apache.commons.jexl3.annotations.NoJexl} to be
54   * processed.</p>
55   * <p>A simple textual configuration can be used to create user-defined permissions using
56   * {@link JexlPermissions#parse(String...)}. The permission syntax supports both positive (+) and negative (-)
57   * declarations:</p>
58   * <ul>
59   * <li><b>Negative restrictions ({@code -})</b>: By default or when prefixed with {@code -}, class restrictions
60   * explicitly <b>deny</b> access to the specified members (or the entire class if the block is empty).
61   * This is the default mode and works like {@link org.apache.commons.jexl3.annotations.NoJexl}.</li>
62   * <li><b>Positive restrictions ({@code +})</b>: When prefixed with {@code +}, class restrictions
63   * explicitly <b>allow only</b> the specified members (or the entire class if the block is empty), denying
64   * all others. This provides a whitelist approach where you must explicitly list what is permitted.</li>
65   * </ul>
66   * <p>For example:</p>
67   * <pre>
68   * // Deny specific methods in a class (negative restriction - default)
69   * java.lang { System { exit(); } }  // or -System { exit(); }
70   *
71   * // Allow only specific methods in a class (positive restriction)
72   * java.lang { +System { currentTimeMillis(); nanoTime(); } }
73   *
74   * // Allow entire class (positive restriction with empty block)
75   * java.io -{ +PrintWriter{} +Writer{} }
76   * </pre>
77   *
78   * <p>To build a policy from scratch, start from {@link #NONE} (or {@link #create(String...)}), which denies
79   * everything, and compose only what scripts need on top - the closed-world, deny-by-default approach. This is the
80   * opposite of {@link #UNRESTRICTED} (the empty {@link #parse(String...)}), which allows everything.</p>
81   *
82   *<p>To instantiate a JEXL engine using permissions, one should use a {@link org.apache.commons.jexl3.JexlBuilder}
83   * and call {@link org.apache.commons.jexl3.JexlBuilder#permissions(JexlPermissions)}. Another approach would
84   * be to instantiate a {@link JexlUberspect} with those permissions and call
85   * {@link org.apache.commons.jexl3.JexlBuilder#uberspect(JexlUberspect)}.</p>
86   *
87   * <p>
88   *     To help migration from earlier versions, it is possible to revert to the JEXL 3.2 default lenient behavior
89   *     by calling {@link org.apache.commons.jexl3.JexlBuilder#setDefaultPermissions(JexlPermissions)} with
90   *     {@link #UNRESTRICTED} as parameter before creating a JEXL engine instance.
91   * </p>
92   * <p>
93   *     For the same reason, using JEXL through scripting, it is possible to revert the underlying JEXL behavior to
94   *     JEXL 3.2 default by calling {@link org.apache.commons.jexl3.scripting.JexlScriptEngine#setPermissions(JexlPermissions)}
95   *     with {@link #UNRESTRICTED} as parameter.
96   * </p>
97   *
98   * @since 3.3
99   */
100 public interface JexlPermissions {
101 
102     /**
103      * A permission delegation that augments the RESTRICTED permission with an explicit
104      * set of classes.
105      * <p>A typical use case is to deny access to a package - and thus all its classes - but allow
106      * a few specific classes.</p>
107      * <p>Note that the newer positive restriction syntax is preferable as in:
108      * <code>RESTRICTED.compose("java.lang { +Class {} }")</code>.</p>
109      */
110     final class ClassPermissions extends JexlPermissions.Delegate {
111       /**
112        * The set of explicitly allowed classes, overriding the delegate permissions.
113        */
114       private final Set<String> allowedClasses;
115 
116       /**
117        * Creates permissions based on the RESTRICTED set but allowing an explicit set.
118        *
119        * @param allow the set of allowed classes
120        */
121       public ClassPermissions(final Class<?>... allow) {
122         this(JexlPermissions.RESTRICTED, allow);
123       }
124 
125       /**
126        * Creates permissions by augmenting an existing set with an explicit set of allowed classes.
127        * @param permissions the base permissions to augment
128        * @param allow the set of allowed classes
129        */
130       public ClassPermissions(final JexlPermissions permissions, final Class<?>... allow) {
131         this(permissions, Arrays.stream(Objects.requireNonNull(allow)).map(Class::getCanonicalName).collect(Collectors.toList()));
132       }
133 
134       /**
135        * Creates permissions by augmenting an existing set with an explicit set of allowed canonical class names.
136        *
137        * @param delegate the base to delegate to
138        * @param allow    the list of class canonical names
139        */
140       public ClassPermissions(final JexlPermissions delegate, final Collection<String> allow) {
141         super(Objects.requireNonNull(delegate));
142         allowedClasses = new HashSet<>(Objects.requireNonNull(allow));
143       }
144 
145       @Override
146       public boolean allow(final Constructor<?> constructor) {
147         return validate(constructor) &&
148             (allowedClasses.contains(constructor.getDeclaringClass().getCanonicalName()) || super.allow(constructor));
149       }
150 
151       @Override
152       public boolean allow(final Class<?> clazz) {
153         return validate(clazz) &&
154             (allowedClasses.contains(clazz.getCanonicalName()) || super.allow(clazz));
155       }
156 
157       @Override
158       public boolean allow(final Class<?> clazz, final Field field) {
159         if (!validate(field)) {
160           return false;
161         }
162         if (!validate(clazz)) {
163           return false;
164         }
165         if (!field.getDeclaringClass().isAssignableFrom(clazz)) {
166           return false;
167         }
168         if (super.allow(clazz, field)) {
169           return true;
170         }
171         return isClassAllowed(clazz);
172       }
173 
174       @Override
175       public boolean allow(final Class<?> clazz, final Method method) {
176         if (!validate(method)) {
177           return false;
178         }
179         if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
180           return false;
181         }
182         if (super.allow(clazz, method)) {
183           return true;
184         }
185         return isClassAllowed(clazz);
186       }
187 
188       @Override
189       public JexlPermissions compose(final String... src) {
190         return new ClassPermissions(base.compose(src), allowedClasses);
191       }
192 
193       private boolean isClassAllowed(final Class<?> aClass) {
194         Class<?> clazz = aClass;
195         // let's walk all interfaces
196         for (final Class<?> inter : clazz.getInterfaces()) {
197           if (allowedClasses.contains(inter.getCanonicalName())) {
198             return true;
199           }
200         }
201         // let's walk all super classes
202         while (clazz != null) {
203           if (allowedClasses.contains(clazz.getCanonicalName())) {
204             return true;
205           }
206           clazz = clazz.getSuperclass();
207         }
208         return false;
209       }
210     }
211 
212     /**
213      * A base for permission delegation allowing functional refinement.
214      * Overloads should call the appropriate validate() method early in their body.
215      */
216     class Delegate implements JexlPermissions {
217         /**
218          * The permissions we delegate to.
219          */
220         protected final JexlPermissions base;
221 
222         /**
223          * Constructs a new instance.
224          *
225          * @param delegate the delegate.
226          */
227         protected Delegate(final JexlPermissions delegate) {
228             base = delegate;
229         }
230 
231         @Override
232         public boolean allow(final Class<?> clazz) {
233             return base.allow(clazz);
234         }
235 
236         @Override
237         public boolean allow(final Constructor<?> ctor) {
238             return base.allow(ctor);
239         }
240 
241         @Override
242         public boolean allow(final Field field) {
243             return validate(field) && allow(field.getDeclaringClass(), field);
244         }
245 
246         @Override
247         public boolean allow(final Class<?> clazz, final Field field) {
248             return base.allow(clazz, field);
249         }
250 
251         @Override
252         public boolean allow(final Method method) {
253             return validate(method) && allow(method.getDeclaringClass(), method);
254         }
255 
256         @Override
257         public boolean allow(final Class<?> clazz, final Method method) {
258             return base.allow(clazz, method);
259         }
260 
261         @Override
262         public boolean allow(final Package pack) {
263             return base.allow(pack);
264         }
265 
266         @Override
267         public JexlPermissions compose(final String... src) {
268             return new Delegate(base.compose(src));
269         }
270     }
271 
272     /**
273      * A permission delegate that logs every allow/deny decision.
274      * <p>This is a debugging aid to determine which reflective elements (classes, constructors, methods, fields)
275      * a permission set allows or denies; wrap any permissions with {@link JexlPermissions#logging()} (or
276      * {@link JexlPermissions#logging(String)} to pick the logger name) and inspect the log to diagnose why a
277      * given object is or is not reachable from scripts.</p>
278      *
279      * @since 3.7.0
280      */
281     class LoggingPermissions extends Delegate {
282         /** The logger that decisions are written to (at info level). */
283         private final Log logger;
284         /** The set of already-emitted log lines, so each decision is logged only once. */
285         private final Set<String> logged = ConcurrentHashMap.newKeySet();
286 
287         /**
288          * Constructs an instance logging to a logger named after this class.
289          *
290          * @param delegate the permissions to delegate to
291          */
292         public LoggingPermissions(final JexlPermissions delegate) {
293             this(LogFactory.getLog(LoggingPermissions.class), delegate);
294         }
295 
296         /**
297          * Constructs an instance logging to a named logger.
298          *
299          * @param loggerName the name of the logger to use
300          * @param delegate the permissions to delegate to
301          */
302         public LoggingPermissions(final String loggerName, final JexlPermissions delegate) {
303             this(LogFactory.getLog(loggerName), delegate);
304         }
305 
306         /**
307          * Constructs an instance with an explicit logger.
308          *
309          * @param log the logger
310          * @param delegate the permissions to delegate to
311          */
312         protected LoggingPermissions(final Log log, final JexlPermissions delegate) {
313             super(delegate);
314             this.logger = log;
315         }
316 
317         /**
318          * Logs a decision once: the first time a given message is seen, it is written to the logger;
319          * subsequent identical messages are suppressed.
320          *
321          * @param allowed the decision to return
322          * @param message the message to log
323          * @return the decision
324          */
325         private boolean log(final boolean allowed, final String message) {
326             if (logged.add(message)) {
327                 logger.info(message);
328             }
329             return allowed;
330         }
331 
332         @Override
333         public boolean allow(final Class<?> clazz) {
334             final boolean allowed = super.allow(clazz);
335             return log(allowed, String.format("Class %s is %s",
336                 clazz.getCanonicalName(), allowed ? "allowed" : "denied"));
337         }
338 
339         @Override
340         public boolean allow(final Constructor<?> ctor) {
341             final boolean allowed = super.allow(ctor);
342             return log(allowed, String.format("Constructor %s.%s() is %s",
343                 ctor.getDeclaringClass().getCanonicalName(), ctor.getName(),
344                 allowed ? "allowed" : "denied"));
345         }
346 
347         @Override
348         public boolean allow(final Field field) {
349             final boolean allowed = super.allow(field);
350             return log(allowed, String.format("Field %s.%s is %s",
351                 field.getDeclaringClass().getCanonicalName(), field.getName(),
352                 allowed ? "allowed" : "denied"));
353         }
354 
355         @Override
356         public boolean allow(final Class<?> clazz, final Field field) {
357             final boolean allowed = super.allow(clazz, field);
358             return log(allowed, String.format("Field %s.%s is %s for class %s",
359                 field.getDeclaringClass().getCanonicalName(), field.getName(),
360                 allowed ? "allowed" : "denied", clazz.getCanonicalName()));
361         }
362 
363         @Override
364         public boolean allow(final Method method) {
365             final boolean allowed = super.allow(method);
366             return log(allowed, String.format("Method %s.%s() is %s",
367                 method.getDeclaringClass().getCanonicalName(), method.getName(),
368                 allowed ? "allowed" : "denied"));
369         }
370 
371         @Override
372         public boolean allow(final Class<?> clazz, final Method method) {
373             final boolean allowed = super.allow(clazz, method);
374             return log(allowed, String.format("Method %s.%s() is %s for class %s",
375                 method.getDeclaringClass().getCanonicalName(), method.getName(),
376                 allowed ? "allowed" : "denied", clazz.getCanonicalName()));
377         }
378 
379         @Override
380         public JexlPermissions compose(final String... src) {
381             return new LoggingPermissions(logger, base.compose(src));
382         }
383     }
384 
385     /**
386      * The unrestricted permissions.
387      * <p>This enables any public class, method, constructor or field to be visible to JEXL and used in scripts.</p>
388      * <p>It is <em>highly</em> discouraged to use this permissions outside of testing.</p>
389      * @since 3.3
390      */
391     JexlPermissions UNRESTRICTED = JexlPermissions.parse();
392 
393     /**
394      * A permission set that denies everything: the empty base to build permissions from scratch.
395      * <p>Unlike {@link #UNRESTRICTED} (the empty {@link #parse(String...)}, which allows everything), NONE allows
396      * nothing. Compose positive declarations on top to grant access, for example:</p>
397      * <pre>JexlPermissions.NONE.compose("java.lang { +String{} }")</pre>
398      * <p>or use the {@link #create(String...)} factory. This is the recommended starting point when you want
399      * a closed-world, deny-by-default policy listing only what your scripts actually need.</p>
400      * @since 3.7.0
401      */
402     JexlPermissions NONE = new JexlPermissions() {
403         @Override public boolean allow(final Package pack)        { return false; }
404         @Override public boolean allow(final Class<?> clazz)      { return false; }
405         @Override public boolean allow(final Constructor<?> ctor) { return false; }
406         @Override public boolean allow(final Field field)         { return false; }
407         @Override public boolean allow(final Method method)       { return false; }
408         @Override public JexlPermissions compose(final String... src) {
409             // NONE has no state to merge; composing rules builds a closed-world set from scratch
410             return src == null || src.length == 0 ? this : JexlPermissions.parse(src);
411         }
412     };
413 
414     /**
415      * A restricted singleton.
416      * <p>The RESTRICTED set is built using the following allowed packages and denied packages/classes.</p>
417      * <p>
418      * RESTRICTED attempts to strike a balance between reasonable out-of-the-box isolation and allowing most
419      * legitimate features; it is convenient when scripts need a broad slice of the JDK. In a mission-critical
420      * scenario, prefer {@link #SECURE} as a base instead and {@link #compose(String...) compose} only what your
421      * scripts actually need on top of it. Be aware that the isolation RESTRICTED provides may be incomplete and
422      * could expose more than intended; should such a case be identified, we will endeavour to resolve it in a
423      * subsequent release. Use {@link #logging()} to audit exactly which elements your workload reaches.
424      * </p>
425      * <p>RESTRICTED is not exhaustive and must not be considered sufficient on its own for executing untrusted user
426      * input. For untrusted scripts, define a tailored, strict whitelist of exactly what your scripts need - ideally
427      * composed on top of {@link #NONE} - rather than relying on RESTRICTED as-is.</p>
428      * <p>Of particular importance are the restrictions on the {@link System},
429      * {@link Runtime}, {@link ProcessBuilder}, {@link Class} and those on {@link java.net},
430      * {@link java.io} and {@link java.lang.reflect} that should provide a decent level of isolation between the scripts
431      * and its host.
432      * </p>
433      * <p>
434      * Every allowed package is declared explicitly using the positive {@code +{}} syntax rather than a
435      * {@code .*} wildcard. A wildcard matches a package <em>and all of its sub-packages</em>, which is not
436      * future-proof: a sub-package added by a later JDK (or a dangerous existing one such as
437      * {@code java.util.zip}/{@code java.util.jar} - which can read files - or {@code java.nio.file}) would be
438      * silently exposed. Listing each package explicitly keeps the perimeter closed: only the packages below are
439      * visible, nothing else.
440      * </p>
441      * <p>Allowed packages (each member is visible unless explicitly denied):</p>
442      * <ul>
443      * <li>java.math</li>
444      * <li>java.text</li>
445      * <li>java.time, java.time.chrono, java.time.format, java.time.temporal, java.time.zone</li>
446      * <li>java.util, java.util.concurrent, java.util.concurrent.atomic, java.util.function, java.util.stream, java.util.regex</li>
447      * <li>java.nio, java.nio.charset</li>
448      * <li>org.w3c.dom</li>
449      * <li>java.lang (minus the denied classes below)</li>
450      * <li>org.apache.commons.jexl3 (minus JexlBuilder)</li>
451      * </ul>
452      * <p>Denied classes / members (carved out of otherwise-allowed packages):</p>
453      * <ul>
454      * <li>java.lang { Runtime, System, ProcessBuilder, Process, RuntimePermission, SecurityManager, Thread, ThreadGroup, Class, ClassLoader }
455      * and the system-property readers Integer.getInteger, Long.getLong, Boolean.getBoolean</li>
456      * <li>java.io { everything except PrintWriter, Writer, StringWriter, Reader, InputStream, OutputStream }</li>
457      * <li>java.util: the classes stay visible but their file/loader members are carved out -
458      * Formatter and Scanner constructors (file I/O), Properties.load/store/loadFromXML/storeToXML/save (file I/O),
459      * ResourceBundle.getBundle/clearCache and PropertyResourceBundle constructors (property-file/class loading),
460      * ServiceLoader.load/loadInstalled (service/class loading). No file can be read or written and no class or
461      * service loaded through java.util.</li>
462      * <li>java.util.concurrent { Executors and the thread-pool / fork-join executor classes }</li>
463      * <li>java.time.zone { ZoneRulesProvider } (prevents JVM-wide time-zone provider registration)</li>
464      * <li>org.apache.commons.jexl3 { JexlBuilder }</li>
465      * </ul>
466      * <p>Notably absent (and therefore denied) are file/IO/persistence/loader-bearing packages such as
467      * {@code java.util.zip}, {@code java.util.jar}, {@code java.util.prefs}, {@code java.util.logging},
468      * {@code java.util.concurrent.locks}, {@code java.nio.file}, {@code java.lang.reflect},
469      * {@code java.lang.invoke} and {@code org.w3c.dom.ls}.</p>
470      * <p>A class is visible only when its <em>own</em> package or class declaration permits it; it is never made
471      * visible merely because one of its super-types is allowed. Consequently a foreign implementation of an allowed
472      * type (for instance a {@code java.util.Map} provided by another library) is not visible unless its own package
473      * is explicitly allowed, e.g. {@code RESTRICTED.compose("com.example.foreign +{}")}. Use {@link #logging()} to
474      * diagnose which elements are allowed or denied.</p>
475      */
476 
477     JexlPermissions RESTRICTED = JexlPermissions.parse(
478         "# Default Uberspect Permissions",
479         "java.math +{}",
480         "java.text +{}",
481         "java.time +{}",
482         "java.time.chrono +{}",
483         "java.time.format +{}",
484         "java.time.temporal +{}",
485         "java.time.zone +{ -ZoneRulesProvider{} }",
486         "java.util +{" +
487             " -Formatter { Formatter(); }" +
488             " -Scanner { Scanner(); }" +
489             " -Properties { load(); store(); loadFromXML(); storeToXML(); save(); }" +
490             " -ResourceBundle { getBundle(); clearCache(); }" +
491             " -PropertyResourceBundle { PropertyResourceBundle(); }" +
492             " -ServiceLoader { load(); loadInstalled(); }" +
493             " }",
494         "java.util.concurrent +{" +
495             "-Executors{} -ExecutorService{} -AbstractExecutorService{}" +
496             "-ThreadPoolExecutor{} -ScheduledThreadPoolExecutor{} -ScheduledExecutorService{}" +
497             "-ForkJoinPool{} -ForkJoinTask{} -ForkJoinWorkerThread{}" +
498             "}",
499         "java.util.concurrent.atomic +{}",
500         "java.util.function +{}",
501         "java.util.stream +{}",
502         "java.util.regex +{}",
503         "org.w3c.dom +{}",
504         "java.lang +{" +
505             "-Runtime{} -System{} -ProcessBuilder{} -Process{}" +
506             "-RuntimePermission{} -SecurityManager{}" +
507             "-Thread{} -ThreadGroup{} -Class{} -ClassLoader{}" +
508             "-Integer { getInteger(); } -Long { getLong(); } -Boolean { getBoolean(); }" +
509             "}",
510         "java.io -{ +PrintWriter{ -PrintWriter(); } +Writer{} +StringWriter{} +Reader{} +InputStream{} +OutputStream{} }",
511         "java.nio +{}",
512         "java.nio.charset +{}",
513         "org.apache.commons.jexl3 +{ -JexlBuilder{} -JexlConfigLoader{} }"
514     );
515 
516     /**
517      * An absolute-minimum, allow-list-first permission set.
518      * <p>This is the tightest sensible baseline: nothing is reachable unless explicitly whitelisted here.
519      * It exposes only the safe {@code java.lang} value types, {@code java.math} big numbers and the
520      * {@code java.util} collection types - enough for arithmetic, string and collection scripting.</p>
521      * <p>Allowed:</p>
522      * <ul>
523      * <li>{@code java.lang}: {@code Object} (minus {@code getClass}/{@code wait}/{@code notify}/{@code notifyAll}),
524      * {@code Number} and the boxed primitives, {@code String}, {@code CharSequence}, {@code StringBuilder},
525      * {@code Math}, {@code Comparable}, {@code Iterable}; everything else in {@code java.lang}
526      * (e.g. {@code System}, {@code Runtime}, {@code Thread}, {@code Class}, {@code ClassLoader}) is denied.</li>
527      * <li>{@code java.math} (for {@code BigInteger}/{@code BigDecimal}, i.e. the {@code 1B}/{@code 1H} literals).</li>
528      * <li>{@code java.util} - the collection types produced by list/map/set literals (and their iterators, views
529      * and entries), <em>minus</em> the file/loader/thread-bearing classes which are denied: {@code Formatter} and
530      * {@code Scanner} (file I/O), {@code ServiceLoader} and the {@code ResourceBundle} family (class/resource
531      * loading), {@code Properties} (file {@code load}/{@code store}) and {@code Timer}/{@code TimerTask} (threads).
532      * Because a positive package does not cover sub-packages, {@code java.util.zip}/{@code concurrent}/{@code jar}/…
533      * stay denied as well.</li>
534      * </ul>
535      * <p><strong>Guarantee:</strong> no class that SECURE allows <em>by default</em> can read or write files, read
536      * environment variables or system properties, load classes, or start threads. In particular {@code Object.getClass()}
537      * is denied (so no {@link Class} can be obtained), and the boxed-type system-property readers
538      * {@code Integer.getInteger}, {@code Long.getLong} and {@code Boolean.getBoolean} are denied.</p>
539      * <p>SECURE is nonetheless a hardened baseline, <em>not</em> a turnkey sandbox: it is not exhaustive and must not be
540      * considered sufficient on its own for executing untrusted user input. For that, define a tailored, strict whitelist
541      * of exactly what your scripts need - ideally composed on top of {@link #NONE} - rather than relying on SECURE as-is.</p>
542      * <p>Arithmetic, comparisons and string concatenation require no permission at all (they are handled by
543      * {@link org.apache.commons.jexl3.JexlArithmetic}); ranges ({@code 1..n}) iterate as a language primitive.
544      * Compose more in with {@link #compose(String...)} (e.g. {@code SECURE.compose("java.time +{}")}), and use
545      * {@link #logging()} to discover what a script is denied.</p>
546      * @since 3.7.0
547      */
548     JexlPermissions SECURE = JexlPermissions.parse(
549         "# Absolute-minimum permissions: safe java.lang value types + java.math + java.util collections",
550         "java.lang -{"
551             + " +Object{ -getClass(); -wait(); -notify(); -notifyAll(); }"
552             + " +Number{} +Boolean{ -getBoolean(); } "
553             + " +Character{} +Byte{} +Short{} +Integer{ -getInteger(); } +Long{ -getLong(); } +Float{} +Double{}"
554             + " +String{} +CharSequence{} +StringBuilder{} +Math{} +Comparable{} +Iterable{}"
555             + " }",
556         "java.math +{}",
557         "java.util +{"
558             + " -Formatter{} -Scanner{} -ServiceLoader{}"
559             + " -ResourceBundle{} -PropertyResourceBundle{} -ListResourceBundle{}"
560             + " -Properties{} -Timer{} -TimerTask{}"
561             + " }"
562     );
563 
564     /**
565      * Parses a set of permissions.
566      * <p>
567      * In JEXL 3.3, the syntax recognizes 2 types of permissions:
568      * </p>
569      * <ul>
570      * <li>Allowing access to a wildcard restricted set of packages. </li>
571      * <li>Denying access to packages, classes (and inner classes), methods and fields</li>
572      * </ul>
573      * <p>Wildcards specifications determine the set of allowed packages. When empty, all packages can be
574      * used. When using JEXL to expose functional elements, their packages should be exposed through wildcards.
575      * These allow composing the volume of what is allowed by addition.</p>
576      * <p>Restrictions behave exactly like the {@link org.apache.commons.jexl3.annotations.NoJexl} annotation;
577      * they can restrict access to package, class, inner-class, methods and fields.
578      *  These allow refining the volume of what is allowed by extrusion.</p>
579      *  An example of a tight environment that would not allow scripts to wander could be:
580      *  <pre>
581      *  # allow a very restricted set of base classes
582      *  java.math.*
583      *  java.text.*
584      *  java.util.*
585      *  # deny classes that could pose a security risk
586      *  java.lang { Runtime {} System {} ProcessBuilder {} Class {} }
587      *  org.apache.commons.jexl3 { JexlBuilder {} }
588      *  </pre>
589      *  <p><b>Syntax Overview:</b></p>
590      *  <ul>
591      *  <li>Syntax for wildcards is the name of the package suffixed by {@code .*}.</li>
592      *  <li>Syntax for restrictions is a list of package restrictions.</li>
593      *  <li>A package restriction is a package name followed by a block (as in curly-bracket block {})
594      *  that contains a list of class restrictions.</li>
595      *  <li>A class restriction is a class name prefixed by an optional {@code -} or {@code +} sign
596      *  followed by a block of member restrictions.</li>
597      *  <li>A member restriction can be a class restriction - to restrict
598      *  nested classes -, a field which is the Java field name suffixed with {@code ;}, a method composed of
599      *  its Java name suffixed with {@code ();}. Constructor restrictions are specified like methods using the
600      *  class name as method name.</li>
601      *  </ul>
602      *  <p><b>Negative ({@code -}) vs Positive ({@code +}) Restrictions:</b></p>
603      *  <ul>
604      *  <li><b>Negative restriction (default or {@code -} prefix)</b>: Explicitly <b>denies</b> access to the members
605      *  declared in its block. If the block is empty, the entire class is denied.
606      *  <br>Example: {@code java.lang { -System { exit(); } }} denies System.exit() but allows other System methods.
607      *  <br>Example: {@code java.lang { Runtime {} }} denies the entire Runtime class (empty block means deny all).</li>
608      *  <li><b>Positive restriction ({@code +} prefix)</b>: Explicitly <b>allows only</b> the members declared
609      *  in its block, denying all others not listed. If the block is empty, the entire class is allowed.
610      *  <br>Example: {@code java.lang { +System { currentTimeMillis(); } }} allows only System.currentTimeMillis(),
611      *  denying all other System methods.
612      *  <br>Example: {@code java.io -{ +PrintWriter{} +Writer{} }} in the context of a denied java.io package,
613      *  allows only PrintWriter and Writer classes entirely (empty blocks mean allow all members).</li>
614      *  </ul>
615      *  <p>
616      *  All overrides and overloads of constructors or methods are allowed or restricted at the same time,
617      *  the restriction being based on their names, not their whole signature. This differs from the @NoJexl annotation.
618      *  </p>
619      *  <p><b>Complete Example:</b></p>
620      *  <pre>
621      *  # some wildcards
622      *  java.util.* # java.util is pretty much a must-have
623      *  my.allowed.package0.*
624      *  another.allowed.package1.*
625      *  # nojexl like restrictions
626      *  my.package.internal {} # the whole package is hidden
627      *  my.package {
628      *   +class4 { theMethod(); } # POSITIVE: only theMethod can be called in class4, all others denied
629      *   class0 {
630      *     class1 {} # NEGATIVE (default): the whole class1 is hidden
631      *     class2 {
632      *         class2(); # class2 constructors cannot be invoked
633      *         class3 {
634      *             aMethod(); # aMethod cannot be called
635      *             aField; # aField cannot be accessed
636      *         }
637      *     } # end of class2
638      *     class0(); # class0 constructors cannot be invoked
639      *     method(); # method cannot be called
640      *     field; # field cannot be accessed
641      *   } # end class0
642      * } # end package my.package
643      * </pre>
644      *
645      * @param src the permissions source, the default (NoJexl aware) permissions if null
646      * @return the permissions instance
647      * @since 3.3
648      */
649     static JexlPermissions parse(final String... src) {
650         return new PermissionsParser().parse(src);
651     }
652 
653     /**
654      * Creates a permission set from scratch: everything is denied unless a rule explicitly allows it.
655      * <p>Equivalent to composing the rules onto {@link #NONE}. Use positive declarations - for instance
656      * {@code "java.lang { +String{} }"} or {@code "java.util.*"} - to grant access; {@code create()} with no
657      * rules denies everything. This differs from {@link #parse(String...)}, whose empty form
658      * ({@link #UNRESTRICTED}) allows everything.</p>
659      *
660      * @param rules the permission DSL declarations
661      * @return the closed-world permission set
662      * @since 3.7.0
663      */
664     static JexlPermissions create(final String... rules) {
665         return NONE.compose(rules);
666     }
667 
668     /**
669      * Wraps these permissions in a {@link LoggingPermissions} that logs every allow/deny decision.
670      * <p>Useful to discover which reflective elements a permission set allows or denies.</p>
671      *
672      * @return a logging view of these permissions
673      * @since 3.7.0
674      */
675     default JexlPermissions logging() {
676         return new LoggingPermissions(this);
677     }
678 
679     /**
680      * Wraps these permissions in a {@link LoggingPermissions} that logs every allow/deny decision
681      * to a named logger.
682      *
683      * @param loggerName the name of the logger to log decisions to
684      * @return a logging view of these permissions
685      * @since 3.7.0
686      */
687     default JexlPermissions logging(final String loggerName) {
688         return new LoggingPermissions(loggerName, this);
689     }
690 
691     /**
692      * Wraps these permissions in a {@link LoggingPermissions} that logs every allow/deny decision
693      * to the given logger.
694      *
695      * @param log the logger to log decisions to
696      * @return a logging view of these permissions
697      * @since 3.7.0
698      */
699     default JexlPermissions logging(final Log log) {
700         return new LoggingPermissions(log, this);
701     }
702 
703     /**
704      * Checks whether a class allows JEXL introspection.
705      * <p>If the class disallows JEXL introspection, none of its constructors, methods or fields
706      * as well as derived classes are visible to JEXL and cannot be used in scripts or expressions.
707      * If one of its super-classes is not allowed, tbe class is not allowed either.</p>
708      * <p>For interfaces, only methods and fields are disallowed in derived interfaces or implementing classes.</p>
709      *
710      * @param clazz the class to check
711      * @return true if JEXL is allowed to introspect, false otherwise
712      * @since 3.3
713      */
714     boolean allow(Class<?> clazz);
715 
716     /**
717      * Checks whether a constructor allows JEXL introspection.
718      * <p>If a constructor is not allowed, the new operator cannot be used to instantiate its declared class
719      * in scripts or expressions.</p>
720      *
721      * @param ctor the constructor to check
722      * @return true if JEXL is allowed to introspect, false otherwise
723      * @since 3.3
724      */
725     boolean allow(Constructor<?> ctor);
726 
727     /**
728      * Checks whether a field explicitly allows JEXL introspection.
729      * <p>If a field is not allowed, it cannot be resolved and accessed in scripts or expressions.</p>
730      *
731      * @param field the field to check
732      * @return true if JEXL is allowed to introspect, false otherwise
733      * @since 3.3
734      */
735     boolean allow(Field field);
736 
737     /**
738      * Checks whether a field explicitly allows JEXL introspection.
739      * <p>If a field is not allowed, it cannot be resolved and accessed in scripts or expressions.</p>
740      * @param clazz the class from which the field is accessed, used to check that the field is allowed for this class
741      * @param field the field to check
742      * @return true if JEXL is allowed to introspect, false otherwise
743      * @since 3.6.3
744    */
745     default boolean allow(Class<?> clazz, Field field) {
746       return allow(field);
747     }
748 
749     /**
750      * Checks whether a method allows JEXL introspection.
751      * <p>If a method is not allowed, it cannot be resolved and called in scripts or expressions.</p>
752      * <p>Since methods can be overridden and overloaded, this also checks that no superclass or interface
753      * explicitly disallows this method.</p>
754      *
755      * @param method the method to check
756      * @return true if JEXL is allowed to introspect, false otherwise
757      * @since 3.3
758      */
759     boolean allow(Method method);
760 
761     /**
762      * Checks whether a method allows JEXL introspection.
763      * <p>If a method is not allowed, it cannot be resolved and called in scripts or expressions.</p>
764      * <p>Since methods can be overridden and overloaded, this checks that this class explicitly allows
765      * this method - superseding any superclass or interface specified permissions.</p>
766      *
767      * @param clazz the class from which the method is accessed, used to check that the method is allowed for this class
768      * @param method the method to check
769      * @return true if JEXL is allowed to introspect, false otherwise
770      * @since 3.6.3
771      */
772     default boolean allow(Class<?> clazz, Method method) {
773       return allow(method);
774     }
775 
776     /**
777      * Checks whether a package allows JEXL introspection.
778      * <p>If the package disallows JEXL introspection, none of its classes or interfaces are visible
779      * to JEXL and cannot be used in scripts or expression.</p>
780      *
781      * @param pack the package
782      * @return true if JEXL is allowed to introspect, false otherwise
783      * @since 3.3
784      */
785     boolean allow(Package pack);
786 
787     /**
788      * Compose these permissions with a new set.
789      * <p>This is a convenience method meant to easily give access to the packages JEXL is
790      * used to integrate with. For instance, using <code>{@link #RESTRICTED}.compose("com.my.app.*")</code>
791      * would extend the restricted set of permissions by allowing the com.my.app package.</p>
792      *
793      * @param src the new constraints
794      * @return the new permissions
795      */
796     JexlPermissions compose(String... src);
797 
798     /**
799      * Checks that a class is valid for permission check.
800      *
801      * @param clazz the class
802      * @return true if the class is not null, false otherwise
803      */
804     default boolean validate(final Class<?> clazz) {
805         return clazz != null;
806     }
807 
808     /**
809      * Checks that a constructor is valid for permission check.
810      *
811      * @param constructor the constructor
812      * @return true if constructor is not null and public, false otherwise
813      */
814     default boolean validate(final Constructor<?> constructor) {
815         return constructor != null && Modifier.isPublic(constructor.getModifiers());
816     }
817 
818     /**
819      * Checks that a field is valid for permission check.
820      *
821      * @param field the constructor
822      * @return true if field is not null and public, false otherwise
823      */
824     default boolean validate(final Field field) {
825         return field != null && Modifier.isPublic(field.getModifiers());
826     }
827 
828     /**
829      * Checks that a method is valid for permission check.
830      *
831      * @param method the method
832      * @return true if method is not null and public, false otherwise
833      */
834     default boolean validate(final Method method) {
835         return method != null && Modifier.isPublic(method.getModifiers());
836     }
837 
838     /**
839      * Checks that a package is valid for permission check.
840      *
841      * @param pack the package
842      * @return true if the class is not null, false otherwise
843      */
844     default boolean validate(final Package pack) {
845         return pack != null;
846     }
847 }