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