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");

Reply via email to