Repository navigation
[CDAP-21270] Allow disabling JEXL allowlist feature through directive config API - #1049
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the JEXL allowlist configuration by introducing a new JexlConfiguration class that encapsulates both the JEXL allowlist and an enabled flag, updating DirectiveConfig and its deserializers accordingly. The review feedback highlights three potential NullPointerException risks: a missing null check on config.getJexlConfiguration() in ConfigDirectiveContext, a potential null return from configStore.getConfig() in DirectivesHandler, and the use of contains(null) on a list in JexlConfiguration which can throw an exception depending on the list implementation.
| public boolean isJexlAllowlistEnabled() { | ||
| return jexlAllowlistEnabled; | ||
| return isJexlAllowlistFeatureFlagEnabled | ||
| && config.getJexlConfiguration().isJexlAllowlistEnabledOrDefault(true); | ||
| } |
There was a problem hiding this comment.
If config.getJexlConfiguration() is null (which can happen if the configuration is empty or has not been fully initialized/migrated), calling isJexlAllowlistEnabledOrDefault will throw a NullPointerException. We should add a null check to safely default to true when the JEXL configuration is missing.
| public boolean isJexlAllowlistEnabled() { | |
| return jexlAllowlistEnabled; | |
| return isJexlAllowlistFeatureFlagEnabled | |
| && config.getJexlConfiguration().isJexlAllowlistEnabledOrDefault(true); | |
| } | |
| public boolean isJexlAllowlistEnabled() { | |
| return isJexlAllowlistFeatureFlagEnabled | |
| && (config.getJexlConfiguration() == null | |
| || config.getJexlConfiguration().isJexlAllowlistEnabledOrDefault(true)); | |
| } |
There was a problem hiding this comment.
If the feature flag (isJexlAllowlistFeatureFlagEnabled) is true, getJexlConfiguration() won't be null. This is already handled in DirectivesHandler, we don't allow a null jexlConfiguration - Reference
| DirectiveConfig config = configStore.getConfig(); | ||
| if (!jexlAllowlistEnabled) { | ||
| JsonObject configJson = config.toJson().getAsJsonObject(); | ||
| configJson.remove(DirectiveConfig.JEXL_ALLOWLIST_KEY); | ||
| configJson.remove(DirectiveConfig.JEXL_CONFIGURATION_KEY); | ||
| return new ServiceResponse<>(configJson); | ||
| } | ||
| enforceDirectiveConfigPermission(StandardPermission.GET); | ||
| return new ServiceResponse<>(config); |
There was a problem hiding this comment.
If configStore.getConfig() returns null (e.g., in uninitialized or test environments), calling config.toJson() will throw a NullPointerException. It is safer to default to DirectiveConfig.EMPTY when the config is null.
DirectiveConfig config = configStore.getConfig();
if (config == null) {
config = DirectiveConfig.EMPTY;
}
if (!jexlAllowlistEnabled) {
JsonObject configJson = config.toJson().getAsJsonObject();
configJson.remove(DirectiveConfig.JEXL_CONFIGURATION_KEY);
return new ServiceResponse<>(configJson);
}
return new ServiceResponse<>(config);There was a problem hiding this comment.
This case is not possible, the directive config is never null in database because on dataprep service initialization, we initialize the database with default directive config. Reference - Wrangler PR#1040
| if (jexlAllowlist != null && jexlAllowlist.contains(null)) { | ||
| throw new IllegalArgumentException("JEXL allowlist cannot contain null entries."); | ||
| } |
There was a problem hiding this comment.
Using jexlAllowlist.contains(null) can throw a NullPointerException depending on the underlying List implementation (for example, Guava's ImmutableList or Java 9+ List.of() throw NPE on contains(null)). It is safer to iterate through the list to check for null elements.
if (jexlAllowlist != null) {
for (JexlAllowlist entry : jexlAllowlist) {
if (entry == null) {
throw new IllegalArgumentException("JEXL allowlist cannot contain null entries.");
}
}
}There was a problem hiding this comment.
This is not needed at all, removed.
| private final ImmutableList<JexlAllowlist> jexlAllowlist; | ||
|
|
||
| public JexlConfiguration( | ||
| @Nullable Boolean jexlAllowlistEnabled, |
There was a problem hiding this comment.
Use Primitive data type boolean. No need to annotate with nullable.
| @Nullable | ||
| private final Boolean jexlAllowlistEnabled; | ||
| @Nullable | ||
| private final ImmutableList<JexlAllowlist> jexlAllowlist; |
There was a problem hiding this comment.
nit: use List here. Underlying implementation could be ImmutableList.
| public boolean isJexlAllowlistEnabledOrDefault(boolean defaultValue) { | ||
| return jexlAllowlistEnabled != null ? jexlAllowlistEnabled : defaultValue; | ||
| } |
| return new JexlConfiguration( | ||
| deserializeProperty(jexlConfigJsonObj, JexlConfiguration.JEXL_ALLOWLIST_ENABLED_KEY, Boolean.class, ctx), | ||
| deserializeProperty(jexlConfigJsonObj, JexlConfiguration.JEXL_ALLOWLIST_KEY, JEXL_ALLOWLIST_LIST_TYPE, ctx)); | ||
| } catch (IllegalArgumentException e) { |
There was a problem hiding this comment.
This is not the correct exception to throw in this case.
Let JsonParseException be propaged.
| public boolean isJexlAllowlistEnabled() { | ||
| return jexlAllowlistEnabled; | ||
| return isJexlAllowlistFeatureFlagEnabled | ||
| && config.getJexlConfiguration().isJexlAllowlistEnabledOrDefault(true); | ||
| } |
| } | ||
|
|
||
| @Nullable | ||
| private JexlConfiguration resolveJexlConfiguration(@Nullable JexlConfiguration userConfig) |
There was a problem hiding this comment.
nit: change userConfig tojexlConfif
| public JexlConfiguration( | ||
| @Nullable Boolean jexlAllowlistEnabled, | ||
| @Nullable List<JexlAllowlist> jexlAllowlist) { | ||
| if (jexlAllowlist != null && jexlAllowlist.contains(null)) { |
There was a problem hiding this comment.
jexlAllowlist.contains(null) is not needed as this will be handled in the deserializer.
da39149 to
baefbf8
Compare
baefbf8 to
6c8fb85
Compare
Description
This PR introduces
JexlConfigurationwithinDirectiveConfigto support enabling or disabling the JEXL allowlist feature dynamically through the Wrangler Directive Configuration API.Changes
JexlConfigurationandJexlConfigurationDeserializer:jexlAllowlistEnabled(boolean flag) andjexlAllowlist(list ofJexlAllowlistrules) under the jexlConfiguration field in DirectiveConfig.isJexlAllowlistEnabledOrDefault(boolean)to fall back to the system-level default when the flag is omitted.DirectiveConfigand Context Resolution:jexlAllowlistfield in DirectiveConfig withjexlConfiguration.jexlConfiguration.isJexlAllowlistEnabledOrDefault(...).DirectivesHandlerAPI Validation & Storage:DirectivesHandlerto allow disabling JEXL allowlisting via the config API while preserving existing allowlist rules if omitted when disabled.ConfigStoreand callers (Wrangler, DataPrepServiceClient, RequestExtractor, RemoteExecutionTask) to serialize/deserialize JexlConfiguration.