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 beae721a9 fix(ai): validate message properties before confirming a
send preview (#4470)
beae721a9 is described below
commit beae721a931e8475ae8f95d77b5291bfdb3594c0
Author: 0 <[email protected]>
AuthorDate: Mon Sep 21 20:59:42 2026 +0800
fix(ai): validate message properties before confirming a send preview
(#4470)
`MessageSendToolHandler.preview` resolved the topic type and validated the
send against it, but never parsed `input.properties()`. `execute` does parse
it, and `parseProperties` throws `ToolError.MESSAGE_PROPERTIES_INVALID` for
anything that is not a flat JSON object of strings. A caller passing
`properties: "{"` therefore received a clean confirmation plan and only failed
once the send was actually attempted — after the operator had approved the
mutation on the strength of that plan. [...]
`preview` now calls `parseProperties(input.properties())` before
`validateForTopicType`, with a comment recording that the parsed value is
deliberately discarded because `execute` rebuilds it. The parameterized test
asserts that preview and execute both throw `ToolExecutionException` for an
unterminated object, a JSON array and a nested object, and that `sendMessage`
is never reached.
---
.../ai/tool/handler/message/MessageSendToolHandler.java | 2 ++
.../handler/message/MessageSendToolHandlerTest.java | 17 +++++++++++++++++
2 files changed, 19 insertions(+)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandler.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandler.java
index d943ab19c..f93e043fe 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandler.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandler.java
@@ -66,6 +66,8 @@ public class MessageSendToolHandler extends
MutationToolHandler<MessageSendInput
@Override
public ToolPlan preview(MessageSendInput input, ToolExecutionContext
context) {
+ // Validates only: the parsed value is rebuilt by execute(), so the
return value is dropped here.
+ parseProperties(input.properties());
validateForTopicType(resolveTopicType(context.instanceId(),
input.topicName()), input);
Map<String, Object> after = new LinkedHashMap<>();
after.put("topic", input.topicName());
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandlerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandlerTest.java
index ac9e8c63d..e75361506 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandlerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/tool/handler/message/MessageSendToolHandlerTest.java
@@ -25,7 +25,10 @@ import org.apache.rocketmq.studio.instance.topic.TopicVO;
import
org.apache.rocketmq.studio.ops.ai.tool.contract.message.MessageSendInput;
import
org.apache.rocketmq.studio.ops.ai.tool.contract.message.MessageSendOutput;
import org.apache.rocketmq.studio.ops.ai.tool.contract.plan.ToolPlan;
+import org.apache.rocketmq.studio.ops.ai.tool.core.ToolExecutionException;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import java.util.Map;
@@ -45,6 +48,20 @@ class MessageSendToolHandlerTest {
private final MetadataService metadata = mock(MetadataService.class);
private final MessageSendToolHandler handler = new
MessageSendToolHandler(metadata);
+ @ParameterizedTest
+ @ValueSource(strings = {"{", "[]", "{\"nested\":{\"key\":\"value\"}}"})
+ void malformedPropertiesFailDuringPreviewAndExecutionTest(String
properties) {
+ givenTopicType(TopicType.NORMAL);
+ MessageSendInput request = new MessageSendInput("instance-a",
"TopicA", "hello", null, null,
+ null, null, properties);
+
+ assertThatThrownBy(() -> handler.preview(request,
context("instance-a")))
+ .isInstanceOf(ToolExecutionException.class);
+ assertThatThrownBy(() -> handler.execute(request,
context("instance-a")))
+ .isInstanceOf(ToolExecutionException.class);
+ verify(metadata, never()).sendMessage(any(SendMessageDTO.class));
+ }
+
private void givenTopicType(TopicType type) {
TopicVO topic = new TopicVO();
topic.setName("TopicA");