Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reject "/path/**/other" patterns in PathPatternParser #24952

Closed
kaladhar-mummadi opened this issue Apr 21, 2020 · 7 comments
Closed

Reject "/path/**/other" patterns in PathPatternParser #24952

kaladhar-mummadi opened this issue Apr 21, 2020 · 7 comments
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@kaladhar-mummadi
Copy link

Issue:

Created 2 mappings as following in the controller, code is in Controller .javaof the attached project.

@GetMapping("/foo/**")
public ResponseEntity getFoo() {}

@GetMapping("/foo/**/bar")
public ResponseEntity getFooBar() {}
  • In webmvc whenever request comes as /foo/1/2/3/bar (as far as last path segment
    is bar) it maps to getFooBar.
  • If bar is not specified ast last path segment then it maps to getFoo. i.e all foo/1, foo/1/2, foo/ab/cd.... maps to getFoo.
  • The issue is in webflux, when I switch to webflux all mappings map to
    getFoo. No matter if path consists of bar in the last path segment.
  • For eg. /foo/ab/cd/bar maps to getFoo instead of getFooBar.

Uploaded the project here , to switch between webflux and webmvc please comment out
appropriate starter dependency in pom.xml.

Repro project here : https://github.com/kaladhar-mummadi/demo-issue

Notes:

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 21, 2020
@bclozel bclozel self-assigned this Apr 21, 2020
@bclozel
Copy link
Member

bclozel commented Apr 21, 2020

Thanks for raising this, it seems we need to improve that case in our implementation and in the reference docs.

First, I believe this is by design (see the first comment in #19112); if I remember correctly, not supporting "**" segments in the middle of patterns was done on purpose, for several reasons:

  • this does not perform well, since it requires backtracking
  • it's been often the source of misunderstanding and complex matches which lead to strange matches or even security issues in particular applications

PathPattern does support ** at the end of the pattern only (see PathPattern Javadoc) - the same way it supports the new "/path/{*captureTheRest}" syntax (but with additional capturing).

I'll turn this issue into an enhancement with two goals:

  1. Ensure that "/path/**/other" patterns are rejected with PatternParseException
  2. Fix the WebFlux reference documentation on URI matching which does not reflect this limitation nor additional capabilities of this implementation

@bclozel bclozel added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 21, 2020
@bclozel bclozel added this to the 5.3 M1 milestone Apr 21, 2020
@bclozel bclozel changed the title Behavior difference between PathPatternParser(WebFlux) vs AntPathMatcher(MVC) Reject "/path/**/other" patterns in PathPatternParser (WebFlux) Apr 21, 2020
@bclozel
Copy link
Member

bclozel commented Apr 21, 2020

Note: this issue can be reproduced with a simple test:

PathPatternParser parser = new PathPatternParser();
PathPattern pattern = parser.parse("/test/**/spring");
PathContainer pathContainer = PathContainer.parsePath("/test/project/other/spring");
boolean matches = pattern.matches(pathContainer);
assertThat(matches).isTrue();

@ranarula
Copy link

@bclozel - I see a recent ticket #24945 to align MVC towards using PathPatternParser. Won't this be a breaking change ?

I wonder why there are different strategies used for WebFlux and MVC. Would have been better if the change remained consistent across both.

@bclozel
Copy link
Member

bclozel commented Apr 21, 2020

I see a recent ticket #24945 to align MVC towards using PathPatternParser. Won't this be a breaking change ?

This won't be a breaking change since we'll support both AntPathMatcher and PathPatternParser infrastructures for Spring MVC, leaving the current one as the default. This just gives an extra choice for developers interested in optimal path matching performance (we're getting quite a few requests about that).

I wonder why there are different strategies used for WebFlux and MVC. Would have been better if the change remained consistent across both.

WebFlux was a completely new effort and we took that opportunity to revisit parts we wanted to improve. Aligning both MVC and WebFlux right away in Spring Framework 5.0 wouldn't have been a wise choice since at that point we didn't have much experience/feedback on running WebFlux applications in production.

As for adding that feature to PathPatternParser, I don't think we should do that for the reasons listed above. Even if WebFlux applications are less popular than MVC apps, I believe it's the first time we're getting an issue about this since 5.0 is out (in 2017).

@ranarula
Copy link

ranarula commented Apr 21, 2020

Thanks @bclozel for the explanation. Make sense.

If both AntPathMatcher and PathPatternParser options are available for MVC I was wondering if there will be an option available to the developer to choose one or the other infrastructure ?

@bclozel
Copy link
Member

bclozel commented Apr 21, 2020

@ranarula Yes, we’ll provide a configuration option in the usual places in Spring Framework and we’ll probably make it a configuration property in Spring Boot.

@rstoyanchev
Copy link
Contributor

Ensure that "/path/**/other" patterns are rejected with PatternParseException

This is a good idea. The error could suggest re-writing as "/path/*/other" and that should be an easy correction.

Fix the WebFlux reference documentation on URI matching..

Also the PathPattern Javadoc could be made more explicit. Currently it says only:

** matches zero or more path segments until the end of the path

bclozel added a commit that referenced this issue Apr 23, 2020
As of gh-24952, `PathPatternParser` will strictly reject patterns with
`"**"` in the middle of them. `"**"` is only allowed at the end of the
pattern for matching multiple path segments until the end of the path.

Currently, if `"**"` is used in the middle of a pattern it will be
considered as a single `"*"` instead. Rejecting such cases should
clarify the situation.

This commit prepares for that upcoming change and:

* logs a warning message if such a case is used by an application
* expands the MVC and WebFlux documentation about URI matching in
general

Closes gh-24958
@bclozel bclozel changed the title Reject "/path/**/other" patterns in PathPatternParser (WebFlux) Reject "/path/**/other" patterns in PathPatternParser Jun 4, 2020
kenny5he pushed a commit to kenny5he/spring-framework that referenced this issue Jun 21, 2020
Prior to this commit, patterns like `"/path/**/other"` would be treated
as `"/path/*/other"` (single wildcard, i.e. matching zero to many chars
within a path segment). This will not match multiple segments, as
expected by `AntPathMatcher` users or by `PathPatternParser` users when
in patterns like `"/resource/**"`.

This commit now rejects patterns like `"/path/**/other"` as invalid.
This behavior was previously warned against since spring-projectsgh-24958.

Closes spring-projectsgh-24952
j-sandy added a commit to j-sandy/igor that referenced this issue Jan 10, 2025
…tead of default PathPatternParser and refactor spring security from 5.x to 6.x during upgrade of spring boot 3.0.x

As of Spring Boot 2.6, the PathPatternParser is used by default. However, for pattern like /abc/**/xyz, PathPattenParser does not resolve the path.
https://spring.io/blog/2022/05/24/preparing-for-spring-boot-3-0#use-spring-mvcs-pathpatternparser
spring-projects/spring-framework#24952

Due to this change, encountered below errors in igor-web module:
```
BuildControllerSpec > get the status of a build FAILED
    java.lang.IllegalStateException: Invalid mapping on handler class [com.netflix.spinnaker.igor.build.BuildController]: public void com.netflix.spinnaker.igor.build.BuildController.update(java.lang.String,java.lang.Integer,com.netflix.spinnaker.igor.build.model.UpdatedBuild,jakarta.servlet.http.HttpServletRequest)
        at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lambda$detectHandlerMethods$1(AbstractHandlerMethodMapping.java:287)
        at org.springframework.core.MethodIntrospector.lambda$selectMethods$0(MethodIntrospector.java:74)
        at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:366)
        at org.springframework.core.MethodIntrospector.selectMethods(MethodIntrospector.java:72)
        at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:280)
        at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(AbstractHandlerMethodMapping.java:265)
        at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:224)
        at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:212)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:225)
        at org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder.registerMvcSingletons(StandaloneMockMvcBuilder.java:419)
        at org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder.initWebAppContext(StandaloneMockMvcBuilder.java:391)
        at org.springframework.test.web.servlet.setup.AbstractMockMvcBuilder.build(AbstractMockMvcBuilder.java:157)
        at com.netflix.spinnaker.igor.build.BuildControllerSpec.setup(BuildControllerSpec.groovy:116)

        Caused by:
        org.springframework.web.util.pattern.PatternParseException: No more pattern data allowed after {*...} or ** pattern element
            at app//org.springframework.web.util.pattern.InternalPathPatternParser.peekDoubleWildcard(InternalPathPatternParser.java:250)
            at app//org.springframework.web.util.pattern.InternalPathPatternParser.parse(InternalPathPatternParser.java:113)
            at app//org.springframework.web.util.pattern.PathPatternParser.parse(PathPatternParser.java:129)
            at app//org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition.parse(PathPatternsRequestCondition.java:84)
            at app//org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition.<init>(PathPatternsRequestCondition.java:74)
            at app//org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder.build(RequestMappingInfo.java:714)
            at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMappingHandlerMapping.java:400)
            at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMappingHandlerMapping.java:345)
            at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(RequestMappingHandlerMapping.java:302)
            at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(RequestMappingHandlerMapping.java:76)
            at app//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lambda$detectHandlerMethods$1(AbstractHandlerMethodMapping.java:283)
            ... 12 more
```

```
GoogleCloudBuildTest > missingAccountTest() FAILED
    java.lang.IllegalStateException: Failed to load ApplicationContext
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:143)
        at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127)
        at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191)
        at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
        at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
Caused by:
org.springframework.web.util.pattern.PatternParseException: No more pattern data allowed after {*...} or ** pattern element
    at app//org.springframework.web.util.pattern.InternalPathPatternParser.peekDoubleWildcard(InternalPathPatternParser.java:250)
    at app//org.springframework.web.util.pattern.InternalPathPatternParser.parse(InternalPathPatternParser.java:113)
    at app//org.springframework.web.util.pattern.PathPatternParser.parse(PathPatternParser.java:129)
    at app//org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition.parse(PathPatternsRequestCondition.java:84)
    at app//org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition.<init>(PathPatternsRequestCondition.java:74)
    at app//org.springframework.web.servlet.mvc.method.RequestMappingInfo$DefaultBuilder.build(RequestMappingInfo.java:714)
    at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMappingHandlerMapping.java:400)
    at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.createRequestMappingInfo(RequestMappingHandlerMapping.java:345)
    at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(RequestMappingHandlerMapping.java:302)
    at app//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getMappingForMethod(RequestMappingHandlerMapping.java:76)
    at app//org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lambda$detectHandlerMethods$1(AbstractHandlerMethodMapping.java:283)
    ... 118 more

```
So, refactoring the tests to replace AntPathMatcher instead of PathPatternParser by adding the property `spring.mvc.pathmatch.matching-strategy = ANT_PATH_MATCHER`

Ref: spinnaker#1211

========================================================
With spring boot upgrade, spring security also upgrades from 5.x to 6.x. As per the migration [steps](https://www.baeldung.com/spring-security-migrate-5-to-6), `WebSecurityConfigurerAdapter` has been removed. So, it is not required to be extended, instead bean can be registered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants