This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 3f3c4604 fix(settings): harden Metrics data source validation (#1474)
3f3c4604 is described below
commit 3f3c4604bff3df8cf7b797e94bd4f8af67cce31e
Author: aias00 <[email protected]>
AuthorDate: Tue Aug 11 15:32:32 2026 +0800
fix(settings): harden Metrics data source validation (#1474)
* fix(settings): validate metrics data source types
Signed-off-by: liuhy <[email protected]>
* fix(settings): validate metrics data source authentication
Signed-off-by: liuhy <[email protected]>
* test: preserve missing data source name validation
---------
Signed-off-by: liuhy <[email protected]>
---
.../rocketmq/studio/settings/DataSourceDTO.java | 11 ++-
.../studio/settings/SettingsControllerTest.java | 88 ++++++++++++++++++++--
2 files changed, 91 insertions(+), 8 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/settings/DataSourceDTO.java
b/server/src/main/java/org/apache/rocketmq/studio/settings/DataSourceDTO.java
index 61626ad9..bf9c5fb6 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/settings/DataSourceDTO.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/settings/DataSourceDTO.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.studio.settings;
import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
import lombok.Data;
@Data
@@ -28,20 +29,26 @@ public class DataSourceDTO {
private String name;
@NotBlank(message = "type is required")
+ @Pattern(
+ regexp = "(?i)\\s*(?:prometheus|victoria[
_]?metrics|thanos|mimir|cortex|arms)?\\s*",
+ message = "Unsupported metrics data source type")
private String type;
@NotBlank(message = "url is required")
private String url;
+ @Pattern(
+ regexp = "(?i)\\s*(?:none|basic auth|bearer token)?\\s*",
+ message = "Unsupported metrics data source authentication")
private String auth;
public DataSourceVO toDataSourceVO() {
return DataSourceVO.builder()
.key(key)
.name(name)
- .type(type)
+ .type(type.trim())
.url(url)
- .auth(auth)
+ .auth(auth == null ? null : auth.trim())
.build();
}
}
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
index 12f14191..094ee02e 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
@@ -160,9 +160,9 @@ class SettingsControllerTest {
@Test
void listDataSourcesShouldReturnAllSources() throws Exception {
- DataSourceVO ds1 =
DataSourceVO.builder().key("ds-1").name("Production").type("rocketmq")
+ DataSourceVO ds1 =
DataSourceVO.builder().key("ds-1").name("Production").type("Prometheus")
.url("prod:9876").status("connected").build();
- DataSourceVO ds2 =
DataSourceVO.builder().key("ds-2").name("Staging").type("rocketmq")
+ DataSourceVO ds2 =
DataSourceVO.builder().key("ds-2").name("Staging").type("Prometheus")
.url("staging:9876").status("disconnected").build();
when(settingsService.listDataSources()).thenReturn(Arrays.asList(ds1,
ds2));
@@ -189,9 +189,9 @@ class SettingsControllerTest {
@Test
void createDataSourceShouldReturnCreatedSource() throws Exception {
- DataSourceVO input = DataSourceVO.builder().name("New
DS").type("rocketmq")
+ DataSourceVO input = DataSourceVO.builder().name("New
DS").type("Prometheus")
.url("new-host:9876").build();
- DataSourceVO created = DataSourceVO.builder().key("ds-new").name("New
DS").type("rocketmq")
+ DataSourceVO created = DataSourceVO.builder().key("ds-new").name("New
DS").type("Prometheus")
.url("new-host:9876").status("connected").build();
when(settingsService.createDataSource(any(DataSourceVO.class))).thenReturn(created);
@@ -222,6 +222,43 @@ class SettingsControllerTest {
verifyNoInteractions(settingsService);
}
+ @Test
+ void createDataSourceShouldRejectUnsupportedMetricsType() throws Exception
{
+ mockMvc.perform(post("/api/settings/datasources/create")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "name": "New DS",
+ "type": "unsupported",
+ "url": "http://metrics.example.test"
+ }
+ """))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Unsupported metrics data
source type")));
+
+ verifyNoInteractions(settingsService);
+ }
+
+ @Test
+ void createDataSourceShouldRejectUnsupportedAuthentication() throws
Exception {
+ mockMvc.perform(post("/api/settings/datasources/create")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "name": "New DS",
+ "type": "Prometheus",
+ "url": "http://metrics.example.test",
+ "auth": "API Key"
+ }
+ """))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Unsupported metrics data
source authentication")));
+
+ verifyNoInteractions(settingsService);
+ }
+
@Test
void createDataSourceShouldRejectNullRequestBody() throws Exception {
mockMvc.perform(post("/api/settings/datasources/create")
@@ -236,7 +273,7 @@ class SettingsControllerTest {
@Test
void updateDataSourceShouldReturnUpdatedSource() throws Exception {
- DataSourceVO input = DataSourceVO.builder().key("ds-1").name("Updated
DS").type("rocketmq")
+ DataSourceVO input = DataSourceVO.builder().key("ds-1").name("Updated
DS").type("Prometheus")
.url("updated:9876").build();
when(settingsService.updateDataSource(any(DataSourceVO.class))).thenReturn(input);
@@ -256,7 +293,7 @@ class SettingsControllerTest {
.content("""
{
"key": "ds-1",
- "type": "rocketmq",
+ "type": "Prometheus",
"url": "updated:9876"
}
"""))
@@ -267,6 +304,45 @@ class SettingsControllerTest {
verifyNoInteractions(settingsService);
}
+ @Test
+ void updateDataSourceShouldRejectUnsupportedMetricsType() throws Exception
{
+ mockMvc.perform(post("/api/settings/datasources/update")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "key": "ds-1",
+ "name": "Updated DS",
+ "type": "unsupported",
+ "url": "http://metrics.example.test"
+ }
+ """))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Unsupported metrics data
source type")));
+
+ verifyNoInteractions(settingsService);
+ }
+
+ @Test
+ void updateDataSourceShouldRejectUnsupportedAuthentication() throws
Exception {
+ mockMvc.perform(post("/api/settings/datasources/update")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "key": "ds-1",
+ "name": "Updated DS",
+ "type": "Prometheus",
+ "url": "http://metrics.example.test",
+ "auth": "API Key"
+ }
+ """))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Unsupported metrics data
source authentication")));
+
+ verifyNoInteractions(settingsService);
+ }
+
@Test
void updateDataSourceShouldRejectNullRequestBody() throws Exception {
mockMvc.perform(post("/api/settings/datasources/update")