Skip to content

fix(core): enforce class-level HTTP method annotations for wildcard-resolved unannotated methods#1689

Closed
g0w6y wants to merge 1 commit into
apache:mainfrom
g0w6y:fix/http-method-interceptor-class-annotation-wildcard
Closed

fix(core): enforce class-level HTTP method annotations for wildcard-resolved unannotated methods#1689
g0w6y wants to merge 1 commit into
apache:mainfrom
g0w6y:fix/http-method-interceptor-class-annotation-wildcard

Conversation

@g0w6y
Copy link
Copy Markdown

@g0w6y g0w6y commented May 17, 2026

Summary

Fixes a regression introduced by the WW-5535 fix (commit 4d2eb93) where class-level
HTTP method annotations are silently ignored for wildcard-resolved action methods that
carry no method-level annotation.

Problem

HttpMethodInterceptor.intercept() uses an if/else-if structure that creates a dead
zone. After the WW-5535 fix made wildcard-resolved methods report isMethodSpecified()=true,
the class-level annotation branch became structurally unreachable for unannotated methods:

if (invocation.getProxy().isMethodSpecified()) {
    Method method = action.getClass().getMethod(invocation.getProxy().getMethod());
    if (AnnotationUtils.isAnnotatedBy(method, HTTP_METHOD_ANNOTATIONS)) {
        return doIntercept(invocation, method);
    }
    // unannotated method falls through silently
} else if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
    return doIntercept(invocation, action.getClass()); // never reached
}
return invocation.invoke(); // no enforcement

Affected scenario:

@HttpPost // intends to restrict all requests to POST
public class OrderAction extends ActionSupport {
    public String create() { ... } // no method-level annotation
}
<action name="order-*" class="com.example.OrderAction" method="{1}">

GET /order-create — resolves create() via wildcard, isMethodSpecified()=true,
method has no annotation, else-if never evaluated, @HttpPost on the class is ignored,
request proceeds.

Fix

Convert else if to a standalone if so the class-level annotation check is always
evaluated as a fallback when the method carries no annotation. Method-level annotations
still take precedence (checked first). One-line change.

if (invocation.getProxy().isMethodSpecified()) {
    Method method = action.getClass().getMethod(invocation.getProxy().getMethod());
    if (AnnotationUtils.isAnnotatedBy(method, HTTP_METHOD_ANNOTATIONS)) {
        return doIntercept(invocation, method);
    }
}
if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
    return doIntercept(invocation, action.getClass());
}
return invocation.invoke();

Tests Added

Two regression tests in HttpMethodInterceptorTest:

  • testWildcardResolvedUnannotatedMethodRespectsClassLevelAnnotation — GET rejected on
    a class annotated with @AllowedHttpMethod(POST) when the resolved method is unannotated
  • testWildcardResolvedUnannotatedMethodAllowsPostWithClassLevelAnnotation — POST allowed
    on the same configuration

Related

…esolved unannotated methods

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.
@g0w6y g0w6y closed this by deleting the head repository May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant