Interface JexlPermissions
- All Known Implementing Classes:
JexlPermissions.ClassPermissions,JexlPermissions.Delegate,JexlPermissions.LoggingPermissions
By specifying or implementing permissions, it is possible to constrain precisely which objects can be manipulated
by JEXL, allowing users to enter their own expressions or scripts whilst maintaining tight control
over what can be executed. JEXL introspection mechanism will check whether it is permitted to
access a constructor, method or field before exposition to the JexlUberspect. The restrictions
are applied in all cases, for any JexlUberspect.ResolverStrategy.
Security disclaimer. Neither RESTRICTED nor SECURE is exhaustive, and neither
must be considered completely safe or sufficient on its own for executing untrusted user input. They are hardened
baselines, not guarantees. Any application that evaluates untrusted scripts must define its own tailored,
strict whitelist of exactly the classes, methods and fields its scripts legitimately need - ideally by composing on
top of NONE (which denies everything) via create(String...) / compose(String...) - and
audit the result with logging().
This complements using a dedicated ClassLoader and/or SecurityManager - being deprecated -
and possibly JexlSandbox with a simpler mechanism. The NoJexl
annotation processing is actually performed using the result of calling parse(String...) with no arguments;
implementations shall delegate calls to its methods for NoJexl to be
processed.
A simple textual configuration can be used to create user-defined permissions using
parse(String...). The permission syntax supports both positive (+) and negative (-)
declarations:
- Negative restrictions (
-): By default or when prefixed with-, class restrictions explicitly deny access to the specified members (or the entire class if the block is empty). This is the default mode and works likeNoJexl. - Positive restrictions (
+): When prefixed with+, class restrictions explicitly allow only the specified members (or the entire class if the block is empty), denying all others. This provides a whitelist approach where you must explicitly list what is permitted.
For example:
// Deny specific methods in a class (negative restriction - default)
java.lang { System { exit(); } } // or -System { exit(); }
// Allow only specific methods in a class (positive restriction)
java.lang { +System { currentTimeMillis(); nanoTime(); } }
// Allow entire class (positive restriction with empty block)
java.io -{ +PrintWriter{} +Writer{} }
To build a policy from scratch, start from NONE (or create(String...)), which denies
everything, and compose only what scripts need on top - the closed-world, deny-by-default approach. This is the
opposite of UNRESTRICTED (the empty parse(String...)), which allows everything.
To instantiate a JEXL engine using permissions, one should use a JexlBuilder
and call JexlBuilder.permissions(JexlPermissions). Another approach would
be to instantiate a JexlUberspect with those permissions and call
JexlBuilder.uberspect(JexlUberspect).
To help migration from earlier versions, it is possible to revert to the JEXL 3.2 default lenient behavior
by calling JexlBuilder.setDefaultPermissions(JexlPermissions) with
UNRESTRICTED as parameter before creating a JEXL engine instance.
For the same reason, using JEXL through scripting, it is possible to revert the underlying JEXL behavior to
JEXL 3.2 default by calling JexlScriptEngine.setPermissions(JexlPermissions)
with UNRESTRICTED as parameter.
- Since:
- 3.3
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic final classA permission delegation that augments the RESTRICTED permission with an explicit set of classes.static classA base for permission delegation allowing functional refinement.static classA permission delegate that logs every allow/deny decision. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final JexlPermissionsA permission set that denies everything: the empty base to build permissions from scratch.static final JexlPermissionsA restricted singleton.static final JexlPermissionsAn absolute-minimum, allow-list-first permission set.static final JexlPermissionsThe unrestricted permissions. -
Method Summary
Modifier and TypeMethodDescriptionbooleanChecks whether a class allows JEXL introspection.default booleanChecks whether a field explicitly allows JEXL introspection.default booleanChecks whether a method allows JEXL introspection.booleanChecks whether a package allows JEXL introspection.booleanallow(Constructor<?> ctor) Checks whether a constructor allows JEXL introspection.booleanChecks whether a field explicitly allows JEXL introspection.booleanChecks whether a method allows JEXL introspection.Compose these permissions with a new set.static JexlPermissionsCreates a permission set from scratch: everything is denied unless a rule explicitly allows it.default JexlPermissionslogging()Wraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision.default JexlPermissionsWraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision to a named logger.default JexlPermissionslogging(org.apache.commons.logging.Log log) Wraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision to the given logger.static JexlPermissionsParses a set of permissions.default booleanChecks that a class is valid for permission check.default booleanChecks that a package is valid for permission check.default booleanvalidate(Constructor<?> constructor) Checks that a constructor is valid for permission check.default booleanChecks that a field is valid for permission check.default booleanChecks that a method is valid for permission check.
-
Field Details
-
UNRESTRICTED
The unrestricted permissions.This enables any public class, method, constructor or field to be visible to JEXL and used in scripts.
It is highly discouraged to use this permissions outside of testing.
- Since:
- 3.3
-
NONE
A permission set that denies everything: the empty base to build permissions from scratch.Unlike
UNRESTRICTED(the emptyparse(String...), which allows everything), NONE allows nothing. Compose positive declarations on top to grant access, for example:JexlPermissions.NONE.compose("java.lang { +String{} }")or use the
create(String...)factory. This is the recommended starting point when you want a closed-world, deny-by-default policy listing only what your scripts actually need.- Since:
- 3.7.0
-
RESTRICTED
A restricted singleton.The RESTRICTED set is built using the following allowed packages and denied packages/classes.
RESTRICTED attempts to strike a balance between reasonable out-of-the-box isolation and allowing most legitimate features; it is convenient when scripts need a broad slice of the JDK. In a mission-critical scenario, prefer
SECUREas a base instead andcomposeonly what your scripts actually need on top of it. Be aware that the isolation RESTRICTED provides may be incomplete and could expose more than intended; should such a case be identified, we will endeavour to resolve it in a subsequent release. Uselogging()to audit exactly which elements your workload reaches.RESTRICTED is not exhaustive and must not be considered sufficient on its own for executing untrusted user input. For untrusted scripts, define a tailored, strict whitelist of exactly what your scripts need - ideally composed on top of
NONE- rather than relying on RESTRICTED as-is.Of particular importance are the restrictions on the
System,Runtime,ProcessBuilder,Classand those onjava.net,java.ioandjava.lang.reflectthat should provide a decent level of isolation between the scripts and its host.Every allowed package is declared explicitly using the positive
+{}syntax rather than a.*wildcard. A wildcard matches a package and all of its sub-packages, which is not future-proof: a sub-package added by a later JDK (or a dangerous existing one such asjava.util.zip/java.util.jar- which can read files - orjava.nio.file) would be silently exposed. Listing each package explicitly keeps the perimeter closed: only the packages below are visible, nothing else.Allowed packages (each member is visible unless explicitly denied):
- java.math
- java.text
- java.time, java.time.chrono, java.time.format, java.time.temporal, java.time.zone
- java.util, java.util.concurrent, java.util.concurrent.atomic, java.util.function, java.util.stream, java.util.regex
- java.nio, java.nio.charset
- org.w3c.dom
- java.lang (minus the denied classes below)
- org.apache.commons.jexl3 (minus JexlBuilder)
Denied classes / members (carved out of otherwise-allowed packages):
- java.lang { Runtime, System, ProcessBuilder, Process, RuntimePermission, SecurityManager, Thread, ThreadGroup, Class, ClassLoader } and the system-property readers Integer.getInteger, Long.getLong, Boolean.getBoolean
- java.io { everything except PrintWriter, Writer, StringWriter, Reader, InputStream, OutputStream }
- java.util: the classes stay visible but their file/loader members are carved out - Formatter and Scanner constructors (file I/O), Properties.load/store/loadFromXML/storeToXML/save (file I/O), ResourceBundle.getBundle/clearCache and PropertyResourceBundle constructors (property-file/class loading), ServiceLoader.load/loadInstalled (service/class loading). No file can be read or written and no class or service loaded through java.util.
- java.util.concurrent { Executors and the thread-pool / fork-join executor classes }
- java.time.zone { ZoneRulesProvider } (prevents JVM-wide time-zone provider registration)
- org.apache.commons.jexl3 { JexlBuilder }
Notably absent (and therefore denied) are file/IO/persistence/loader-bearing packages such as
java.util.zip,java.util.jar,java.util.prefs,java.util.logging,java.util.concurrent.locks,java.nio.file,java.lang.reflect,java.lang.invokeandorg.w3c.dom.ls.A class is visible only when its own package or class declaration permits it; it is never made visible merely because one of its super-types is allowed. Consequently a foreign implementation of an allowed type (for instance a
java.util.Mapprovided by another library) is not visible unless its own package is explicitly allowed, e.g.RESTRICTED.compose("com.example.foreign +{}"). Uselogging()to diagnose which elements are allowed or denied. -
SECURE
An absolute-minimum, allow-list-first permission set.This is the tightest sensible baseline: nothing is reachable unless explicitly whitelisted here. It exposes only the safe
java.langvalue types,java.mathbig numbers and thejava.utilcollection types - enough for arithmetic, string and collection scripting.Allowed:
java.lang:Object(minusgetClass/wait/notify/notifyAll),Numberand the boxed primitives,String,CharSequence,StringBuilder,Math,Comparable,Iterable; everything else injava.lang(e.g.System,Runtime,Thread,Class,ClassLoader) is denied.java.math(forBigInteger/BigDecimal, i.e. the1B/1Hliterals).java.util- the collection types produced by list/map/set literals (and their iterators, views and entries), minus the file/loader/thread-bearing classes which are denied:FormatterandScanner(file I/O),ServiceLoaderand theResourceBundlefamily (class/resource loading),Properties(fileload/store) andTimer/TimerTask(threads). Because a positive package does not cover sub-packages,java.util.zip/concurrent/jar/… stay denied as well.
Guarantee: no class that SECURE allows by default can read or write files, read environment variables or system properties, load classes, or start threads. In particular
Object.getClass()is denied (so noClasscan be obtained), and the boxed-type system-property readersInteger.getInteger,Long.getLongandBoolean.getBooleanare denied.SECURE is nonetheless a hardened baseline, not a turnkey sandbox: it is not exhaustive and must not be considered sufficient on its own for executing untrusted user input. For that, define a tailored, strict whitelist of exactly what your scripts need - ideally composed on top of
NONE- rather than relying on SECURE as-is.Arithmetic, comparisons and string concatenation require no permission at all (they are handled by
JexlArithmetic); ranges (1..n) iterate as a language primitive. Compose more in withcompose(String...)(e.g.SECURE.compose("java.time +{}")), and uselogging()to discover what a script is denied.- Since:
- 3.7.0
-
-
Method Details
-
parse
Parses a set of permissions.In JEXL 3.3, the syntax recognizes 2 types of permissions:
- Allowing access to a wildcard restricted set of packages.
- Denying access to packages, classes (and inner classes), methods and fields
Wildcards specifications determine the set of allowed packages. When empty, all packages can be used. When using JEXL to expose functional elements, their packages should be exposed through wildcards. These allow composing the volume of what is allowed by addition.
Restrictions behave exactly like the
An example of a tight environment that would not allow scripts to wander could be:NoJexlannotation; they can restrict access to package, class, inner-class, methods and fields. These allow refining the volume of what is allowed by extrusion.# allow a very restricted set of base classes java.math.* java.text.* java.util.* # deny classes that could pose a security risk java.lang { Runtime {} System {} ProcessBuilder {} Class {} } org.apache.commons.jexl3 { JexlBuilder {} }Syntax Overview:
- Syntax for wildcards is the name of the package suffixed by
.*. - Syntax for restrictions is a list of package restrictions.
- A package restriction is a package name followed by a block (as in curly-bracket block {}) that contains a list of class restrictions.
- A class restriction is a class name prefixed by an optional
-or+sign followed by a block of member restrictions. - A member restriction can be a class restriction - to restrict
nested classes -, a field which is the Java field name suffixed with
;, a method composed of its Java name suffixed with();. Constructor restrictions are specified like methods using the class name as method name.
Negative (
-) vs Positive (+) Restrictions:- Negative restriction (default or
-prefix): Explicitly denies access to the members declared in its block. If the block is empty, the entire class is denied.
Example:java.lang { -System { exit(); } }denies System.exit() but allows other System methods.
Example:java.lang { Runtime {} }denies the entire Runtime class (empty block means deny all). - Positive restriction (
+prefix): Explicitly allows only the members declared in its block, denying all others not listed. If the block is empty, the entire class is allowed.
Example:java.lang { +System { currentTimeMillis(); } }allows only System.currentTimeMillis(), denying all other System methods.
Example:java.io -{ +PrintWriter{} +Writer{} }in the context of a denied java.io package, allows only PrintWriter and Writer classes entirely (empty blocks mean allow all members).
All overrides and overloads of constructors or methods are allowed or restricted at the same time, the restriction being based on their names, not their whole signature. This differs from the @NoJexl annotation.
Complete Example:
# some wildcards java.util.* # java.util is pretty much a must-have my.allowed.package0.* another.allowed.package1.* # nojexl like restrictions my.package.internal {} # the whole package is hidden my.package { +class4 { theMethod(); } # POSITIVE: only theMethod can be called in class4, all others denied class0 { class1 {} # NEGATIVE (default): the whole class1 is hidden class2 { class2(); # class2 constructors cannot be invoked class3 { aMethod(); # aMethod cannot be called aField; # aField cannot be accessed } } # end of class2 class0(); # class0 constructors cannot be invoked method(); # method cannot be called field; # field cannot be accessed } # end class0 } # end package my.package- Parameters:
src- the permissions source, the default (NoJexl aware) permissions if null- Returns:
- the permissions instance
- Since:
- 3.3
-
create
Creates a permission set from scratch: everything is denied unless a rule explicitly allows it.Equivalent to composing the rules onto
NONE. Use positive declarations - for instance"java.lang { +String{} }"or"java.util.*"- to grant access;create()with no rules denies everything. This differs fromparse(String...), whose empty form (UNRESTRICTED) allows everything.- Parameters:
rules- the permission DSL declarations- Returns:
- the closed-world permission set
- Since:
- 3.7.0
-
logging
Wraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision.Useful to discover which reflective elements a permission set allows or denies.
- Returns:
- a logging view of these permissions
- Since:
- 3.7.0
-
logging
Wraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision to a named logger.- Parameters:
loggerName- the name of the logger to log decisions to- Returns:
- a logging view of these permissions
- Since:
- 3.7.0
-
logging
Wraps these permissions in aJexlPermissions.LoggingPermissionsthat logs every allow/deny decision to the given logger.- Parameters:
log- the logger to log decisions to- Returns:
- a logging view of these permissions
- Since:
- 3.7.0
-
allow
Checks whether a class allows JEXL introspection.If the class disallows JEXL introspection, none of its constructors, methods or fields as well as derived classes are visible to JEXL and cannot be used in scripts or expressions. If one of its super-classes is not allowed, tbe class is not allowed either.
For interfaces, only methods and fields are disallowed in derived interfaces or implementing classes.
- Parameters:
clazz- the class to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.3
-
allow
Checks whether a constructor allows JEXL introspection.If a constructor is not allowed, the new operator cannot be used to instantiate its declared class in scripts or expressions.
- Parameters:
ctor- the constructor to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.3
-
allow
Checks whether a field explicitly allows JEXL introspection.If a field is not allowed, it cannot be resolved and accessed in scripts or expressions.
- Parameters:
field- the field to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.3
-
allow
Checks whether a field explicitly allows JEXL introspection.If a field is not allowed, it cannot be resolved and accessed in scripts or expressions.
- Parameters:
clazz- the class from which the field is accessed, used to check that the field is allowed for this classfield- the field to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.6.3
-
allow
Checks whether a method allows JEXL introspection.If a method is not allowed, it cannot be resolved and called in scripts or expressions.
Since methods can be overridden and overloaded, this also checks that no superclass or interface explicitly disallows this method.
- Parameters:
method- the method to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.3
-
allow
Checks whether a method allows JEXL introspection.If a method is not allowed, it cannot be resolved and called in scripts or expressions.
Since methods can be overridden and overloaded, this checks that this class explicitly allows this method - superseding any superclass or interface specified permissions.
- Parameters:
clazz- the class from which the method is accessed, used to check that the method is allowed for this classmethod- the method to check- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.6.3
-
allow
Checks whether a package allows JEXL introspection.If the package disallows JEXL introspection, none of its classes or interfaces are visible to JEXL and cannot be used in scripts or expression.
- Parameters:
pack- the package- Returns:
- true if JEXL is allowed to introspect, false otherwise
- Since:
- 3.3
-
compose
Compose these permissions with a new set.This is a convenience method meant to easily give access to the packages JEXL is used to integrate with. For instance, using
would extend the restricted set of permissions by allowing the com.my.app package.RESTRICTED.compose("com.my.app.*")- Parameters:
src- the new constraints- Returns:
- the new permissions
-
validate
Checks that a class is valid for permission check.- Parameters:
clazz- the class- Returns:
- true if the class is not null, false otherwise
-
validate
Checks that a constructor is valid for permission check.- Parameters:
constructor- the constructor- Returns:
- true if constructor is not null and public, false otherwise
-
validate
Checks that a field is valid for permission check.- Parameters:
field- the constructor- Returns:
- true if field is not null and public, false otherwise
-
validate
Checks that a method is valid for permission check.- Parameters:
method- the method- Returns:
- true if method is not null and public, false otherwise
-
validate
Checks that a package is valid for permission check.- Parameters:
pack- the package- Returns:
- true if the class is not null, false otherwise
-