Package org.apache.commons.jexl3.introspection

# Interface JexlPermissions

All Known Implementing Classes:
  `JexlPermissions.ClassPermissions`, `JexlPermissions.Delegate`

public interface JexlPermissions
This interface describes permissions used by JEXL introspection that constrain which packages/classes/constructors/fields/methods are made visible to JEXL scripts.

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`.

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 like `NoJexl`.
- 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 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 Classes
  Modifier and Type
  Interface
  Description
  `static final class`
  `JexlPermissions.ClassPermissions`
  A permission delegation that augments the RESTRICTED permission with an explicit set of classes.
  `static class`
  `JexlPermissions.Delegate`
  A base for permission delegation allowing functional refinement.

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final JexlPermissions`
  `RESTRICTED`
  A restricted singleton.
  `static final JexlPermissions`
  `UNRESTRICTED`
  The unrestricted permissions.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `boolean`
  `allow(Class<?> clazz)`
  Checks whether a class allows JEXL introspection.
  `default boolean`
  `allow(Class<?> clazz, Field field)`
  Checks whether a field explicitly allows JEXL introspection.
  `default boolean`
  `allow(Class<?> clazz, Method method)`
  Checks whether a method allows JEXL introspection.
  `boolean`
  `allow(Package pack)`
  Checks whether a package allows JEXL introspection.
  `boolean`
  `allow(Constructor<?> ctor)`
  Checks whether a constructor allows JEXL introspection.
  `boolean`
  `allow(Field field)`
  Checks whether a field explicitly allows JEXL introspection.
  `boolean`
  `allow(Method method)`
  Checks whether a method allows JEXL introspection.
  `JexlPermissions`
  `compose(String... src)`
  Compose these permissions with a new set.
  `static JexlPermissions`
  `parse(String... src)`
  Parses a set of permissions.
  `default boolean`
  `validate(Class<?> clazz)`
  Checks that a class is valid for permission check.
  `default boolean`
  `validate(Package pack)`
  Checks that a package is valid for permission check.
  `default boolean`
  `validate(Constructor<?> constructor)`
  Checks that a constructor is valid for permission check.
  `default boolean`
  `validate(Field field)`
  Checks that a field is valid for permission check.
  `default boolean`
  `validate(Method method)`
  Checks that a method is valid for permission check.

- ## Field Details

  - ### UNRESTRICTED

    static final JexlPermissions UNRESTRICTED
    The unrestricted permissions.

    This enables any public class, method, constructor or field to be visible to JEXL and used in scripts.

    Since:
      3.3

  - ### RESTRICTED

    static final JexlPermissions RESTRICTED
    A restricted singleton.

    The RESTRICTED set is built using the following allowed packages and denied packages/classes.

    Of particular importance are the restrictions on the `System`, `Runtime`, `ProcessBuilder`, `Class` and those on `java.net`, `java.io` and `java.lang.reflect` that 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 as `java.util.zip`/`java.util.jar` - which can read files - or `java.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 }
    - java.io { everything except PrintWriter, Writer, StringWriter, Reader, InputStream, OutputStream }
    - 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.invoke` and `org.w3c.dom.ls`.

- ## Method Details

  - ### parse

    static JexlPermissions parse(String... src)
    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 `NoJexl` annotation; they can restrict access to package, class, inner-class, methods and fields. These allow refining the volume of what is allowed by extrusion.

    An example of a tight environment that would not allow scripts to wander could be:

          # 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

  - ### allow

    boolean allow(Class<?> clazz)
    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

    boolean allow(Constructor<?> ctor)
    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

    boolean allow(Field field)
    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

    default boolean allow(Class<?> clazz, Field field)
    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 class
      `field` - the field to check
    Returns:
      true if JEXL is allowed to introspect, false otherwise
    Since:
      3.6.3

  - ### allow

    boolean allow(Method method)
    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

    default boolean allow(Class<?> clazz, Method method)
    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 class
      `method` - the method to check
    Returns:
      true if JEXL is allowed to introspect, false otherwise
    Since:
      3.6.3

  - ### allow

    boolean allow(Package pack)
    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

    JexlPermissions compose(String... src)
    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 ``RESTRICTED`.compose("com.my.app.*")` would extend the restricted set of permissions by allowing the com.my.app package.

    Parameters:
      `src` - the new constraints
    Returns:
      the new permissions

  - ### validate

    default boolean validate(Class<?> clazz)
    Checks that a class is valid for permission check.
    Parameters:
      `clazz` - the class
    Returns:
      true if the class is not null, false otherwise

  - ### validate

    default boolean validate(Constructor<?> constructor)
    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

    default boolean validate(Field field)
    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

    default boolean validate(Method method)
    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

    default boolean validate(Package pack)
    Checks that a package is valid for permission check.
    Parameters:
      `pack` - the package
    Returns:
      true if the class is not null, false otherwise