001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.jexl3.introspection;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Field;
021import java.lang.reflect.Method;
022import java.lang.reflect.Modifier;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.HashSet;
026import java.util.Objects;
027import java.util.Set;
028import java.util.concurrent.ConcurrentHashMap;
029import java.util.stream.Collectors;
030
031import org.apache.commons.jexl3.internal.introspection.PermissionsParser;
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034
035/**
036 * This interface describes permissions used by JEXL introspection that constrain which
037 * packages/classes/constructors/fields/methods are made visible to JEXL scripts.
038 * <p>By specifying or implementing permissions, it is possible to constrain precisely which objects can be manipulated
039 * by JEXL, allowing users to enter their own expressions or scripts whilst maintaining tight control
040 * over what can be executed. JEXL introspection mechanism will check whether it is permitted to
041 * access a constructor, method or field before exposition to the {@link JexlUberspect}. The restrictions
042 * are applied in all cases, for any {@link org.apache.commons.jexl3.introspection.JexlUberspect.ResolverStrategy}.
043 * </p>
044 * <p><strong>Arithmetic exemptions.</strong> Permissions gate <em>reflective</em> access performed through the
045 * {@link JexlUberspect}. They do not gate the handful of {@link java.lang.Object} / collection methods that
046 * {@link org.apache.commons.jexl3.JexlArithmetic} invokes directly (without reflection) as operator fast-paths -
047 * namely {@code equals} and {@code compareTo} (comparison operators), {@code toString} (string coercion and
048 * concatenation), and {@code isEmpty}/{@code size}/{@code contains} (the {@code empty}, {@code size} and
049 * {@code =~} operators on collections and maps). These are language-level operations analogous to arithmetic on
050 * numbers and are always available; a permission entry denying, say, {@code equals} does not disable the
051 * {@code ==} operator. To constrain those, derive {@link org.apache.commons.jexl3.JexlArithmetic}.</p>
052 * <p><strong>Security disclaimer.</strong> Neither {@link #RESTRICTED} nor {@link #SECURE} is exhaustive, and neither
053 * must be considered completely safe or sufficient on its own for executing untrusted user input. They are hardened
054 * baselines, not guarantees. Any application that evaluates untrusted scripts <em>must</em> define its own tailored,
055 * strict whitelist of exactly the classes, methods and fields its scripts legitimately need - ideally by composing on
056 * top of {@link #NONE} (which denies everything) via {@link #create(String...)} / {@link #compose(String...)} - and
057 * audit the result with {@link #logging()}.</p>
058 * <p><strong>Compiler surface.</strong> Permissions gate reflective access; they do not gate JEXL's own compiler.
059 * {@link #RESTRICTED} denies the second-stage compiler surface reachable through reflection - {@code JexlBuilder},
060 * and the {@code createScript}/{@code createExpression}/{@code createJxltEngine} methods on
061 * {@link org.apache.commons.jexl3.JexlEngine} as well as {@code createExpression}/{@code createTemplate} on
062 * {@link org.apache.commons.jexl3.JxltEngine} - so a script that gets hold of a live engine cannot compile and run
063 * further scripts. Note, however, that a {@link org.apache.commons.jexl3.JexlScript} value passed into a script and
064 * invoked as a lambda (e.g. {@code fn(args)}) executes <em>by design</em> without a reflective call and is therefore
065 * not mediated by these permissions; only pass already-compiled scripts a caller trusts.</p>
066 * <p>This complements using a dedicated {@link ClassLoader} and/or {@link SecurityManager} - being deprecated -
067 * and possibly {@link JexlSandbox} with a simpler mechanism. The {@link org.apache.commons.jexl3.annotations.NoJexl}
068 * annotation processing is actually performed using the result of calling {@link #parse(String...)} with no arguments;
069 * implementations shall delegate calls to its methods for {@link org.apache.commons.jexl3.annotations.NoJexl} to be
070 * processed.</p>
071 * <p>A simple textual configuration can be used to create user-defined permissions using
072 * {@link JexlPermissions#parse(String...)}. The permission syntax supports both positive (+) and negative (-)
073 * declarations:</p>
074 * <ul>
075 * <li><b>Negative restrictions ({@code -})</b>: By default or when prefixed with {@code -}, class restrictions
076 * explicitly <b>deny</b> access to the specified members (or the entire class if the block is empty).
077 * This is the default mode and works like {@link org.apache.commons.jexl3.annotations.NoJexl}.</li>
078 * <li><b>Positive restrictions ({@code +})</b>: When prefixed with {@code +}, class restrictions
079 * explicitly <b>allow only</b> the specified members (or the entire class if the block is empty), denying
080 * all others. This provides a whitelist approach where you must explicitly list what is permitted.</li>
081 * </ul>
082 * <p>For example:</p>
083 * <pre>
084 * // Deny specific methods in a class (negative restriction - default)
085 * java.lang { System { exit(); } }  // or -System { exit(); }
086 *
087 * // Allow only specific methods in a class (positive restriction)
088 * java.lang { +System { currentTimeMillis(); nanoTime(); } }
089 *
090 * // Allow entire class (positive restriction with empty block)
091 * java.io -{ +PrintWriter{} +Writer{} }
092 * </pre>
093 *
094 * <p>To build a policy from scratch, start from {@link #NONE} (or {@link #create(String...)}), which denies
095 * everything, and compose only what scripts need on top - the closed-world, deny-by-default approach. This is the
096 * opposite of {@link #UNRESTRICTED} (the empty {@link #parse(String...)}), which allows everything.</p>
097 *
098 *<p>To instantiate a JEXL engine using permissions, one should use a {@link org.apache.commons.jexl3.JexlBuilder}
099 * 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 */
116public 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}