Skip to content

Commit 8d51f47

Browse files
committed
Enfoce single version removal in content versioning
Prior to this commit, content based version strategies would remove all instances of the version string in the request path when trying to resolve the original resource with the chain. This can cause issues in rare cases where there is a collision between the content version and some other version string in the request path. Because this strategy is based on the contents of the file itself, we should only remove the last instance of the version string and then attempt to resolve the original file. Fixes gh-36698
1 parent fb0c78a commit 8d51f47

6 files changed

Lines changed: 34 additions & 2 deletions

File tree

spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractFileNameVersionStrategy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ public String extractVersion(String requestPath) {
5555

5656
@Override
5757
public String removeVersion(String requestPath, String version) {
58-
return StringUtils.delete(requestPath, "-" + version);
58+
String versionString = "-" + version;
59+
int index = requestPath.lastIndexOf(versionString);
60+
if (index != -1) {
61+
return requestPath.substring(0, index) + requestPath.substring(index + versionString.length());
62+
}
63+
return requestPath;
5964
}
6065

6166
@Override

spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ private Mono<Resource> resolveVersionedResource(@Nullable ServerWebExchange exch
179179
}
180180

181181
String simplePath = versionStrategy.removeVersion(requestPath, candidate);
182+
if (ResourceHandlerUtils.shouldIgnoreInputPath(simplePath)) {
183+
return Mono.empty();
184+
}
182185
return chain.resolveResource(exchange, simplePath, locations)
183186
.filterWhen(resource -> versionStrategy.getResourceVersion(resource)
184187
.map(actual -> {

spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ void removeVersion() {
6262
assertThat(this.strategy.removeVersion(String.format(path, "-", hash), hash)).isEqualTo(String.format(path, "", ""));
6363
}
6464

65+
@Test
66+
void removeVersionOnlyOnce() {
67+
String hash = "sha";
68+
String path = "font-awesome/css%s%s/font-awesome.min%s%s.css";
69+
70+
assertThat(this.strategy.removeVersion(String.format(path, "-", hash, "-", hash), hash)).isEqualTo(String.format(path, "-", hash, "", ""));
71+
}
72+
6573
@Test
6674
void getResourceVersion() throws Exception {
6775
Resource expected = new ClassPathResource("test/bar.css", getClass());

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractVersionStrategy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@ public String extractVersion(String requestPath) {
137137

138138
@Override
139139
public String removeVersion(String requestPath, String version) {
140-
return StringUtils.delete(requestPath, "-" + version);
140+
String versionString = "-" + version;
141+
int index = requestPath.lastIndexOf(versionString);
142+
if (index != -1) {
143+
return requestPath.substring(0, index) + requestPath.substring(index + versionString.length());
144+
}
145+
return requestPath;
141146
}
142147

143148
@Override

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ protected Resource resolveResourceInternal(@Nullable HttpServletRequest request,
177177
}
178178

179179
String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion);
180+
if (ResourceHandlerUtils.shouldIgnoreInputPath(simplePath)) {
181+
return null;
182+
}
180183
Resource baseResource = chain.resolveResource(request, simplePath, locations);
181184
if (baseResource == null) {
182185
return null;

spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ContentBasedVersionStrategyTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ void removeVersion() {
6363
assertThat(this.versionStrategy.removeVersion(String.format(file, "-", hash), hash)).isEqualTo(String.format(file, "", ""));
6464
}
6565

66+
@Test
67+
void removeVersionOnlyOnce() {
68+
String hash = "sha";
69+
String file = "font-awesome/css%s%s/font-awesome.min%s%s.css";
70+
71+
assertThat(this.versionStrategy.removeVersion(String.format(file, "-", hash, "-", hash), hash)).isEqualTo(String.format(file, "-", hash, "", ""));
72+
}
73+
6674
@Test
6775
void getResourceVersion() throws IOException {
6876
Resource expected = new ClassPathResource("test/bar.css", getClass());

0 commit comments

Comments
 (0)