This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/storm.git
The following commit(s) were added to refs/heads/master by this push:
new c14bfaf21 Add ui.enable.jsonp to control JSONP callback wrapping in UI
and Logviewer API responses (#8977)
c14bfaf21 is described below
commit c14bfaf217b2160f9688ba48b65f777be619ff08
Author: Richard Zowalla <[email protected]>
AuthorDate: Sat Aug 22 18:12:29 2026 +0200
Add ui.enable.jsonp to control JSONP callback wrapping in UI and Logviewer
API responses (#8977)
---
conf/defaults.yaml | 1 +
docs/STORM-UI-REST-API.md | 1 +
.../main/java/org/apache/storm/DaemonConfig.java | 8 +++++++
.../java/org/apache/storm/daemon/ui/UIHelpers.java | 19 +++++++++++++++
.../org/apache/storm/daemon/ui/UIHelpersTest.java | 27 ++++++++++++++++++++++
5 files changed, 56 insertions(+)
diff --git a/conf/defaults.yaml b/conf/defaults.yaml
index a159f6bc1..d48f1aa44 100644
--- a/conf/defaults.yaml
+++ b/conf/defaults.yaml
@@ -118,6 +118,7 @@ ui.http.creds.plugin:
org.apache.storm.security.auth.DefaultHttpCredentialsPlugi
ui.pagination: 20
ui.disable.http.binding: true
ui.disable.spout.lag.monitoring: true
+ui.enable.jsonp: false
logviewer.port: 8000
logviewer.childopts: "-Xmx128m"
diff --git a/docs/STORM-UI-REST-API.md b/docs/STORM-UI-REST-API.md
index c035e76d4..4dd6c501c 100644
--- a/docs/STORM-UI-REST-API.md
+++ b/docs/STORM-UI-REST-API.md
@@ -13,6 +13,7 @@ metrics data and configuration information as well as
management operations such
The REST API returns JSON responses and supports JSONP.
Clients can pass a callback query parameter to wrap JSON in the callback
function.
+JSONP is disabled by default; the callback parameter is ignored unless
`ui.enable.jsonp` is set to true.
# Using the UI REST API
diff --git a/storm-server/src/main/java/org/apache/storm/DaemonConfig.java
b/storm-server/src/main/java/org/apache/storm/DaemonConfig.java
index 71d3c406b..b62702abc 100644
--- a/storm-server/src/main/java/org/apache/storm/DaemonConfig.java
+++ b/storm-server/src/main/java/org/apache/storm/DaemonConfig.java
@@ -402,6 +402,14 @@ public class DaemonConfig implements Validated {
@IsBoolean
public static final String UI_DISABLE_SPOUT_LAG_MONITORING =
"ui.disable.spout.lag.monitoring";
+ /**
+ * This controls whether the Storm UI and Logviewer REST APIs wrap their
response in the
+ * JSONP callback named by the "callback" query parameter. It is disabled
by default, since
+ * a JSONP response can be read by any page that is able to include it
with a script tag.
+ */
+ @IsBoolean
+ public static final String UI_ENABLE_JSONP = "ui.enable.jsonp";
+
/**
* This controls wheather Storm Logviewer should bind to http port even if
logviewer.port is > 0.
*/
diff --git
a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIHelpers.java
b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIHelpers.java
index 2e4f64f2f..3f50e9f04 100644
--- a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIHelpers.java
+++ b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIHelpers.java
@@ -18,6 +18,7 @@
package org.apache.storm.daemon.ui;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -89,6 +90,7 @@ import org.apache.storm.logging.filters.AccessLoggingFilter;
import
org.apache.storm.scheduler.resource.normalization.NormalizedResourceRequest;
import org.apache.storm.stats.StatsUtil;
import org.apache.storm.thrift.TException;
+import org.apache.storm.utils.ConfigUtils;
import org.apache.storm.utils.IVersionInfo;
import org.apache.storm.utils.ObjectReader;
import org.apache.storm.utils.Time;
@@ -447,10 +449,27 @@ public class UIHelpers {
private static final Pattern JSONP_CALLBACK_PATTERN =
Pattern.compile("^[A-Za-z_$][A-Za-z0-9_$]*(?:\\.[A-Za-z_$][A-Za-z0-9_$]*)*$");
+ /**
+ * Whether the "callback" query parameter is honored, see {@link
DaemonConfig#UI_ENABLE_JSONP}.
+ * It is read once, like the rest of the daemon configuration, so a change
needs a restart.
+ */
+ private static boolean jsonpEnabled =
+
ObjectReader.getBoolean(ConfigUtils.readStormConfig().get(DaemonConfig.UI_ENABLE_JSONP),
false);
+
+ @VisibleForTesting
+ static void setJsonpEnabled(boolean enabled) {
+ jsonpEnabled = enabled;
+ }
+
private static String sanitizeJsonpCallback(String callback) {
if (callback == null) {
return null;
}
+ if (!jsonpEnabled) {
+ LOG.warn("Ignoring JSONP callback parameter, set {} to true to
enable JSONP responses",
+ DaemonConfig.UI_ENABLE_JSONP);
+ return null;
+ }
if (callback.length() > 128 ||
!JSONP_CALLBACK_PATTERN.matcher(callback).matches()) {
LOG.warn("Ignoring invalid JSONP callback parameter");
return null;
diff --git
a/storm-webapp/src/test/java/org/apache/storm/daemon/ui/UIHelpersTest.java
b/storm-webapp/src/test/java/org/apache/storm/daemon/ui/UIHelpersTest.java
index 852af5cb9..d2fef3aa6 100644
--- a/storm-webapp/src/test/java/org/apache/storm/daemon/ui/UIHelpersTest.java
+++ b/storm-webapp/src/test/java/org/apache/storm/daemon/ui/UIHelpersTest.java
@@ -102,6 +102,8 @@ class UIHelpersTest {
void cleanup() {
// Stop simulating time
mockTime.close();
+ // Restore the default of ui.enable.jsonp
+ UIHelpers.setJsonpEnabled(false);
}
/**
@@ -603,6 +605,7 @@ class UIHelpersTest {
@Test
public void testGetJsonResponseBodyValidCallbackIsWrapped() {
+ UIHelpers.setJsonpEnabled(true);
Map<String, Object> data = new HashMap<>();
data.put("a", 1);
String body = UIHelpers.getJsonResponseBody(data, "myCb", true);
@@ -611,12 +614,14 @@ class UIHelpersTest {
@Test
public void testGetJsonResponseBodyValidDottedCallbackIsWrapped() {
+ UIHelpers.setJsonpEnabled(true);
String body = UIHelpers.getJsonResponseBody("{\"x\":1}",
"foo.bar.$baz_0", false);
assertEquals("foo.bar.$baz_0({\"x\":1});", body);
}
@Test
public void testGetJsonResponseBodyInvalidCallbackFallsBackToJson() {
+ UIHelpers.setJsonpEnabled(true);
Map<String, Object> data = new HashMap<>();
data.put("a", 1);
String body = UIHelpers.getJsonResponseBody(data,
"alert(document.cookie)//", true);
@@ -625,12 +630,14 @@ class UIHelpersTest {
@Test
public void testGetJsonResponseBodyEmptyCallbackFallsBackToJson() {
+ UIHelpers.setJsonpEnabled(true);
String body = UIHelpers.getJsonResponseBody("{\"x\":1}", "", false);
assertEquals("{\"x\":1}", body);
}
@Test
public void testGetJsonResponseBodyTooLongCallbackFallsBackToJson() {
+ UIHelpers.setJsonpEnabled(true);
StringBuilder sb = new StringBuilder("cb");
for (int i = 0; i < 200; i++) {
sb.append('x');
@@ -641,10 +648,20 @@ class UIHelpersTest {
@Test
public void testGetJsonResponseBodyRejectsCallbacksStartingWithDigit() {
+ UIHelpers.setJsonpEnabled(true);
String body = UIHelpers.getJsonResponseBody("{\"x\":1}", "1cb", false);
assertEquals("{\"x\":1}", body);
}
+ @Test
+ public void testGetJsonResponseBodyCallbackIgnoredWhenJsonpDisabled() {
+ UIHelpers.setJsonpEnabled(false);
+ Map<String, Object> data = new HashMap<>();
+ data.put("a", 1);
+ String body = UIHelpers.getJsonResponseBody(data, "myCb", true);
+ assertEquals("{\"a\":1}", body);
+ }
+
@Test
public void testGetJsonResponseHeadersNoCallbackUsesJsonContentType() {
Map headers = UIHelpers.getJsonResponseHeaders(null, null);
@@ -654,6 +671,7 @@ class UIHelpersTest {
@Test
public void
testGetJsonResponseHeadersValidCallbackUsesJavaScriptContentType() {
+ UIHelpers.setJsonpEnabled(true);
Map headers = UIHelpers.getJsonResponseHeaders("myCb", null);
assertEquals("application/javascript;charset=utf-8",
headers.get("Content-Type"));
assertEquals("nosniff", headers.get("X-Content-Type-Options"));
@@ -661,8 +679,17 @@ class UIHelpersTest {
@Test
public void
testGetJsonResponseHeadersInvalidCallbackFallsBackToJsonContentType() {
+ UIHelpers.setJsonpEnabled(true);
Map headers = UIHelpers.getJsonResponseHeaders("alert(1)//", null);
assertEquals("application/json;charset=utf-8",
headers.get("Content-Type"));
assertEquals("nosniff", headers.get("X-Content-Type-Options"));
}
+
+ @Test
+ public void testGetJsonResponseHeadersCallbackIgnoredWhenJsonpDisabled() {
+ UIHelpers.setJsonpEnabled(false);
+ Map headers = UIHelpers.getJsonResponseHeaders("myCb", null);
+ assertEquals("application/json;charset=utf-8",
headers.get("Content-Type"));
+ assertEquals("nosniff", headers.get("X-Content-Type-Options"));
+ }
}
\ No newline at end of file