This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git


The following commit(s) were added to refs/heads/main by this push:
     new 213b83f64 fix(core): enforce class-level HTTP method annotations for 
wildcard-resolved unannotated methods (#1690)
213b83f64 is described below

commit 213b83f64f8fc83f9988edad56f2ad10a246ffeb
Author: ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻 <[email protected]>
AuthorDate: Tue May 19 11:35:21 2026 +0530

    fix(core): enforce class-level HTTP method annotations for 
wildcard-resolved unannotated methods (#1690)
    
    The WW-5535 fix (commit 4d2eb93) corrected isMethodSpecified() for 
wildcard-resolved
    methods but introduced a structural gap in 
HttpMethodInterceptor.intercept().
    
    The if/else-if structure made the class-level annotation check unreachable 
whenever
    isMethodSpecified()=true and the resolved method carries no method-level 
annotation:
    
      if (isMethodSpecified()) {
          if (isAnnotatedBy(method)) { ... }
          // falls through silently
      } else if (isAnnotatedBy(class)) { ... }  // never reached
      return invocation.invoke();               // no enforcement
    
    Fix: convert else-if to standalone if so the class-level check is always 
evaluated
    as a fallback when the method itself has no annotation. Method-level 
annotations
    still take precedence (checked first).
    
    Add two regression tests covering the wildcard-resolved unannotated method 
scenario.
    
    Co-authored-by: g0w6y <[email protected]>
---
 .../httpmethod/HttpMethodInterceptor.java          |  3 +-
 .../httpmethod/HttpMethodInterceptorTest.java      | 45 ++++++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptor.java
 
b/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptor.java
index cc2dd4cc8..8e732f326 100644
--- 
a/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptor.java
+++ 
b/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptor.java
@@ -90,7 +90,8 @@ public class HttpMethodInterceptor extends 
AbstractInterceptor {
                     invocation.getProxy().getMethod(), 
AllowedHttpMethod.class.getSimpleName(), request.getMethod());
                 return doIntercept(invocation, method);
             }
-        } else if (AnnotationUtils.isAnnotatedBy(action.getClass(), 
HTTP_METHOD_ANNOTATIONS)) {
+        }
+        if (AnnotationUtils.isAnnotatedBy(action.getClass(), 
HTTP_METHOD_ANNOTATIONS)) {
             LOG.debug("Action: {} annotated with: {}, checking if request: {} 
meets allowed methods!",
                 action, AllowedHttpMethod.class.getSimpleName(), 
request.getMethod());
             return doIntercept(invocation, action.getClass());
diff --git 
a/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptorTest.java
 
b/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptorTest.java
index 5b2548479..ef67d65ab 100644
--- 
a/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodInterceptorTest.java
@@ -273,6 +273,51 @@ public class HttpMethodInterceptorTest extends 
StrutsInternalTestCase {
         invocation.setProxy(actionProxy);
     }
 
+
+    /**
+     * Regression: wildcard-resolved method with NO method-level annotation on 
a class
+     * that has a class-level @AllowedHttpMethod(POST) — GET must be rejected.
+     * The WW-5535 fix introduced an if/else-if that made the class-level check
+     * unreachable when isMethodSpecified()=true and the method is unannotated.
+     */
+    public void 
testWildcardResolvedUnannotatedMethodRespectsClassLevelAnnotation() throws 
Exception {
+        // given — HttpMethodsTestAction has @AllowedHttpMethod(POST) at class 
level
+        // execute() inherited from ActionSupport has no method-level HTTP 
annotation
+        HttpMethodsTestAction action = new HttpMethodsTestAction();
+        prepareActionInvocation(action);
+        actionProxy.setMethod("execute");
+        actionProxy.setMethodSpecified(true); // simulates wildcard-resolved, 
not default
+
+        prepareRequest("get");
+
+        // when
+        String resultName = interceptor.intercept(invocation);
+
+        // then — class-level @AllowedHttpMethod(POST) must still be enforced
+        assertEquals("bad-request", resultName);
+    }
+
+    /**
+     * Counterpart: POST on wildcard-resolved unannotated method must succeed
+     * when the class allows POST via class-level annotation.
+     */
+    public void 
testWildcardResolvedUnannotatedMethodAllowsPostWithClassLevelAnnotation() 
throws Exception {
+        // given
+        HttpMethodsTestAction action = new HttpMethodsTestAction();
+        prepareActionInvocation(action);
+        actionProxy.setMethod("execute");
+        actionProxy.setMethodSpecified(true);
+        invocation.setResultCode("success");
+
+        prepareRequest("post");
+
+        // when
+        String resultName = interceptor.intercept(invocation);
+
+        // then
+        assertEquals("success", resultName);
+    }
+
     private void prepareRequest(String httpMethod) {
         MockHttpServletRequest request = new 
MockHttpServletRequest(httpMethod, "/action");
         ActionContext.getContext().withServletRequest(request);

Reply via email to