From b9379e33d52b491e227715776a072d07bcccddab Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 29 Jun 2026 14:41:04 +0100 Subject: [PATCH] Check viewName for special prefixes in UrlFilenameViewController Closes gh-37027 --- .../mvc/UrlFilenameViewController.java | 14 ++++++++++++- .../mvc/UrlFilenameViewControllerTests.java | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/UrlFilenameViewController.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/UrlFilenameViewController.java index 07ecbfeee24..9d4d7d3a7a7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/UrlFilenameViewController.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/UrlFilenameViewController.java @@ -22,8 +22,11 @@ import java.util.concurrent.ConcurrentHashMap; import jakarta.servlet.http.HttpServletRequest; import org.jspecify.annotations.Nullable; +import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.view.UrlBasedViewResolver; import org.springframework.web.util.ServletRequestPathUtils; /** @@ -150,7 +153,16 @@ public class UrlFilenameViewController extends AbstractUrlViewController { * @see #getSuffix() */ protected String postProcessViewName(String viewName) { - return getPrefix() + viewName + getSuffix(); + return checkViewName(getPrefix() + viewName + getSuffix(), viewName); + } + + private static String checkViewName(String viewNameToUse, String originalViewName) { + if (viewNameToUse.startsWith(UrlBasedViewResolver.REDIRECT_URL_PREFIX) || + viewNameToUse.startsWith(UrlBasedViewResolver.FORWARD_URL_PREFIX)) { + throw new ResponseStatusException( + HttpStatus.BAD_REQUEST, "Rejected viewName '" + originalViewName + "'"); + } + return viewNameToUse; } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java index d32142db1bb..9344528d75a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/UrlFilenameViewControllerTests.java @@ -22,8 +22,10 @@ import java.util.stream.Stream; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; import org.springframework.ui.ModelMap; import org.springframework.util.AntPathMatcher; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.ModelAndView; @@ -34,6 +36,7 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse; import org.springframework.web.util.ServletRequestPathUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * @author Juergen Hoeller @@ -75,6 +78,24 @@ class UrlFilenameViewControllerTests { assertThat(mv.getModel()).isEmpty(); } + @PathPatternsParameterizedTest + void withRedirectPrefix(Function requestFactory) { + UrlFilenameViewController controller = new UrlFilenameViewController(); + MockHttpServletRequest request = requestFactory.apply("/redirect:index"); + assertThatExceptionOfType(ResponseStatusException.class) + .isThrownBy(() -> controller.handleRequest(request, new MockHttpServletResponse())) + .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST)); + } + + @PathPatternsParameterizedTest + void withForwardPrefix(Function requestFactory) { + UrlFilenameViewController controller = new UrlFilenameViewController(); + MockHttpServletRequest request = requestFactory.apply("/forward:index"); + assertThatExceptionOfType(ResponseStatusException.class) + .isThrownBy(() -> controller.handleRequest(request, new MockHttpServletResponse())) + .satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST)); + } + @PathPatternsParameterizedTest void withPrefixAndSuffix(Function requestFactory) throws Exception { UrlFilenameViewController controller = new UrlFilenameViewController();