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