This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new ef6f32b429 fix(plugin): prevent corruption of URL-encoded RPC query
and form parameters (#7757)
ef6f32b429 is described below
commit ef6f32b42999a7cccaed85844e9372573a16e145
Author: lymerin <[email protected]>
AuthorDate: Mon Oct 19 09:33:23 2026 +0800
fix(plugin): prevent corruption of URL-encoded RPC query and form
parameters (#7757)
---
.../shenyu/plugin/api/utils/BodyParamUtils.java | 6 +++--
.../plugin/transform/RpcParamTransformPlugin.java | 5 ++--
.../transform/RpcParamTransformPluginTest.java | 30 +++++++++++++++++++++-
3 files changed, 35 insertions(+), 6 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/utils/BodyParamUtils.java
b/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/utils/BodyParamUtils.java
index 7951a527d8..83b278da57 100644
---
a/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/utils/BodyParamUtils.java
+++
b/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/utils/BodyParamUtils.java
@@ -17,6 +17,8 @@
package org.apache.shenyu.plugin.api.utils;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -63,10 +65,10 @@ public final class BodyParamUtils {
LinkedMultiValueMap<String, String> params = new
LinkedMultiValueMap<>();
Matcher matcher = QUERY_PARAM_PATTERN.matcher(param);
while (matcher.find()) {
- String name = matcher.group(1);
+ String name = URLDecoder.decode(matcher.group(1),
StandardCharsets.UTF_8);
String eq = matcher.group(2);
String value = matcher.group(3);
- params.add(name, Objects.nonNull(value) ? value :
(StringUtils.isNotBlank(eq) ? "" : null));
+ params.add(name, Objects.nonNull(value) ? URLDecoder.decode(value,
StandardCharsets.UTF_8) : (StringUtils.isNotBlank(eq) ? "" : null));
}
return params;
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/main/java/org/apache/shenyu/plugin/transform/RpcParamTransformPlugin.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/main/java/org/apache/shenyu/plugin/transform/RpcParamTransformPlugin.java
index 81ad86b99b..6611e7d0ed 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/main/java/org/apache/shenyu/plugin/transform/RpcParamTransformPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/main/java/org/apache/shenyu/plugin/transform/RpcParamTransformPlugin.java
@@ -34,7 +34,6 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
-import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Optional;
@@ -89,7 +88,7 @@ public class RpcParamTransformPlugin implements ShenyuPlugin {
if (map.isPresent()) {
String param = resolveBodyFromRequest(map.get());
LinkedMultiValueMap<String, String>
linkedMultiValueMap;
- linkedMultiValueMap =
BodyParamUtils.buildBodyParams(URLDecoder.decode(param,
StandardCharsets.UTF_8));
+ linkedMultiValueMap =
BodyParamUtils.buildBodyParams(param);
exchange.getAttributes().put(Constants.PARAM_TRANSFORM,
HttpParamConverter.toMap(() -> linkedMultiValueMap));
}
return chain.execute(exchange);
@@ -97,7 +96,7 @@ public class RpcParamTransformPlugin implements ShenyuPlugin {
}
private Mono<Void> query(final ServerWebExchange exchange, final
ServerHttpRequest serverHttpRequest, final ShenyuPluginChain chain) {
- exchange.getAttributes().put(Constants.PARAM_TRANSFORM,
HttpParamConverter.ofString(() -> serverHttpRequest.getURI().getQuery()));
+ exchange.getAttributes().put(Constants.PARAM_TRANSFORM,
HttpParamConverter.ofString(() -> serverHttpRequest.getURI().getRawQuery()));
return chain.execute(exchange);
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/test/java/org/apache/shenyu/plugin/transform/RpcParamTransformPluginTest.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/test/java/org/apache/shenyu/plugin/transform/RpcParamTransformPluginTest.java
index 4402461a36..cc9440ca2a 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/test/java/org/apache/shenyu/plugin/transform/RpcParamTransformPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-transform/src/test/java/org/apache/shenyu/plugin/transform/RpcParamTransformPluginTest.java
@@ -17,6 +17,7 @@
package org.apache.shenyu.plugin.transform;
+import com.google.gson.JsonParser;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.enums.RpcTypeEnum;
@@ -28,6 +29,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
@@ -35,6 +37,8 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+import java.net.URI;
+
import static org.mockito.Mockito.mock;
/**
@@ -42,6 +46,9 @@ import static org.mockito.Mockito.mock;
*/
@ExtendWith(MockitoExtension.class)
public final class RpcParamTransformPluginTest {
+
+ private static final String ENCODED_PARAMS =
"value=a%26b%3Dc&plus=a%2Bb&space=a+b&encoded=%2526"
+ + "&empty=&repeat=first&repeat=second";
private ShenyuPluginChain chain;
@@ -95,13 +102,34 @@ public final class RpcParamTransformPluginTest {
@Test
public void testFormatBody() {
final ServerWebExchange exchange = MockServerWebExchange.from(
-
MockServerHttpRequest.post("localhost").contentType(MediaType.APPLICATION_FORM_URLENCODED).body("test=test"));
+
MockServerHttpRequest.post("localhost").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(ENCODED_PARAMS));
+ Mockito.when(chain.execute(exchange)).thenReturn(Mono.empty());
+ ShenyuContext context = new ShenyuContext();
+ context.setRpcType(RpcTypeEnum.DUBBO.getName());
+ exchange.getAttributes().put(Constants.CONTEXT, context);
+ Mono<Void> result = rpcParamTransformPlugin.execute(exchange, chain);
+ StepVerifier.create(result).expectSubscription().verifyComplete();
+
Assertions.assertEquals(JsonParser.parseString("{\"value\":\"a&b=c\",\"plus\":\"a+b\",\"space\":\"a
b\","
+ +
"\"encoded\":\"%26\",\"empty\":\"\",\"repeat\":\"first\"}"),
+
JsonParser.parseString(exchange.getAttribute(Constants.PARAM_TRANSFORM)));
+ }
+
+ /**
+ * Test query parameters.
+ */
+ @Test
+ public void testQueryParams() {
+ ServerWebExchange exchange = MockServerWebExchange.from(
+ MockServerHttpRequest.method(HttpMethod.GET,
URI.create("http://localhost/?" + ENCODED_PARAMS)).build());
Mockito.when(chain.execute(exchange)).thenReturn(Mono.empty());
ShenyuContext context = new ShenyuContext();
context.setRpcType(RpcTypeEnum.DUBBO.getName());
exchange.getAttributes().put(Constants.CONTEXT, context);
Mono<Void> result = rpcParamTransformPlugin.execute(exchange, chain);
StepVerifier.create(result).expectSubscription().verifyComplete();
+
Assertions.assertEquals(JsonParser.parseString("{\"value\":\"a&b=c\",\"plus\":\"a+b\",\"space\":\"a
b\","
+ +
"\"encoded\":\"%26\",\"empty\":\"\",\"repeat\":\"second\"}"),
+
JsonParser.parseString(exchange.getAttribute(Constants.PARAM_TRANSFORM)));
}
/**