Skip to content

Commit cdd4e8c

Browse files
committed
Improve regex support for URL path matching
Closes gh-28815
1 parent 02b7ddb commit cdd4e8c

File tree

6 files changed

+27
-28
lines changed

6 files changed

+27
-28
lines changed

spring-core/src/main/java/org/springframework/util/AntPathMatcher.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -701,8 +701,8 @@ else if (match.startsWith("{") && match.endsWith("}")) {
701701
else {
702702
this.exactMatch = false;
703703
patternBuilder.append(quote(pattern, end, pattern.length()));
704-
this.pattern = (this.caseSensitive ? Pattern.compile(patternBuilder.toString()) :
705-
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
704+
this.pattern = Pattern.compile(patternBuilder.toString(),
705+
Pattern.DOTALL | (this.caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
706706
}
707707
}
708708

spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -131,6 +131,7 @@ void match() {
131131

132132
assertThat(pathMatcher.match("/{bla}.*", "/testing.html")).isTrue();
133133
assertThat(pathMatcher.match("/{bla}", "//x\ny")).isTrue();
134+
assertThat(pathMatcher.match("/{var:.*}", "/x\ny")).isTrue();
134135
}
135136

136137
@Test

spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -59,15 +59,9 @@ class CaptureVariablePathElement extends PathElement {
5959
}
6060
else {
6161
this.variableName = new String(captureDescriptor, 1, colon - 1);
62-
if (caseSensitive) {
63-
this.constraintPattern = Pattern.compile(
64-
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2));
65-
}
66-
else {
67-
this.constraintPattern = Pattern.compile(
68-
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2),
69-
Pattern.CASE_INSENSITIVE);
70-
}
62+
this.constraintPattern = Pattern.compile(
63+
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2),
64+
Pattern.DOTALL | (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
7165
}
7266
}
7367

spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -108,12 +108,8 @@ else if (match.startsWith("{") && match.endsWith("}")) {
108108
}
109109

110110
patternBuilder.append(quote(text, end, text.length()));
111-
if (this.caseSensitive) {
112-
return Pattern.compile(patternBuilder.toString());
113-
}
114-
else {
115-
return Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE);
116-
}
111+
return Pattern.compile(patternBuilder.toString(),
112+
Pattern.DOTALL | (this.caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
117113
}
118114

119115
public List<String> getVariableNames() {

spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ public void encodingAndBoundVariablesCapturePathElement() {
300300
checkCapture("{var:f o}","f%20o","var","f o"); // constraint is expressed in non encoded form
301301
checkCapture("{var:f.o}","f%20o","var","f o");
302302
checkCapture("{var:f\\|o}","f%7co","var","f|o");
303+
checkCapture("{var:.*}","x\ny","var","x\ny");
303304
}
304305

305306
@Test
@@ -319,6 +320,8 @@ public void encodingAndBoundVariablesRegexPathElement() {
319320
checkCapture("/{var1}_ _{var2}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
320321
checkCapture("/{var1}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
321322
checkCapture("/{var1:f o}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
323+
checkCapture("/{var1:f o}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
324+
checkCapture("/{var1}_{var2}","/f\noo_foo","var1","f\noo","var2","foo");
322325
}
323326

324327
@Test

src/docs/asciidoc/web/webmvc.adoc

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ See <<mvc-config-interceptors>> in the section on MVC configuration for examples
648648
configure interceptors. You can also register them directly by using setters on individual
649649
`HandlerMapping` implementations.
650650

651-
Note that `postHandle` is less useful with `@ResponseBody` and `ResponseEntity` methods for
651+
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
652652
which the response is written and committed within the `HandlerAdapter` and before
653653
`postHandle`. That means it is too late to make any changes to the response, such as adding
654654
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
@@ -657,6 +657,7 @@ declare it as an <<mvc-ann-controller-advice>> bean or configure it directly on
657657

658658

659659

660+
660661
[[mvc-exceptionhandlers]]
661662
=== Exceptions
662663
[.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, WebFlux>>#
@@ -5362,7 +5363,6 @@ the following example shows:
53625363
public void addInterceptors(InterceptorRegistry registry) {
53635364
registry.addInterceptor(new LocaleChangeInterceptor());
53645365
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
5365-
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
53665366
}
53675367
}
53685368
----
@@ -5376,7 +5376,6 @@ the following example shows:
53765376
override fun addInterceptors(registry: InterceptorRegistry) {
53775377
registry.addInterceptor(LocaleChangeInterceptor())
53785378
registry.addInterceptor(ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**")
5379-
registry.addInterceptor(SecurityInterceptor()).addPathPatterns("/secure/*")
53805379
}
53815380
}
53825381
----
@@ -5392,13 +5391,19 @@ The following example shows how to achieve the same configuration in XML:
53925391
<mvc:exclude-mapping path="/admin/**"/>
53935392
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
53945393
</mvc:interceptor>
5395-
<mvc:interceptor>
5396-
<mvc:mapping path="/secure/*"/>
5397-
<bean class="org.example.SecurityInterceptor"/>
5398-
</mvc:interceptor>
53995394
</mvc:interceptors>
54005395
----
54015396

5397+
NOTE: Mapped interceptors are not ideally suited as a security layer due to the potential
5398+
for a mismatch with annotated controller path matching, which can also match trailing
5399+
slashes and path extensions transparently, along with other path matching options. Many
5400+
of these options have been deprecated but the potential for a mismatch remains.
5401+
Generally, we recommend using Spring Security which includes a dedicated
5402+
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
5403+
to align with Spring MVC path matching and also has a security firewall that blocks many
5404+
unwanted characters in URL paths.
5405+
5406+
54025407

54035408

54045409
[[mvc-config-content-negotiation]]

0 commit comments

Comments
 (0)