From ec706297194be34070690ac07302e48a13c33c39 Mon Sep 17 00:00:00 2001 From: mundur Date: Mon, 19 Oct 2026 17:05:20 +0800 Subject: [PATCH] Fix uncaught exception on an unknown HTTP method HttpMethodInterceptor calls HttpMethod.parse() with the request method for any action implementing HttpMethodAware. HttpMethod is an enum whose parse() is valueOf(method.toUpperCase()) with no fallback, and the interceptor has no try/catch, so any method outside its nine constants - PROPFIND, MKCOL, REPORT, or any invented token - raises IllegalArgumentException before the allow-checks run and reaches the client as a 500. Add HttpMethod.parseOrNull(), a total function returning null for an unrecognised method, and use it in the interceptor, leaving the method unset so the request falls through to the existing isAllowed() check. With http-method configured that answers 405 with an Allow header, which is the response the code already produces for a disallowed method. Both parse() and parseOrNull() also use toUpperCase(Locale.ROOT); the bare call is locale-sensitive, and XmlDocConfigurationProvider already uses Locale.ROOT for the same lookup. Tests added to HttpMethodTest. --- .../interceptor/httpmethod/HttpMethod.java | 24 +++++++- .../httpmethod/HttpMethodInterceptor.java | 5 +- .../httpmethod/HttpMethodTest.java | 55 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethod.java b/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethod.java index 8e9b850997..13d3ec283f 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethod.java +++ b/core/src/main/java/org/apache/struts2/interceptor/httpmethod/HttpMethod.java @@ -18,6 +18,8 @@ */ package org.apache.struts2.interceptor.httpmethod; +import java.util.Locale; + /** * Enum represents possible http request types * @@ -36,8 +38,28 @@ public enum HttpMethod { CONNECT, PATCH; + /** + * Returns the {@link HttpMethod} for the given request method, or {@code null} if it is not + * one of the methods this enum knows about. + * + *

Use this for the method of an incoming request. The client chooses that token freely, + * so an unrecognised value is ordinary input rather than a programming error, and must not + * raise.

+ */ + public static HttpMethod parseOrNull(String httpRequestMethod) { + if (httpRequestMethod == null) { + return null; + } + + try { + return valueOf(httpRequestMethod.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + return null; + } + } + public static HttpMethod parse(String httpRequestMethod) { - return valueOf(httpRequestMethod.toUpperCase()); + return valueOf(httpRequestMethod.toUpperCase(Locale.ROOT)); } } 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 c6dbfc0d3b..08b3614018 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 @@ -95,7 +95,10 @@ public String intercept(ActionInvocation invocation) throws Exception { if (action instanceof HttpMethodAware) { LOG.debug("Action: {} implements: {}, setting request method: {}", action, HttpMethodAware.class.getSimpleName(), request.getMethod()); - ((HttpMethodAware) (action)).setMethod(HttpMethod.parse(request.getMethod())); + HttpMethod requestedHttpMethod = HttpMethod.parseOrNull(request.getMethod()); + if (requestedHttpMethod != null) { + ((HttpMethodAware) (action)).setMethod(requestedHttpMethod); + } } if (invocation.getProxy().isMethodSpecified()) { diff --git a/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodTest.java b/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodTest.java index c56ef5f7ae..2e624171fe 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/httpmethod/HttpMethodTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; public class HttpMethodTest { @@ -55,4 +56,58 @@ public void shouldValueOfThrowsException() { assertEquals(expected.getClass(), IllegalArgumentException.class); } + + @Test + public void shouldParseUnknownRequestMethod_asNull_notRaise() { + // given : a request method this enum does not know about, e.g. a WebDAV verb + String httpRequestMethod = "PROPFIND"; + + // when + HttpMethod httpMethod = HttpMethod.parseOrNull(httpRequestMethod); + + // then + assertNull(httpMethod); + } + + @Test + public void shouldParseKnownRequestMethod_regardlessOfCase() { + // given + String httpRequestMethod = "get"; + + // when + HttpMethod httpMethod = HttpMethod.parseOrNull(httpRequestMethod); + + // then + assertEquals(HttpMethod.GET, httpMethod); + } + + @Test + public void shouldParseNullRequestMethod_asNull() { + // given + String httpRequestMethod = null; + + // when + HttpMethod httpMethod = HttpMethod.parseOrNull(httpRequestMethod); + + // then + assertNull(httpMethod); + } + + @Test + public void shouldStillRejectUnknownMethodInConfiguration() { + // given : configuration is trusted input, an unknown name there is a real error + String configuredMethod = "PROPFIND"; + + // when + Throwable expected = null; + try { + HttpMethod.parse(configuredMethod); + } catch (IllegalArgumentException e) { + expected = e; + } + + // then + assertNotNull(expected); + } + }