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

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 gh-24958.

Closes gh-24952
This commit is contained in:
Brian Clozel
2020-05-11 19:08:21 +02:00
parent 7bcda3a29b
commit e4cb25f365
5 changed files with 35 additions and 40 deletions

View File

@@ -28,6 +28,7 @@ import org.springframework.web.util.pattern.PatternParseException.PatternMessage
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
/**
@@ -128,12 +129,12 @@ public class PathPatternParserTests {
pathPattern = checkStructure("/{var:\\\\}");
PathElement next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
assertMatches(pathPattern,"/\\");
assertMatches(pathPattern, "/\\");
pathPattern = checkStructure("/{var:\\/}");
next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
assertNoMatch(pathPattern,"/aaa");
assertNoMatch(pathPattern, "/aaa");
pathPattern = checkStructure("/{var:a{1,2}}");
next = pathPattern.getHeadSection().next;
@@ -142,25 +143,25 @@ public class PathPatternParserTests {
pathPattern = checkStructure("/{var:[^\\/]*}");
next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
PathPattern.PathMatchInfo result = matchAndExtract(pathPattern,"/foo");
PathPattern.PathMatchInfo result = matchAndExtract(pathPattern, "/foo");
assertThat(result.getUriVariables().get("var")).isEqualTo("foo");
pathPattern = checkStructure("/{var:\\[*}");
next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
result = matchAndExtract(pathPattern,"/[[[");
result = matchAndExtract(pathPattern, "/[[[");
assertThat(result.getUriVariables().get("var")).isEqualTo("[[[");
pathPattern = checkStructure("/{var:[\\{]*}");
next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
result = matchAndExtract(pathPattern,"/{{{");
result = matchAndExtract(pathPattern, "/{{{");
assertThat(result.getUriVariables().get("var")).isEqualTo("{{{");
pathPattern = checkStructure("/{var:[\\}]*}");
next = pathPattern.getHeadSection().next;
assertThat(next.getClass().getName()).isEqualTo(CaptureVariablePathElement.class.getName());
result = matchAndExtract(pathPattern,"/}}}");
result = matchAndExtract(pathPattern, "/}}}");
assertThat(result.getUriVariables().get("var")).isEqualTo("}}}");
pathPattern = checkStructure("*");
@@ -249,10 +250,10 @@ public class PathPatternParserTests {
PathPattern pp = parse("/{abc:foo(bar)}");
assertThatIllegalArgumentException().isThrownBy(() ->
pp.matchAndExtract(toPSC("/foo")))
.withMessage("No capture groups allowed in the constraint regex: foo(bar)");
.withMessage("No capture groups allowed in the constraint regex: foo(bar)");
assertThatIllegalArgumentException().isThrownBy(() ->
pp.matchAndExtract(toPSC("/foobar")))
.withMessage("No capture groups allowed in the constraint regex: foo(bar)");
.withMessage("No capture groups allowed in the constraint regex: foo(bar)");
}
@Test
@@ -414,12 +415,12 @@ public class PathPatternParserTests {
assertThat(patterns.get(1)).isEqualTo(p2);
}
@Test // Should be updated with gh-24952
public void doubleWildcardWithinPatternNotSupported() {
@Test
public void captureTheRestWithinPatternNotSupported() {
PathPatternParser parser = new PathPatternParser();
PathPattern pattern = parser.parse("/resources/**/details");
assertThat(pattern.matches(PathContainer.parsePath("/resources/test/details"))).isTrue();
assertThat(pattern.matches(PathContainer.parsePath("/resources/projects/spring/details"))).isFalse();
assertThatThrownBy(() -> parser.parse("/resources/**/details"))
.isInstanceOf(PatternParseException.class)
.extracting("messageType").isEqualTo(PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
}
@Test
@@ -461,16 +462,16 @@ public class PathPatternParserTests {
String... expectedInserts) {
assertThatExceptionOfType(PatternParseException.class)
.isThrownBy(() -> pathPattern = parse(pattern))
.satisfies(ex -> {
if (expectedPos >= 0) {
assertThat(ex.getPosition()).as(ex.toDetailedString()).isEqualTo(expectedPos);
}
assertThat(ex.getMessageType()).as(ex.toDetailedString()).isEqualTo(expectedMessage);
if (expectedInserts.length != 0) {
assertThat(ex.getInserts()).isEqualTo(expectedInserts);
}
});
.isThrownBy(() -> pathPattern = parse(pattern))
.satisfies(ex -> {
if (expectedPos >= 0) {
assertThat(ex.getPosition()).as(ex.toDetailedString()).isEqualTo(expectedPos);
}
assertThat(ex.getMessageType()).as(ex.toDetailedString()).isEqualTo(expectedMessage);
if (expectedInserts.length != 0) {
assertThat(ex.getInserts()).isEqualTo(expectedInserts);
}
});
}
@SafeVarargs

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,8 +54,10 @@ public class PathPatternTests {
public void hasPatternSyntax() {
PathPatternParser parser = new PathPatternParser();
assertThat(parser.parse("/foo/*").hasPatternSyntax()).isTrue();
assertThat(parser.parse("/foo/**/bar").hasPatternSyntax()).isTrue();
assertThat(parser.parse("/foo/**").hasPatternSyntax()).isFalse();
assertThat(parser.parse("/foo/{*elem}").hasPatternSyntax()).isFalse();
assertThat(parser.parse("/f?o").hasPatternSyntax()).isTrue();
assertThat(parser.parse("/f*").hasPatternSyntax()).isTrue();
assertThat(parser.parse("/foo/{bar}/baz").hasPatternSyntax()).isTrue();
assertThat(parser.parse("/foo/bar").hasPatternSyntax()).isFalse();
}
@@ -867,13 +869,10 @@ public class PathPatternTests {
assertThat(pathMatcher.combine("", "/hotels")).isEqualTo("/hotels");
assertThat(pathMatcher.combine("/hotels/*", "booking")).isEqualTo("/hotels/booking");
assertThat(pathMatcher.combine("/hotels/*", "/booking")).isEqualTo("/hotels/booking");
assertThat(pathMatcher.combine("/hotels/**", "booking")).isEqualTo("/hotels/**/booking");
assertThat(pathMatcher.combine("/hotels/**", "/booking")).isEqualTo("/hotels/**/booking");
assertThat(pathMatcher.combine("/hotels", "/booking")).isEqualTo("/hotels/booking");
assertThat(pathMatcher.combine("/hotels", "booking")).isEqualTo("/hotels/booking");
assertThat(pathMatcher.combine("/hotels/", "booking")).isEqualTo("/hotels/booking");
assertThat(pathMatcher.combine("/hotels/*", "{hotel}")).isEqualTo("/hotels/{hotel}");
assertThat(pathMatcher.combine("/hotels/**", "{hotel}")).isEqualTo("/hotels/**/{hotel}");
assertThat(pathMatcher.combine("/hotels", "{hotel}")).isEqualTo("/hotels/{hotel}");
assertThat(pathMatcher.combine("/hotels", "{hotel}.*")).isEqualTo("/hotels/{hotel}.*");
assertThat(pathMatcher.combine("/hotels/*/booking", "{booking}")).isEqualTo("/hotels/*/booking/{booking}");