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 b8a71f6f74 fix(waf): default the reject status code to 403 when the
handle omits it (#7320)
b8a71f6f74 is described below
commit b8a71f6f7492897c9eaff64df170b0ad728fc03d
Author: Sean-Walker0 <[email protected]>
AuthorDate: Wed Sep 30 11:07:34 2026 +0800
fix(waf): default the reject status code to 403 when the handle omits it
(#7320)
* fix(waf): default the reject status code to 403 when the handle omits it
A WafHandle deserialized from a rule handle like {"permission":"reject"}
has a null statusCode, and WafPlugin#doExecute passed it straight to
Integer.parseInt, so a matching request failed with NumberFormatException
instead of being rejected: the gateway answered 500 for a rule whose only
problem is a missing optional field. The null-selector/rule branch right
above already rejects with 403, and WafHandle.newDefaultInstance() also
uses 403, so blank status codes now fall back to HttpStatus.FORBIDDEN too.
The new test fails on current master with NumberFormatException and
passes with this change.
Fixes #5274
* fix(waf): fall back to 403 for malformed reject status codes too
Fold in the review suggestion: NumberUtils.toInt covers missing, blank
and non-numeric status codes in one expression, so a handle like
{"permission":"reject","statusCode":"forbidden"} (or a value with
stray whitespace) rejects with 403 instead of throwing
NumberFormatException out of doExecute.
The new malformed-status test failed on the previous commit with
NumberFormatException: For input string "forbidden" and passes here.
---------
Co-authored-by: Sean-Walker0
<[email protected]>
---
.../org/apache/shenyu/plugin/waf/WafPlugin.java | 3 ++-
.../apache/shenyu/plugin/waf/WafPluginTest.java | 26 ++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git
a/shenyu-plugin/shenyu-plugin-waf/src/main/java/org/apache/shenyu/plugin/waf/WafPlugin.java
b/shenyu-plugin/shenyu-plugin-waf/src/main/java/org/apache/shenyu/plugin/waf/WafPlugin.java
index e08563e579..c7be9271eb 100644
---
a/shenyu-plugin/shenyu-plugin-waf/src/main/java/org/apache/shenyu/plugin/waf/WafPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-waf/src/main/java/org/apache/shenyu/plugin/waf/WafPlugin.java
@@ -18,6 +18,7 @@
package org.apache.shenyu.plugin.waf;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
@@ -65,7 +66,7 @@ public class WafPlugin extends AbstractShenyuPlugin {
return chain.execute(exchange);
}
if (WafEnum.REJECT.getName().equals(wafHandle.getPermission())) {
- int statusCode = Integer.parseInt(wafHandle.getStatusCode());
+ int statusCode = NumberUtils.toInt(wafHandle.getStatusCode(),
HttpStatus.FORBIDDEN.value());
exchange.getResponse().setRawStatusCode(statusCode);
Object error = ShenyuResultWrap.error(exchange, statusCode,
Constants.REJECT_MSG, null);
return WebFluxResultUtils.result(exchange, error);
diff --git
a/shenyu-plugin/shenyu-plugin-waf/src/test/java/org/apache/shenyu/plugin/waf/WafPluginTest.java
b/shenyu-plugin/shenyu-plugin-waf/src/test/java/org/apache/shenyu/plugin/waf/WafPluginTest.java
index c83ae71378..bfa012655e 100644
---
a/shenyu-plugin/shenyu-plugin-waf/src/test/java/org/apache/shenyu/plugin/waf/WafPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-waf/src/test/java/org/apache/shenyu/plugin/waf/WafPluginTest.java
@@ -133,6 +133,32 @@ public final class WafPluginTest {
assertEquals(404, exchange.getResponse().getRawStatusCode());
}
+ @Test
+ public void
testWafPluginRejectWithMalformedStatusCodeFallsBackToForbidden() {
+ // stub distinct ids: setId on the mock is a no-op, and the shared
"null_null" cache key
+ // would leak this handle into the other tests that run against the
same key
+ when(ruleData.getSelectorId()).thenReturn("waf");
+ when(ruleData.getId()).thenReturn("waf-bad-status");
+ WafHandle handle =
GsonUtils.getGson().fromJson("{\"permission\":\"reject\",\"statusCode\":\"forbidden\"}",
WafHandle.class);
+
WafPluginDataHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData),
handle);
+ Mono<Void> execute = wafPluginUnderTest.doExecute(exchange, chain,
selectorData, ruleData);
+ StepVerifier.create(execute).expectSubscription().verifyComplete();
+ assertEquals(403, exchange.getResponse().getRawStatusCode());
+ }
+
+ @Test
+ public void testWafPluginRejectWithoutStatusCodeFallsBackToForbidden() {
+ // stub distinct ids: setId on the mock is a no-op, and the shared
"null_null" cache key
+ // would leak this handle into the other tests that run against the
same key
+ when(ruleData.getSelectorId()).thenReturn("waf");
+ when(ruleData.getId()).thenReturn("waf-no-status");
+ WafHandle handle =
GsonUtils.getGson().fromJson("{\"permission\":\"reject\"}", WafHandle.class);
+
WafPluginDataHandler.CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData),
handle);
+ Mono<Void> execute = wafPluginUnderTest.doExecute(exchange, chain,
selectorData, ruleData);
+ StepVerifier.create(execute).expectSubscription().verifyComplete();
+ assertEquals(403, exchange.getResponse().getRawStatusCode());
+ }
+
@Test
public void testWafPluginAllow() {
ruleData.setId("waf");