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 }