feature: Enable JEXL expressions allowlisting in Wrangler Directives - #1040
feature: Enable JEXL expressions allowlisting in Wrangler Directives#1040riyaa14 wants to merge 1 commit into
Conversation
ff086b3 to
5f9b323
Compare
5f9b323 to
67dcbb4
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a secure JEXL execution sandbox in Wrangler to restrict allowed classes, methods, and properties, updating various directives to compile expressions with sandbox rules and adding a comprehensive performance benchmark. The review feedback identifies several critical improvement opportunities, primarily focusing on preventing potential NullPointerExceptions in EL.java and JexlInclusion.java (especially when handling null JexlInfo or deserialized null fields). Additionally, the feedback points out a logic bug where the sandbox is incorrectly enforced even when the secure JEXL feature is disabled, and advises against committing user-specific absolute paths in .vscode/settings.json.
I am having trouble creating individual review comments. Click here to see my feedback.
wrangler-core/src/main/java/io/cdap/wrangler/expression/EL.java (156-175)
The info object returned by jexlEx.getInfo() can be null in some JexlException scenarios. Accessing info.getDetail(), info.getLine(), or info.getColumn() without a null check will throw a NullPointerException. Please add a null check for info before accessing its properties, similar to how it is handled in handleExecutionException.
if (ex instanceof JexlException) {
JexlException jexlEx = (JexlException) ex;
JexlInfo info = jexlEx.getInfo();
String detail = (info == null || info.getDetail() == null) ? expression : info.getDetail().toString();
int line = info == null ? 0 : info.getLine();
int column = info == null ? 0 : info.getColumn();
String errorMessage = jexlEx.getMessage();
if (errorMessage != null && (errorMessage.contains("unsolvable function/method")
|| errorMessage.contains("unsolvable property"))) {
return new ELException(
String.format("Security violation: Access to JEXL component '%s' is not "
+ "permitted by wrangler. Hence, this JEXL expression '%s' can't be resolved.",
detail, expression),
jexlEx);
}
return new ELException(
String.format("Error encountered while compiling '%s' at line '%d' "
+ "and column '%d'. Make sure a valid jexl "
+ "transformation is provided.",
detail, line, column),
jexlEx);
}wrangler-core/src/main/java/io/cdap/wrangler/expression/EL.java (189-191)
If allowlistEnabled is false, the sandbox should not be created or enforced at all. However, the current condition !allowlistEnabled && (inclusions == null || inclusions.isEmpty()) means that if allowlistEnabled is false but inclusions is not empty, a sandbox will still be created and enforced. This violates the feature flag and can break existing JEXL expressions in pipelines where the secure JEXL feature is disabled. Please simplify the check to return null immediately if allowlistEnabled is false.
if (!allowlistEnabled) {
return null;
}
.vscode/settings.json (3-13)
Committing user-specific absolute paths (such as /usr/local/google/home/riyagarg/...) in shared configuration files is a bad practice. It will cause build or IDE configuration issues for other developers who do not have the same directory structure. Consider removing this file from the repository, adding .vscode/ to .gitignore, or using standard/relative paths.
wrangler-core/src/main/java/io/cdap/wrangler/expression/EL.java (215-217)
If inclusions contains a null element, iterating over it and calling applyInclusionRule will result in a NullPointerException when calling rule.getClassName(). Adding a null check for rule prevents this potential crash.
if (rule == null || rule.getClassName() == null || rule.getClassName().trim().isEmpty()) {
return;
}
wrangler-api/src/main/java/io/cdap/wrangler/api/JexlInclusion.java (86-96)
If JexlInclusion is deserialized from JSON and the JSON explicitly contains "methods": null or "properties": null, the fields will be set to null (bypassing constructor defaults). Returning null from these getters can cause NullPointerExceptions downstream. Returning an empty list when they are null is much safer.
public List<String> getMethods() {
return methods == null ? Collections.emptyList() : methods;
}
/**
* Gets the list of allowed properties.
*
* @return the allowed properties
*/
public List<String> getProperties() {
return properties == null ? Collections.emptyList() : properties;
}wrangler-api/src/main/java/io/cdap/wrangler/api/JexlInclusion.java (103-114)
If methods or properties is null due to deserialization, calling methods.isEmpty() or properties.isEmpty() will throw a NullPointerException. Adding null checks here ensures robust and defensive execution.
public boolean isAllMethods() {
return methods == null || methods.isEmpty() || methods.contains("*");
}
/**
* Checks if all properties are allowed.
*
* @return true if all properties are allowed
*/
public boolean isAllProperties() {
return properties == null || properties.isEmpty() || properties.contains("*");
}
554f3fc to
c3a9400
Compare
c3a9400 to
3b324a7
Compare
Changes:
jexlInclusionsfield inDirectiveConfigJexlAllowedClasses.Note: For now, feature flag is not read, it is false by default until it is introduced in CDAP.