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}