From f4a3193637d16251d7d6c03ab9bf7bbdf04c055a Mon Sep 17 00:00:00 2001 From: juicew <491852417@qq.com> Date: Wed, 19 Aug 2026 21:06:08 +0800 Subject: [PATCH] fix: fix path-variable off-by-one and guard placeholder substitution in rewrite plugin (#6882) 1. Remove the "+1" when extracting the path-variable value from the URI, so the first character of the value is no longer truncated (e.g. /http/findById/123 was rewritten to .../23, now .../123). 2. Only enter the placeholder-substitution branch when the regex also contains '{', so a replace template with '{' combined with a plain regex no longer throws StringIndexOutOfBoundsException (regex.substring(-1)) that returned 500. --- .../shenyu/plugin/rewrite/RewritePlugin.java | 4 +-- .../plugin/rewrite/RewritePluginTest.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/shenyu-plugin/shenyu-plugin-rewrite/src/main/java/org/apache/shenyu/plugin/rewrite/RewritePlugin.java b/shenyu-plugin/shenyu-plugin-rewrite/src/main/java/org/apache/shenyu/plugin/rewrite/RewritePlugin.java index 3f0616fbcc59..398443bd0eb7 100644 --- a/shenyu-plugin/shenyu-plugin-rewrite/src/main/java/org/apache/shenyu/plugin/rewrite/RewritePlugin.java +++ b/shenyu-plugin/shenyu-plugin-rewrite/src/main/java/org/apache/shenyu/plugin/rewrite/RewritePlugin.java @@ -74,9 +74,9 @@ protected Mono doExecute(final ServerWebExchange exchange, final ShenyuPlu final Integer percentage = Optional.ofNullable(rewriteHandle.getPercentage()).orElse(100); if (StringUtils.isNoneBlank(rewriteHandle.getRegex(), rewriteHandle.getReplace()) && ThreadLocalRandom.current().nextInt(100) < percentage) { - rewriteUri = rewriteHandle.getReplace().contains("{") + rewriteUri = rewriteHandle.getReplace().contains("{") && rewriteHandle.getRegex().contains("{") ? PathMatchUtils.replaceAll(rewriteHandle.getReplace(), rewriteHandle.getRegex().substring(rewriteHandle.getRegex().indexOf("{")), - rewriteUri.substring(rewriteHandle.getRegex().indexOf("{") + 1)) + rewriteUri.substring(rewriteHandle.getRegex().indexOf("{"))) : rewriteUri.replaceAll(rewriteHandle.getRegex(), rewriteHandle.getReplace()); Map attributes = exchange.getAttributes(); if (Optional.ofNullable(rewriteHandle.getRewriteMetaData()).orElse(false)) { diff --git a/shenyu-plugin/shenyu-plugin-rewrite/src/test/java/org/apache/shenyu/plugin/rewrite/RewritePluginTest.java b/shenyu-plugin/shenyu-plugin-rewrite/src/test/java/org/apache/shenyu/plugin/rewrite/RewritePluginTest.java index 3d9f75a3dce0..5b5ec80bfc8b 100644 --- a/shenyu-plugin/shenyu-plugin-rewrite/src/test/java/org/apache/shenyu/plugin/rewrite/RewritePluginTest.java +++ b/shenyu-plugin/shenyu-plugin-rewrite/src/test/java/org/apache/shenyu/plugin/rewrite/RewritePluginTest.java @@ -88,6 +88,32 @@ public void shouldReturnNewURIForRewritePlugin() { assertEquals("/shenyu/rewrite", exchange.getAttributes().get(Constants.REWRITE_URI)); } + @Test + public void shouldRewritePathVariableWithCompleteValue() { + RuleData data = new RuleData(); + data.setHandle("{\"regex\":\"/shenyu/{id}\",\"replace\":\"/shenyu/{id}\"}"); + RewriteHandle rewriteHandle = GsonUtils.getGson().fromJson(data.getHandle(), RewriteHandle.class); + RewritePluginDataHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(data), rewriteHandle); + ServerWebExchange rewriteExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/shenyu/123").build()); + rewriteExchange.getAttributes().put(Constants.CONTEXT, new ShenyuContext()); + when(chain.execute(rewriteExchange)).thenReturn(Mono.empty()); + SelectorData selectorData = mock(SelectorData.class); + StepVerifier.create(rewritePlugin.doExecute(rewriteExchange, chain, selectorData, data)).expectSubscription().verifyComplete(); + assertEquals("/shenyu/123", rewriteExchange.getAttributes().get(Constants.REWRITE_URI)); + } + + @Test + public void shouldNotThrowWhenOnlyReplaceContainsPlaceholder() { + RuleData data = new RuleData(); + data.setHandle("{\"regex\":\"/shenyu/.*\",\"replace\":\"/new/{id}\"}"); + RewriteHandle rewriteHandle = GsonUtils.getGson().fromJson(data.getHandle(), RewriteHandle.class); + RewritePluginDataHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(data), rewriteHandle); + when(chain.execute(exchange)).thenReturn(Mono.empty()); + SelectorData selectorData = mock(SelectorData.class); + StepVerifier.create(rewritePlugin.doExecute(exchange, chain, selectorData, data)).expectSubscription().verifyComplete(); + assertEquals("/new/{id}", exchange.getAttributes().get(Constants.REWRITE_URI)); + } + @Test public void testSkip() { final boolean result = rewritePlugin.skip(exchange);