diff --git a/pom.xml b/pom.xml
index 65e2e0dbf..de3452302 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,7 +77,7 @@
1.6
2.16.1
1.4
- 3.0
+ 3.1
2.6
3.5
3.6.1
diff --git a/wrangler-api/pom.xml b/wrangler-api/pom.xml
index e97464a64..847b4c1c6 100644
--- a/wrangler-api/pom.xml
+++ b/wrangler-api/pom.xml
@@ -39,6 +39,11 @@
${cdap.version}
provided
-
+
+ com.google.guava
+ guava
+ ${guava.version}
+ provided
+
diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/DefaultJexlAllowlist.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/DefaultJexlAllowlist.java
new file mode 100644
index 000000000..b47009c29
--- /dev/null
+++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/DefaultJexlAllowlist.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2026 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.cdap.wrangler.api;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Defines the set of classes allowed in the JEXL Sandbox by default.
+ */
+public final class DefaultJexlAllowlist {
+
+ private static final List DEFAULT_CLASSES = Arrays.asList(
+ // Data types
+ "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", "java.lang.Double", "java.lang.Float",
+ "java.lang.Integer", "java.lang.Long", "java.lang.Short",
+
+ // Strings
+ "java.lang.String", "java.lang.StringBuilder", "java.util.StringJoiner",
+
+ // Math
+ "java.lang.Math", "java.math.BigDecimal", "java.math.BigInteger",
+
+ // Time
+ "java.time.ZonedDateTime", "java.time.LocalDate", "java.time.LocalDateTime", "java.time.Instant",
+ "java.time.Duration", "java.time.format.DateTimeFormatter",
+
+ // Utilities
+ "java.util.Arrays", "java.util.Collections", "java.util.UUID", "java.util.Base64");
+
+ private static final List ALLOWLIST = Collections.unmodifiableList(
+ DEFAULT_CLASSES.stream()
+ .map(className -> {
+ return new JexlAllowlist(
+ className,
+ Collections.singletonList(JexlAllowlist.INCLUDE_ALL_WILDCARD),
+ Collections.singletonList(JexlAllowlist.INCLUDE_ALL_WILDCARD));
+ })
+ .collect(Collectors.toList()));
+
+ private DefaultJexlAllowlist() {
+ }
+
+ /**
+ * @return the list of default allowed classes.
+ */
+ public static List get() {
+ return ALLOWLIST;
+ }
+}
diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfig.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfig.java
index d139594cc..075724690 100644
--- a/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfig.java
+++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfig.java
@@ -16,19 +16,24 @@
package io.cdap.wrangler.api;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.annotation.Nullable;
/**
- * This class {@link DirectiveConfig} defines the configuration for the Wrangler.
+ * This class {@link DirectiveConfig} defines the configuration for the
+ * Wrangler.
* It specifies the directive exclusions -- meaning directives that should
* not be accessible to the users and as well as directive aliases.
*
@@ -42,18 +47,63 @@
* "aliases" : {
* "json-parser" : "parse-as-json",
* "js-parser" : "parse-as-json"
- * }
+ * },
+ * "jexlAllowlist" : [
+ * {
+ * "className": "java.lang.Runtime",
+ * "methods": ["*"],
+ * "properties": ["*"]
+ * }
+ * ]
* }
*/
@Deprecated
public final class DirectiveConfig {
- public static final DirectiveConfig EMPTY = new DirectiveConfig();
- // RecipeParser to be excluded or made non-accessible.
- private final Set exclusions = new HashSet<>();
+ public static final DirectiveConfig EMPTY = new DirectiveConfig(null, null, Collections.emptyList());
+ public static final String EXCLUSIONS_KEY = "exclusions";
+ public static final String ALIASES_KEY = "aliases";
+ public static final String JEXL_ALLOWLIST_KEY = "jexlAllowlist";
- // RecipeParser to be aliased.
- private final Map aliases = new HashMap<>();
+ private final ImmutableSet exclusions;
+ private final ImmutableMap aliases;
+ @Nullable private final ImmutableList jexlAllowlist;
+ public DirectiveConfig(
+ @Nullable Set exclusions,
+ @Nullable Map aliases,
+ @Nullable List jexlAllowlist) {
+ this.exclusions = exclusions != null ? ImmutableSet.copyOf(exclusions) : ImmutableSet.of();
+ this.aliases = aliases != null ? ImmutableMap.copyOf(aliases) : ImmutableMap.of();
+ this.jexlAllowlist = jexlAllowlist != null ? ImmutableList.copyOf(jexlAllowlist) : null;
+ }
+
+ /**
+ * Gets the set of excluded directives.
+ *
+ * @return the set of excluded directives
+ */
+ public Set getExclusions() {
+ return exclusions;
+ }
+
+ /**
+ * Gets the directive alias mappings.
+ *
+ * @return map of alias to directive name
+ */
+ public Map getAliases() {
+ return aliases;
+ }
+
+ /**
+ * Gets the list of JEXL inclusions.
+ *
+ * @return the list of JEXL inclusions
+ */
+ @Nullable
+ public List getJexlAllowlist() {
+ return jexlAllowlist;
+ }
/**
* Checks if a directive is aliased.
@@ -108,8 +158,9 @@ public boolean isExcluded(String directive) {
public JsonElement toJson() {
Gson gson = new Gson();
JsonObject object = new JsonObject();
- object.add("exclusions", gson.toJsonTree(exclusions));
- object.add("aliases", gson.toJsonTree(aliases));
+ object.add(EXCLUSIONS_KEY, gson.toJsonTree(exclusions));
+ object.add(ALIASES_KEY, gson.toJsonTree(aliases));
+ object.add(JEXL_ALLOWLIST_KEY, gson.toJsonTree(jexlAllowlist));
return object;
}
}
diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfigDeserializer.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfigDeserializer.java
new file mode 100644
index 000000000..814de8bb3
--- /dev/null
+++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/DirectiveConfigDeserializer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2026 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.cdap.wrangler.api;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/**
+ * Custom GSON deserializer for {@link DirectiveConfig}.
+ */
+public final class DirectiveConfigDeserializer implements JsonDeserializer {
+ private static final Type STRING_SET_TYPE = new TypeToken>() { }.getType();
+ private static final Type STRING_MAP_TYPE = new TypeToken